Come creare linee guida dinamiche?


10

Sto cercando di creare linee guida dinamiche utilizzando una vista PostGIS oltre allo strumento "Sposta etichetta" di QGIS.

CREATE VIEW leader_line AS
SELECT
gid,
ST_MakeLine(geom, ST_SetSRID(ST_MakePoint(xcord_label, ycord_label), SRID))::geometry(linestring, SRID) AS geom
FROM point
WHERE xcord_label IS NOT NULL;

Funziona bene per tutte le etichette WHERE ST_X(geom) < xcord_labelma crea linee guida dall'aspetto sbagliato per le etichette WHERE ST_X(geom) > xcord_label.

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Qualcuno sa come ottenere le linee guida posizionate correttamente per le etichette WHERE ST_X(geom) > xcord_label? C'è un modo per fare riferimento alla coordinata xmax delle etichette?

inserisci qui la descrizione dell'immagine


1
sono le tue etichette in punti o unità della mappa? Se si tratta di unità della mappa dovrebbe essere abbastanza facile indovinare l'altezza, e quindi accorciare la linea guida per compensare)
Steven Kay

La dimensione dell'etichetta è espressa in unità della mappa.
Mare lunare,

Risposte:


9

È possibile utilizzare l'identificatore di posizionamento del quadrante di QGIS determinato dall'azimut della linea per posizionare un'etichetta migliore. Il quadrante specifica 8 posizioni attorno a un punto:

[ 0=Above Left | 1=Above | 2=Above Right |
  3=Left       | 4=Over  | 5=Right       |
  6=Below Left | 7=Below | 8=Below Right ]

Ecco un esempio intorno a Null Island , creando una tabella e due viste.

CREATE TABLE points (
  gid serial PRIMARY KEY,
  geom geometry(Point, 4326),
  label_geom geometry(Point, 4326),
  label text
);

INSERT INTO points(geom, label_geom, label)
SELECT origin, pt, round(degrees(ST_Azimuth(origin, pt))) || ' degrees'
FROM (
  SELECT
    ST_SetSRID(ST_MakePoint(0, 0), 4326) AS origin,
    ST_SetSRID(ST_MakePoint(cos(radians(x)), sin(radians(x))), 4326) AS pt
  FROM generate_series(0, 350, 15) AS x
) AS f;

CREATE OR REPLACE VIEW point_labels AS
  SELECT gid, label_geom AS geom,
  CASE
    WHEN ST_Azimuth(geom, label_geom) ISNULL THEN 2 -- default if azimuth cannot be determined
    WHEN degrees(ST_Azimuth(geom, label_geom)) < 22.5 THEN 1 -- Above
    WHEN degrees(ST_Azimuth(geom, label_geom)) < 67.5 THEN 2 -- Above Right
    WHEN degrees(ST_Azimuth(geom, label_geom)) < 112.5 THEN 5 -- Right
    WHEN degrees(ST_Azimuth(geom, label_geom)) < 157.5 THEN 8 -- Below Right
    WHEN degrees(ST_Azimuth(geom, label_geom)) < 202.5 THEN 7 -- Below
    WHEN degrees(ST_Azimuth(geom, label_geom)) < 247.5 THEN 6 -- Below Left
    WHEN degrees(ST_Azimuth(geom, label_geom)) < 292.5 THEN 3 -- Left
    WHEN degrees(ST_Azimuth(geom, label_geom)) < 337.5 THEN 0 -- Above Left
    ELSE 1 -- >= 337.5 Above
  END AS quadrant, label
  FROM points;

CREATE OR REPLACE VIEW leader_line AS
  SELECT gid, ST_MakeLine(geom, label_geom)::geometry(LineString, 4326) AS geom, label
  FROM points;

Quindi in QGIS, aggiungi:

  • points - geom
  • leader_line- geom- La chiave primaria deve esseregid
  • point_labels- geom- La chiave primaria deve esseregid

QGIS

Ora configura le proprietà del layer per point_labels:

  • Cambia stile in modo che il punto non venga disegnato, ad es. Cambia la dimensione in 0,0
  • Etichetta questo livello con labele cambia il posizionamento in "Offset dal punto", modificando il "Quadrante" per utilizzare il campo dell'attributoquadrant

quadrante

Bingo!

tombola

Si noti che per i geographytipi è richiesto un approccio leggermente diverso , poiché ST_Azimuth si comporta diversamente.


Aggiornamento: quando si aggiungono nuovi punti al pointslivello, il geomcampo viene aggiornato come al solito, ma non lo label_geomè. Per popolare un valore predefinito di label_geomcon nuovi punti, è necessario creare un trigger . Ma se viene utilizzata una funzione di trigger, l' quadrantidentificatore può essere memorizzato nella pointstabella e la point_labelsvista può essere ignorata:

Ad esempio, ricominciamo con un esempio leggermente diverso con una tabella e una vista:

-- DROP TABLE points CASCADE;
CREATE TABLE points (
  gid serial PRIMARY KEY,
  geom geometry(Point, 4326),
  label_geom geometry(Point, 4326),
  quadrant integer,
  label text
);

CREATE FUNCTION label_geom_tg_fn() RETURNS trigger AS
$BODY$
DECLARE
  azimuth float8;
BEGIN
  -- Set a default label_geom
  IF NEW.label_geom ISNULL THEN
    NEW.label_geom := NEW.geom;
  END IF;
  -- Determine quadrant
  azimuth := degrees(ST_Azimuth(NEW.geom, NEW.label_geom));
  NEW.quadrant := CASE
    WHEN azimuth ISNULL THEN 2 -- azimuth cannot be determined, so put Above Right
    WHEN azimuth < 22.5 THEN 1 -- Above
    WHEN azimuth < 67.5 THEN 2 -- Above Right
    WHEN azimuth < 112.5 THEN 5 -- Right
    WHEN azimuth < 157.5 THEN 8 -- Below Right
    WHEN azimuth < 202.5 THEN 7 -- Below
    WHEN azimuth < 247.5 THEN 6 -- Below Left
    WHEN azimuth < 292.5 THEN 3 -- Left
    WHEN azimuth < 337.5 THEN 0 -- Above Left
    ELSE 1 END;-- >= 337.5 Above
  RETURN NEW;
END;$BODY$ LANGUAGE plpgsql;

CREATE TRIGGER label_geom_tg BEFORE INSERT OR UPDATE
   ON points FOR EACH ROW
   EXECUTE PROCEDURE label_geom_tg_fn();

Dal primo esempio, rifai le istruzioni INSERT INTO pointse CREATE OR REPLACE VIEW leader_line, poiché non richiedono modifiche. Ma ignora la leader_linevista.

Quindi in QGIS, aggiungi:

  • points - geom
  • points - label_geom
  • leader_line- geom- La chiave primaria deve esseregid

Ora configura le proprietà del layer per pointscon label_geomcome ha fatto il primo esempio point_labels. L' quadrantidentificatore verrà modificato automaticamente per i punti nuovi e spostati, ma noterai queste modifiche solo ogni volta che salvi le modifiche.


Ottimo lavoro, ma come aggiungere una nuova funzione punto in QGIS con due colonne geometriche in una tabella PostGIS?
Lunar Sea,

@Lunar Sea - interessante, ottieni due voci per la tabella, una per geometria, ma qgis non ti consente di impostare il campo della geometria dalla combo? Hai provato a utilizzare una query sql manuale nella finestra di dialogo di importazione (è la colonna più a destra e spesso nascosta alla vista ...)?
Steven Kay,

Ho due livelli 'punti' in QGIS ( gid | label_geom | labele gid, geom, label).
Mare lunare,

@LunarSea Ho rielaborato un secondo esempio che ha una tabella e una vista. La tabella ha funzioni di trigger per determinare un valore predefinito per label_geome aggiorna anche il quadrantvalore, quindi il point_labellayer / view non è più necessario.
Mike T

Bella soluzione alternativa Mike! Dopo aver spostato un label_geomho per salvare il livello, modificare e aggiornare l'area di disegno per vedere la posizione effettiva dell'etichetta. È un peccato che non sia possibile utilizzare un identificatore di quadrante con lo strumento "Sposta etichetta" di QGIS.
Lunar Sea,

1

va bene .. dato che è nelle unità della mappa questo dovrebbe essere abbastanza semplice, entro i limiti. Conosci già l'altezza dell'etichetta. Se fosse in punti sarebbe dipendente dalla scala.

Ciò presuppone una dimensione dell'etichetta fissa, quindi la sua efficacia dipende dall'uniformità delle etichette e dal fatto che si usi o meno un carattere proporzionale o a larghezza fissa (la larghezza fissa è più semplice: moltiplicare la lunghezza dell'etichetta per la dimensione dell'etichetta per ottenere la larghezza dell'etichetta).

Purtroppo questo non risponde alla tua domanda su come trovare effettivamente i limiti dell'etichetta come renderizzati .

hai 4 casi (NE, NW, SE, SW).

suppongo che il tuo tavolo assomigli a questo (scuse, alcuni nomi di campi sono diversi)

CREATE TABLE points
(
  uniq int PRIMARY KEY,
  geom geometry(Point,27700),
  label_x int,
  label_y int,
  labeltext character varying(100)
);
ALTER TABLE points
  OWNER TO user;
GRANT ALL ON TABLE points TO user;
GRANT SELECT ON TABLE points TO public;

Quindi, aggiungi 4 punti (tutti identici) ma con etichette nei 4 quadranti per rappresentare i 4 casi d'uso principali

insert into points values 
(1,ST_SetSRID(ST_Point(1000,1000),27700),750,750,'123');

insert into points values(2,ST_SetSRID(ST_Point(1000,1000),27700),1250,1250,'456')

insert into points values 
(3,ST_SetSRID(ST_Point(1000,1000),27700),750,1250,'456')

insert into points values 
(4,ST_SetSRID(ST_Point(1000,1000),27700),1250,750,'789')

Ho usato CRS 27700 (0,0 in basso a sinistra, unità della mappa in m) Ho assunto un'etichetta larghezza 50, altezza 30 unità mappa.

-- SW use case
CREATE OR REPLACE VIEW leader_line_sw AS
SELECT
uniq,
ST_MakeLine(geom, ST_SetSRID(ST_MakePoint(label_x+50, label_y+30), 27700))::geometry(linestring, 27700) AS geom
FROM points
WHERE label_x IS NOT NULL AND 
label_y<=ST_Y(geom) and label_x<=ST_X(geom);

-- SE use case
CREATE OR REPLACE VIEW leader_line_se AS
SELECT
uniq,
ST_MakeLine(geom, ST_SetSRID(ST_MakePoint(label_x, label_y-30), 27700))::geometry(linestring, 27700) AS geom
FROM points
WHERE label_x IS NOT NULL AND 
label_y<=ST_Y(geom) and label_x>ST_X(geom);


-- NE use case
CREATE OR REPLACE VIEW leader_line_ne AS
SELECT
uniq,
ST_MakeLine(geom, ST_SetSRID(ST_MakePoint(label_x, label_y), 27700))::geometry(linestring, 27700) AS geom
FROM points
WHERE label_x IS NOT NULL AND 
label_y>ST_Y(geom) and label_x>ST_X(geom);

-- NW use case
CREATE OR REPLACE VIEW leader_line_nw2 AS
SELECT
uniq,
ST_MakeLine(geom, ST_SetSRID(ST_MakePoint(label_x+50, label_y), 27700))::geometry(linestring, 27700) AS geom
FROM points
WHERE label_x IS NOT NULL AND 
label_y>ST_Y(geom) and label_x<=ST_X(geom);

Trasformazioni Affine

Un'altra possibilità è abbreviare tutte le linee principali, per dire l'80%.

  • È possibile utilizzare ST_Translate (geom, -ST_X (geom), - ST_Y (geom)) per spostare la riga sull'origine per ottenere geom_o
  • usa ST_Scale (geom_o, 0.8,0.8) per ottenere geom_o_scaled
  • quindi ritradurre usando ST_Translate (geom_o_scaled, ST_X (geom), ST_Y (geom)) di nuovo nella posizione originale.

Potrebbe funzionare meglio, anche se non l'ho provato.


Grazie per i tuoi effetti, purtroppo le linee guida non corrispondono molto bene alle etichette.
Mare lunare,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.