Bagaimana cara membuat garis pemimpin yang dinamis?

10

Saya mencoba membuat garis pemimpin dinamis dengan menggunakan tampilan PostGIS di samping alat QGIS "Pindahkan 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;

Ini berfungsi dengan baik untuk semua label WHERE ST_X(geom) < xcord_labeltetapi menciptakan garis pemimpin yang keliru untuk label WHERE ST_X(geom) > xcord_label.

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Apakah ada yang tahu cara mendapatkan garis pemimpin yang tepat untuk label WHERE ST_X(geom) > xcord_label? Apakah ada cara untuk merujuk ke koordinat xmax dari label?

masukkan deskripsi gambar di sini

Laut Bulan
sumber
1
apakah label Anda dalam poin atau unit peta? Jika ini adalah unit peta, itu seharusnya cukup mudah untuk menebak ketinggian, dan dengan demikian memperpendek garis pemimpin Anda untuk mengkompensasi)
Steven Kay
Ukuran label dalam satuan peta.
Laut Lunar

Jawaban:

9

Anda dapat menggunakan specifier penempatan kuadran QGIS yang ditentukan dari azimuth garis untuk menempatkan label yang lebih baik. Kuadran menentukan 8 posisi di sekitar titik:

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

Berikut ini contoh di sekitar Pulau Null , membuat tabel dan dua tampilan.

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;

Kemudian di QGIS, tambahkan:

  • points - geom
  • leader_line- geom- Kunci utama harusgid
  • point_labels- geom- Kunci utama harusgid

QGIS

Sekarang konfigurasikan properti layer untuk point_labels:

  • Ubah gaya sehingga intinya tidak digambar, mis. Ubah ukuran menjadi 0,0
  • Beri label lapisan ini dengan label, dan ubah penempatan ke "Offset from point", modifikasi "Quadrant" untuk menggunakan bidang atributquadrant

kuadran

Bingo!

Bingo

Perhatikan bahwa pendekatan yang sedikit berbeda diperlukan untuk geographytipe, karena ST_Azimuth berperilaku berbeda.


Pembaruan: Saat menambahkan poin baru ke pointslayer, geombidang diperbarui seperti biasa, tetapi label_geomtidak. Untuk mengisi nilai default label_geomdengan poin baru, pemicu perlu dibuat . Tetapi jika fungsi pemicu digunakan, quadrantspecifier dapat disimpan dalam pointstabel dan point_labelstampilan dapat diabaikan:

Sebagai contoh, mari kita mulai lagi dengan contoh yang sedikit berbeda dengan satu tabel dan satu tampilan:

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

Dari contoh pertama, lakukan kembali INSERT INTO pointsdan CREATE OR REPLACE VIEW leader_linepernyataan, karena ini tidak memerlukan modifikasi. Tapi abaikan leader_linepemandangan.

Kemudian di QGIS, tambahkan:

  • points - geom
  • points - label_geom
  • leader_line- geom- Kunci utama harusgid

Sekarang konfigurasikan properti layer pointsdengan dengan label_geomseperti contoh pertama untuk point_labels. The quadrantspecifier akan diubah secara otomatis untuk poin baru dan pindah, tetapi Anda hanya akan melihat ini berubah setiap kali Anda menyimpan suntingan Anda.

Mike T
sumber
Kerja bagus, tapi bagaimana cara menambahkan fitur titik baru di QGIS yang memiliki dua kolom geometri dalam satu tabel PostGIS?
Laut Lunar
@Lunar Sea - menarik, apakah Anda mendapatkan dua entri untuk tabel, satu per geometri, tetapi qgis tidak membiarkan Anda mengatur bidang geometri dari kombo? Sudahkah Anda mencoba menggunakan kueri sql manual di kotak dialog impor (ini adalah kolom paling kanan, dan sering disembunyikan di luar tampilan ...)?
Steven Kay
Saya memiliki dua lapisan 'poin' di QGIS ( gid | label_geom | labeldan gid, geom, label).
Laut Lunar
@ LunarSea Saya ulang contoh kedua yang memiliki satu tabel dan satu tampilan. Tabel memiliki fungsi pemicu untuk menentukan nilai default untuk label_geom, dan juga memperbarui quadrantnilai juga, sehingga point_labellapisan / tampilan tidak lagi diperlukan.
Mike T
Penanganan yang bagus Mike! Setelah memindahkan, label_geomsaya harus menyimpan layer edit dan menyegarkan kanvas untuk melihat posisi sebenarnya dari label. Sangat disayangkan bahwa tidak ada cara menggunakan specifier kuadran dengan alat "Pindahkan label" QGIS.
Laut Lunar
1

oke .. karena ada di unit peta ini harus cukup mudah, dalam batasan. Anda sudah tahu ketinggian label. Jika dalam poin itu akan tergantung pada skala.

Ini mengasumsikan ukuran label tetap, jadi seberapa baik ini bekerja tergantung pada seberapa seragam label Anda, dan apakah Anda menggunakan font proporsional atau lebar-lebar (lebar tetap lebih mudah - gandakan panjang label dengan ukuran label ke dapatkan lebar label).

Sayangnya ini tidak menjawab pertanyaan Anda tentang cara menemukan batas-batas label yang diberikan .

Anda memiliki 4 kasing (NE, NW, SE, SW).

saya berasumsi meja Anda terlihat seperti ini (permintaan maaf, beberapa nama bidang berbeda)

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;

Selanjutnya, tambahkan 4 poin (semua identik) tetapi dengan label di 4 kuadran untuk mewakili 4 kasus penggunaan utama

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

Saya menggunakan CRS 27700 (0,0 di kiri bawah, unit peta dalam m) Saya telah mengasumsikan label lebar 50, tinggi 30 unit peta.

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

Transformasi Affine

Kemungkinan lain adalah memperpendek semua lini terkemuka, katakanlah 80%.

  • Anda dapat menggunakan ST_Translate (geom, -ST_X (geom), - ST_Y (geom)) untuk memindahkan garis ke asal untuk mendapatkan geom_o
  • gunakan ST_Scale (geom_o, 0.8.0.8) untuk mendapatkan geom_o_scaled
  • kemudian menerjemahkan kembali menggunakan ST_Translate (geom_o_scaled, ST_X (geom), ST_Y (geom)) kembali ke posisi semula.

Ini mungkin bekerja lebih baik, walaupun saya belum mencobanya.

Steven Kay
sumber
Terima kasih atas upaya Anda, sayangnya garis pemimpin tidak cocok dengan label dengan baik.
Laut Merah