Wie erstelle ich dynamische Führungslinien?

10

Ich versuche, dynamische Führungslinien mithilfe einer PostGIS-Ansicht zusätzlich zum QGIS-Tool „Label verschieben“ zu erstellen.

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;

Dies funktioniert für alle Etiketten einwandfrei WHERE ST_X(geom) < xcord_label, erzeugt jedoch falsch aussehende Führungslinien für Etiketten WHERE ST_X(geom) > xcord_label.

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Weiß jemand, wie man richtig platzierte Führungslinien für Etiketten erhält WHERE ST_X(geom) > xcord_label? Gibt es eine Möglichkeit, auf die xmax-Koordinate der Beschriftungen zu verweisen?

Geben Sie hier die Bildbeschreibung ein

Mondsee
quelle
1
Sind Ihre Beschriftungen in Punkten oder Karteneinheiten? Wenn es sich um Karteneinheiten handelt, sollte es ziemlich einfach sein, die Höhe zu erraten und dadurch Ihre Führungslinie zu verkürzen, um dies zu kompensieren.)
Steven Kay
Die Etikettengröße ist in Karteneinheiten angegeben.
Lunar Sea

Antworten:

9

Sie können den Quadrantenplatzierungsspezifizierer von QGIS verwenden, der aus dem Azimut der Linie ermittelt wird, um eine bessere Beschriftung zu platzieren. Der Quadrant gibt 8 Positionen um einen Punkt an:

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

Hier ist ein Beispiel für Null Island , in dem eine Tabelle und zwei Ansichten erstellt werden.

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;

Fügen Sie dann in QGIS Folgendes hinzu:

  • points - - geom
  • leader_line- geom- Primärschlüssel muss seingid
  • point_labels- geom- Primärschlüssel muss seingid

QGIS

Konfigurieren Sie nun die Ebeneneigenschaften für point_labels:

  • Ändern Sie den Stil so, dass der Punkt nicht gezeichnet wird, z. B. ändern Sie die Größe auf 0,0
  • Beschriften Sie diese Ebene mit labelund ändern Sie die Platzierung in "Versatz vom Punkt". Ändern Sie dabei den "Quadranten", um das Attributfeld zu verwendenquadrant

Quadrant

Bingo!

Bingo

Beachten Sie, dass für geographyTypen ein etwas anderer Ansatz erforderlich ist , da sich ST_Azimuth anders verhält.


Aktualisieren: Wenn Sie der pointsEbene neue Punkte hinzufügen , wird das geomFeld wie gewohnt aktualisiert, das label_geomjedoch nicht. Um einen Standardwert von label_geommit neuen Punkten zu füllen, muss ein Trigger erstellt werden . Wenn jedoch eine Triggerfunktion verwendet wird, kann der Bezeichner quadrantin der pointsTabelle gespeichert und die point_labelsAnsicht ignoriert werden:

Beginnen wir zum Beispiel noch einmal mit einem etwas anderen Beispiel mit einer Tabelle und einer Ansicht:

-- 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();

Führen Sie im ersten Beispiel die Anweisungen INSERT INTO pointsund erneut aus CREATE OR REPLACE VIEW leader_line, da diese nicht geändert werden müssen. Aber ignoriere die leader_lineAnsicht.

Fügen Sie dann in QGIS Folgendes hinzu:

  • points - - geom
  • points - - label_geom
  • leader_line- geom- Primärschlüssel muss seingid

Konfigurieren Sie nun die Ebeneneigenschaften für pointsmit label_geomwie im ersten Beispiel für point_labels. Der quadrantBezeichner wird automatisch für neue und verschobene Punkte geändert. Sie werden diese Änderungen jedoch nur jedes Mal bemerken, wenn Sie Ihre Änderungen speichern.

Mike T.
quelle
Großartige Arbeit, aber wie kann man in QGIS ein neues Punkt-Feature mit zwei Geometriespalten in einer PostGIS-Tabelle hinzufügen?
Lunar Sea
@Lunar Sea - interessant, erhalten Sie zwei Einträge für die Tabelle, einen pro Geometrie, aber mit qgis können Sie das Geometriefeld nicht über die Kombination festlegen? Haben Sie versucht, eine manuelle SQL-Abfrage im Importdialogfeld zu verwenden (es ist die Spalte ganz rechts und oft nicht sichtbar ...)?
Steven Kay
Ich habe zwei Punktebenen in QGIS ( gid | label_geom | labelund gid, geom, label).
Lunar Sea
@LunarSea Ich habe ein zweites Beispiel mit einer Tabelle und einer Ansicht überarbeitet. Die Tabelle verfügt über Triggerfunktionen zum Bestimmen eines Standardwerts für label_geomund aktualisiert den quadrantWert ebenfalls, sodass die point_labelEbene / Ansicht nicht mehr benötigt wird.
Mike T
Gute Lösung für Mike! Nach dem Verschieben eines muss label_geomich die Ebenenbearbeitung speichern und die Leinwand aktualisieren, um die tatsächliche Position des Etiketts zu sehen. Es ist schade, dass es keine Möglichkeit gibt, einen Quadrantenspezifizierer mit dem QGIS-Tool "Etikett verschieben" zu verwenden.
Lunar Sea
1

okay .. da es sich um Karteneinheiten handelt, sollte dies innerhalb von Grenzen ziemlich einfach sein. Sie kennen bereits die Höhe des Etiketts. Wenn es in Punkten wäre, wäre es skalierungsabhängig.

Dies setzt eine feste Etikettengröße voraus. Wie gut dies funktioniert, hängt davon ab, wie einheitlich Ihre Etiketten sind und ob Sie eine proportionale Schriftart oder eine Schriftart mit fester Breite verwenden (feste Breite ist einfacher - multiplizieren Sie die Länge des Etiketts mit der Etikettengröße mit Holen Sie sich die Etikettenbreite).

Leider beantwortet dies nicht Ihre Frage, wie Sie die Grenzen des gerenderten Etiketts tatsächlich finden können .

Sie haben 4 Fälle (NE, NW, SE, SW).

Ich gehe davon aus, dass Ihre Tabelle so aussieht (Entschuldigung, einige Feldnamen sind unterschiedlich)

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;

Fügen Sie als Nächstes 4 Punkte (alle identisch) hinzu, jedoch mit Beschriftungen in den 4 Quadranten, um die 4 Hauptanwendungsfälle darzustellen

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')

Ich habe CRS 27700 verwendet (0,0 in links unten, Karteneinheiten in m). Ich habe eine Etikettenbreite von 50 und eine Kartenhöhe von 30 angenommen.

-- 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);

Affine Transformationen

Eine andere Möglichkeit besteht darin, alle führenden Linien auf 80% zu verkürzen.

  • Sie können ST_Translate (geom, -ST_X (geom), - ST_Y (geom)) verwenden, um die Linie zum Ursprung zu verschieben und geom_o zu erhalten
  • Verwenden Sie ST_Scale (geom_o, 0.8,0.8), um geom_o_scaled zu erhalten
  • dann mit ST_Translate (geom_o_scaled, ST_X (geom), ST_Y (geom)) wieder an die ursprüngliche Position übersetzen.

Dies könnte besser funktionieren, obwohl ich es nicht ausprobiert habe.

Steven Kay
quelle
Vielen Dank für Ihre Mühe, leider passen die Führungslinien nicht sehr gut zu den Etiketten.
Lunar Sea