Як створити динамічні лідерські лінії?


10

Я намагаюся створити динамічні лідерні лінії за допомогою подання PostGIS на додаток до інструменту QGIS „Move Label“.

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;

Це добре працює для всіх етикеток, WHERE ST_X(geom) < xcord_labelале створює неправильно виглядають лідери ліній для етикетки WHERE ST_X(geom) > xcord_label.

введіть тут опис зображення введіть тут опис зображення

Хтось знає, як правильно розмістити лідери ліній для етикетки WHERE ST_X(geom) > xcord_label? Чи є спосіб звернутися до координати xmax міток?

введіть тут опис зображення


1
це ваші мітки в точках або одиницях карт? Якщо це одиниці карт, слід досить легко відгадати висоту і тим самим скоротити ваш лідер, щоб компенсувати)
Steven Kay

Розмір етикетки в одиницях карти.
Місячне море

Відповіді:


9

Ви можете використовувати специфікатор розміщення квадранта QGIS, визначений у азимуті рядка, щоб розмістити кращу мітку. Квадрант визначає 8 позицій навколо точки:

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

Ось приклад навколо Null Island , створення таблиці та двох подань.

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;

Потім в QGIS додайте:

  • points - geom
  • leader_line- geom- первинний ключ повинен бутиgid
  • point_labels- geom- первинний ключ повинен бутиgid

QGIS

Тепер налаштуйте властивості шару для point_labels:

  • Змініть стиль, щоб крапка не малювалася, наприклад, змініть розмір на 0,0
  • Позначте цей шар labelі змініть розташування на "Зсув від точки", змінивши "Квадрант", щоб використовувати поле атрибутаquadrant

квадрант

Бінго!

Бінго

Зауважте, що для geographyтипів потрібен дещо інший підхід , оскільки ST_Azimuth поводиться по-різному.


Оновлення: додаючи нові точки до pointsшару, geomполе оновлюється, як зазвичай, але label_geomце не так. Для заповнення за замовчуванням значення label_geomнових точок потрібно створити тригер . Але якщо використовується тригерна функція, quadrantспецифікатор може зберігатися в pointsтаблиці, а point_labelsпредставлення можна ігнорувати:

Наприклад, почнемо ще раз із трохи іншого прикладу з однієї таблиці та одного виду:

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

З першого прикладу, повторіть оператори INSERT INTO pointsта CREATE OR REPLACE VIEW leader_lineоператори, оскільки вони не потребують змін. Але ігноруйте leader_lineпогляд.

Потім в QGIS додайте:

  • points - geom
  • points - label_geom
  • leader_line- geom- первинний ключ повинен бутиgid

Тепер налаштуйте властивості шару для pointsза label_geomдопомогою першого прикладу point_labels. Специфікатор quadrantбуде змінено автоматично для нових і переміщених точок, але ви помітите ці зміни лише щоразу, коли ви збережете свої зміни.


Чудова робота, але як додати нову функцію точки в QGIS, що має дві стовпчики геометрії в одній таблиці PostGIS?
Місячне море

@Lunar Sea - цікаво, ви отримуєте два записи для таблиці, по одній на геометрію, але qgis не дозволяє вам встановити поле геометрії з комбінації? Ви спробували скористатися ручним запитом sql у діалоговому вікні імпорту (це найправіший стовпець, і часто прихований поза зором ...)?
Стівен Кей

У QGIS ( gid | label_geom | labelі gid, geom, label) у мене два шари "балів" .
Місячне море

@LunarSea Я переробив другий приклад, який має одну таблицю та один вид. У таблиці є тригерні функції для визначення значення за замовчуванням для label_geom, а також оновлення quadrantзначення, тому point_labelшар / перегляд більше не потрібен.
Майк Т

Гарний обхід Майка! Після переміщення a label_geomя повинен зберегти редагування шару та оновити полотно, щоб побачити фактичне положення мітки. Шкода, що немає можливості використовувати специфікатор квадрату з інструментом QGIS "Перемістити мітку".
Місячне море

1

добре .. як це в одиницях карт, це має бути досить прямо, в межах обмежень. Ви вже знаєте висоту етикетки. Якби це було в балах, це було б залежно від масштабу.

Це передбачає фіксований розмір мітки, тому від того, наскільки добре це працює, залежить від того, наскільки однакові ваші мітки, і від того, чи використовуєте ви шрифт пропорційної чи фіксованої ширини (фіксовану ширину простіше - помножте довжину мітки на розмір мітки на отримати ширину мітки).

На жаль, це не дає відповіді на ваше запитання про те, як насправді знайти межі викладених .

у вас 4 випадки (СЗ, СЗ, ЮЗ, ЮЗ).

я припускаю, що ваша таблиця виглядає так (вибачте, деякі назви полів різні)

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;

Далі додайте 4 бали (усі однакові), але з мітками у чотирьох квадрантах, щоб представити чотири основні випадки використання

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

Я використав CRS 27700 (0,0 внизу ліворуч, одиниці карти в м). Я припустив ширину етикетки 50, висоту 30 одиниць карт.

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

Аффінні трансформації

Ще одна можливість - скоротити всі провідні лінії, скажімо, на 80%.

  • Ви можете використовувати ST_Translate (geom, -ST_X (geom), - ST_Y (geom)) для переміщення лінії до початку, щоб отримати geom_o
  • використовуйте ST_Scale (geom_o, 0.8,0.8), щоб отримати geom_o_scaled
  • потім повторно перекладіть за допомогою ST_Translate (geom_o_scaled, ST_X (geom), ST_Y (geom)) назад у вихідне положення.

Це може працювати і краще, хоча я цього не пробував.


Дякуємо за ваші зусилля, на жаль, лідери не дуже добре відповідають етикеткам.
Місячне море
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.