วิธีสร้างบรรทัดผู้นำแบบไดนามิก


10

ฉันกำลังพยายามสร้างบรรทัดผู้นำแบบไดนามิกโดยใช้มุมมอง PostGIS เพิ่มเติมจากเครื่องมือ 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;

นี้ทำงานได้ดีสำหรับป้ายกำกับทั้งหมดแต่สร้างผิดมองเส้นผู้นำสำหรับป้ายชื่อWHERE ST_X(geom) < xcord_labelWHERE ST_X(geom) > xcord_label

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

ไม่มีใครรู้วิธีการวางสายผู้นำสำหรับป้ายกำกับอย่างเหมาะสมWHERE ST_X(geom) > xcord_labelหรือไม่ มีวิธีใดบ้างในการอ้างถึงพิกัด xmax ของฉลาก?

ป้อนคำอธิบายรูปภาพที่นี่


1
มีป้ายชื่อของคุณในจุดหรือแผนที่หน่วยหากหน่วยแผนที่มันควรจะเป็นค่อนข้างง่ายที่จะคาดเดาความสูงและจึงร่นบรรทัดผู้นำของคุณเพื่อชดเชย)
สตีเฟนเคย์

ขนาดฉลากอยู่ในหน่วยแผนที่
Sea Lunar

คำตอบ:


9

คุณสามารถใช้ตัวระบุตำแหน่งquadrantของ QGIS ที่กำหนดจาก azimuth ของบรรทัดเพื่อวางเลเบลที่ดีขึ้น ควอแดรนท์ระบุ 8 ตำแหน่งรอบจุด:

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

นี่คือตัวอย่างรอบเกาะ Nullสร้างตารางและมุมมองสองมุม

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และเปลี่ยนตำแหน่งเป็น "Offset from point", แก้ไข "Quadrant" เพื่อใช้ฟิลด์ attributequadrant

วอด

บิงโก!

การเล่นชนิดหนึ่ง

โปรดทราบว่าจำเป็นต้องใช้วิธีที่แตกต่างกันเล็กน้อยสำหรับ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

@ Lunar Sea - น่าสนใจคุณได้รับสองรายการสำหรับหนึ่งรายการต่อรูปทรงเรขาคณิต แต่ qgis ไม่อนุญาตให้คุณตั้งค่าฟิลด์รูปทรงเรขาคณิตจากคำสั่งผสม? คุณได้ลองใช้การสอบถาม sql ด้วยตนเองในกล่องโต้ตอบการนำเข้า (เป็นคอลัมน์ที่ถูกต้องและมักจะถูกซ่อนอยู่นอกสายตา ... )?
Steven Kay

ฉันมี 'เลเยอร์' แต้มสองจุดใน QGIS ( gid | label_geom | labelและgid, geom, label)
Sea Lunar

@ LunarSea ฉันทำใหม่ตัวอย่างที่สองที่มีหนึ่งตารางและหนึ่งมุมมอง ตารางมีฟังก์ชันทริกเกอร์เพื่อกำหนดค่าเริ่มต้นสำหรับlabel_geomและยังปรับปรุงquadrantค่าเช่นกันดังนั้นจึงpoint_labelไม่จำเป็นต้องใช้เลเยอร์ / มุมมองอีกต่อไป
Mike T

วิธีแก้ปัญหาที่ดีไมค์! หลังจากย้ายlabel_geomฉันจะบันทึกเลเยอร์แก้ไขและรีเฟรชผืนผ้าใบเพื่อดูตำแหน่งที่แท้จริงของฉลาก เป็นเรื่องน่าเสียดายที่ไม่มีวิธีใช้ตัวระบุ Quadrant ด้วยเครื่องมือ "ย้ายป้ายกำกับ" ของ QGIS
Sea Lunar

1

โอเค .. เนื่องจากอยู่ในหน่วยแผนที่จึงควรตรงไปตรงมาภายในข้อ จำกัด คุณทราบความสูงของฉลากแล้ว ถ้ามันอยู่ในจุดมันจะขึ้นอยู่กับขนาด

วิธีนี้จะใช้ขนาดป้ายผนึกที่คงที่ดังนั้นขนาดของฉลากจึงเป็นไปได้ดีเพียงใดและขึ้นอยู่กับว่าคุณใช้แบบอักษรที่มีสัดส่วนหรือความกว้างคงที่หรือไม่ รับความกว้างของฉลาก)

น่าเศร้าที่นี่ไม่ได้ตอบคำถามของคุณว่าจะหาขอบเขตของฉลากได้อย่างไร

คุณมี 4 ราย (NE, NW, SE, SW)

ฉันถือว่าตารางของคุณมีลักษณะเช่นนี้ (ขออภัยชื่อฟิลด์บางชื่อแตกต่างกัน)

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 คะแนน (เหมือนกันทั้งหมด) แต่มีป้ายกำกับใน 4 Quadrants เพื่อเป็นตัวแทนของเคสหลักที่ใช้

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 ในมุมซ้ายล่างหน่วยแผนที่เป็น m) ฉันคิดว่ามีความกว้างของฉลาก 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)) กลับไปที่ตำแหน่งเดิม

สิ่งนี้อาจทำงานได้ดีขึ้นแม้ว่าฉันจะไม่ได้ลอง


ขอบคุณสำหรับผลงานของคุณโชคไม่ดีที่ลีดเดอร์ไลน์ไม่ตรงกับฉลากมากนัก
Sea Lunar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.