Comment créer des lignes de repère dynamiques?

10

J'essaie de créer des lignes de repère dynamiques en utilisant une vue PostGIS en plus de l'outil QGIS «Déplacer l'étiquette».

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;

Cela fonctionne bien pour toutes les étiquettes, WHERE ST_X(geom) < xcord_labelmais crée des lignes de repère d'apparence incorrecte pour les étiquettes WHERE ST_X(geom) > xcord_label.

entrez la description de l'image ici entrez la description de l'image ici

Quelqu'un sait-il comment obtenir des lignes de repère correctement placées pour les étiquettes WHERE ST_X(geom) > xcord_label? Existe-t-il un moyen de se référer à la coordonnée xmax des étiquettes?

entrez la description de l'image ici

Mer lunaire
la source
1
vos étiquettes sont-elles en points ou en unités de carte? S'il s'agit d'unités de carte, il devrait être assez facile de deviner la hauteur, et ainsi de raccourcir votre ligne de repère pour compenser)
Steven Kay
La taille de l'étiquette est en unités de carte.
Lunar Sea

Réponses:

9

Vous pouvez utiliser le spécificateur de placement de quadrant de QGIS déterminé à partir de l'azimut de la ligne pour placer une meilleure étiquette. Le quadrant spécifie 8 positions autour d'un point:

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

Voici un exemple autour de Null Island , créant une table et deux vues.

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;

Ensuite, dans QGIS, ajoutez:

  • points - geom
  • leader_line- geom- la clé primaire doit êtregid
  • point_labels- geom- la clé primaire doit êtregid

QGIS

Configurez maintenant les propriétés de la couche pour point_labels:

  • Changez de style pour que le point ne soit pas tracé, par exemple, changez la taille en 0.0
  • Étiquetez cette couche avec labelet changez le placement en "Décalage par rapport au point", en modifiant le "Quadrant" pour utiliser le champ d'attributquadrant

quadrant

Bingo!

Bingo

Notez qu'une approche légèrement différente est requise pour les geographytypes, car ST_Azimuth se comporte différemment.


Mise à jour: lors de l'ajout de nouveaux points à la pointscouche, le geomchamp est mis à jour comme d'habitude, mais ce label_geomn'est pas le cas. Pour remplir une valeur par défaut de label_geomavec de nouveaux points, un déclencheur doit être créé . Mais si une fonction de déclenchement est utilisée, le quadrantspécificateur peut être stocké dans la pointstable et la point_labelsvue peut être ignorée:

Par exemple, recommençons avec un exemple légèrement différent avec une table et une vue:

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

Dans le premier exemple, refaites les instructions INSERT INTO pointset CREATE OR REPLACE VIEW leader_line, car celles-ci ne nécessitent pas de modification. Mais ignorez la leader_linevue.

Ensuite, dans QGIS, ajoutez:

  • points - geom
  • points - label_geom
  • leader_line- geom- la clé primaire doit êtregid

Configurez maintenant les propriétés de la couche pour pointsavec label_geomcomme le premier exemple l'a fait pour point_labels. Le quadrantspécificateur sera modifié automatiquement pour les points nouveaux et déplacés, mais vous ne remarquerez ces changements que chaque fois que vous enregistrerez vos modifications.

Mike T
la source
Excellent travail, mais comment ajouter une nouvelle entité ponctuelle dans QGIS ayant deux colonnes de géométrie dans une table PostGIS?
Lunar Sea
@Lunar Sea - intéressant, obtenez-vous deux entrées pour la table, une par géométrie, mais qgis ne vous permet pas de définir le champ de géométrie à partir du combo? Avez-vous essayé d'utiliser une requête SQL manuelle dans la boîte de dialogue d'importation (c'est la colonne la plus à droite, et souvent cachée hors de vue ...)?
Steven Kay
J'ai deux couches de «points» dans QGIS ( gid | label_geom | labelet gid, geom, label).
Lunar Sea
@LunarSea J'ai retravaillé un deuxième exemple qui a une table et une vue. Le tableau a des fonctions de déclenchement pour déterminer une valeur par défaut pour le label_geom, et met également à jour la quadrantvaleur aussi, de sorte que la point_labelcouche / vue n'est plus nécessaire.
Mike T
Belle solution de contournement Mike! Après avoir déplacé un label_geomje dois enregistrer le calque modifier et actualiser le canevas pour voir la position réelle de l'étiquette. Dommage qu'il n'y ait aucun moyen d'utiliser un spécificateur de quadrant avec l'outil QGIS "Déplacer l'étiquette".
Lunar Sea
1

ok .. car il est en unités de carte, cela devrait être assez simple, dans les limites. Vous connaissez déjà la hauteur de l'étiquette. Si c'était en points, cela dépendrait de l'échelle.

Cela suppose une taille d'étiquette fixe, donc la façon dont cela fonctionne dépend de l'uniformité de vos étiquettes et de l'utilisation ou non d'une police proportionnelle ou à largeur fixe (la largeur fixe est plus facile - multipliez la longueur de l'étiquette par la taille de l'étiquette à obtenir la largeur de l'étiquette).

Malheureusement, cela ne répond pas à votre question sur la façon de trouver réellement les limites de l'étiquette telles qu'elles sont rendues .

vous avez 4 cas (NE, NW, SE, SW).

je suppose que votre table ressemble à ceci (excuses, certains noms de champs sont différents)

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;

Ensuite, ajoutez 4 points (tous identiques) mais avec des étiquettes dans les 4 quadrants pour représenter les 4 principaux cas d'utilisation

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

J'ai utilisé CRS 27700 (0,0 en bas à gauche, unités de carte en m) J'ai supposé une largeur d'étiquette de 50, une hauteur de 30 unités de carte.

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

Transformations affines

Une autre possibilité est de raccourcir toutes les lignes de tête, soit 80%.

  • Vous pouvez utiliser ST_Translate (geom, -ST_X (geom), - ST_Y (geom)) pour déplacer la ligne vers l'origine pour obtenir geom_o
  • utilisez ST_Scale (geom_o, 0.8,0.8) pour obtenir geom_o_scaled
  • puis retraduisez en utilisant ST_Translate (geom_o_scaled, ST_X (geom), ST_Y (geom)) à la position d'origine.

Cela pourrait mieux fonctionner, même si je ne l'ai pas essayé.

Steven Kay
la source
Merci pour vos efforts, malheureusement les lignes de repère ne correspondent pas très bien aux étiquettes.
Lunar Sea