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.