Dinamik lider çizgileri nasıl oluşturulur?


10

QGIS „Etiketi Taşı“ aracına ek olarak bir PostGIS görünümü kullanarak dinamik lider satırları oluşturmaya çalışıyorum.

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;

Bu, tüm etiketler için iyi çalışır, WHERE ST_X(geom) < xcord_labelancak etiketler için yanlış görünümlü lider çizgiler oluşturur WHERE ST_X(geom) > xcord_label.

resim açıklamasını buraya girin resim açıklamasını buraya girin

Etiketler için doğru şekilde yerleştirilmiş lider çizgileri nasıl edineceğinizi bilen var mı WHERE ST_X(geom) > xcord_label? Etiketlerin xmax koordinatlarına başvurmanın bir yolu var mı?

resim açıklamasını buraya girin


1
Harita birimleri ise yüksekliği tahmin etmek ve böylece telafi etmek için lider çizginizi kısaltmak oldukça kolay olmalıdır)
Steven Kay

Etiket boyutu harita birimindedir.
Ay Denizi

Yanıtlar:


9

Daha iyi bir etiket yerleştirmek için QGIS'in satırın azimutundan belirlenen çeyrek daire yerleşim belirtecini kullanabilirsiniz . Çeyrek, bir nokta etrafında 8 konum belirtir:

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

İşte bir masa ve iki görünüm oluşturan Null Adası çevresinde bir örnek .

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;

Ardından QGIS'de şunları ekleyin:

  • points - geom
  • leader_line- geom- birincil anahtarıngid
  • point_labels- geom- birincil anahtarıngid

QGIS

Şimdi katman özelliklerini aşağıdakiler için yapılandırın point_labels:

  • Nokta çizilmeyecek şekilde stili değiştirin, örneğin boyutu 0,0 olarak değiştirin
  • Bu katmanı etiketleyin labelve yerleşimi "Noktadan ofset" olarak değiştirin, nitelik alanını kullanmak için "Çeyrek Daire" yi değiştirinquadrant

çeyrek daire

Bingo!

Bingo

ST_Azimuth farklı davrandığından geography, türler için biraz farklı bir yaklaşımın gerekli olduğunu unutmayın .


Güncelleme:points Katmana yeni noktalar eklerken , geomalan her zamanki gibi güncellenir, ancak label_geomdeğildir. Varsayılan değerini label_geomyeni noktalarla doldurmak için bir tetikleyici oluşturulması gerekir . Ancak bir tetikleyici işlevi kullanılırsa, quadrantbelirtici pointstabloda saklanabilir ve point_labelsgörünüm yoksayılabilir:

Örneğin, bir tablo ve bir görünümle biraz farklı bir örnekle tekrar başlayalım:

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

İlk örnekten, değişiklik gerektirmediği için INSERT INTO pointsve CREATE OR REPLACE VIEW leader_lineifadelerini yeniden yapın . Ama görüşü görmezden gel leader_line.

Ardından QGIS'de şunları ekleyin:

  • points - geom
  • points - label_geom
  • leader_line- geom- birincil anahtarıngid

Şimdi için katman özelliklerini yapılandırmak pointsile label_geomilk örnek için yaptığı gibi point_labels. quadrantBelirteci yeni ve taşınan puan için otomatik değiştirilmiş olacaktır, ancak yalnızca bu düzenlemelerinizi kaydetmek her zaman değiştirir göreceksiniz.


Harika bir çalışma, ancak QGIS'te bir PostGIS tablosunda iki geometri sütununa sahip yeni bir nokta özelliği nasıl eklenir?
Lunar Sea

@Lunar Sea - ilginç, her geometri için bir tane olmak üzere tablo için iki giriş alıyor musunuz, ancak qgis geometri alanını combo'dan ayarlamanıza izin vermiyor mu? İçe aktar iletişim kutusunda manuel bir sql sorgusu kullanmayı denediniz mi (en sağdaki sütundur ve genellikle görünmez olarak gizlenmiştir ...)?
Steven Kay

QGIS'de ( gid | label_geom | labelve gid, geom, label) iki 'nokta' katmanım var .
Lunar Sea

@LunarSea Bir tablo ve bir görünüm içeren ikinci bir örnek üzerinde çalıştım. Tablo, için varsayılan bir değer belirlemek üzere tetikleyici işlevlere sahiptir label_geomve ayrıca quadrantdeğeri de güncelleştirir , böylece point_labelkatman / görünüm artık gerekli değildir.
Mike T

Güzel bir çözüm Mike! Taşıdıktan sonra bir label_geomkatman düzenlemesini kaydetmem ve tuvalin gerçek konumunu görmek için tuvali yenilemem gerekir. QGIS "Etiketi taşı" aracıyla bir çeyrek değer belirteci kullanmanın bir yolu olmaması üzücü.
Ay Denizi

1

tamam .. harita birimlerinde olduğu gibi, bu sınırlamalar dahilinde oldukça basit olmalıdır. Etiketin yüksekliğini zaten biliyorsunuz. Puanlar içinde olsaydı ölçeğe bağlı olurdu.

Bu, sabit bir etiket boyutu olduğunu varsaydığından, bunun ne kadar iyi çalıştığı, etiketlerinizin ne kadar düzgün olduğuna ve orantılı veya sabit genişlikli bir yazı tipi kullanıp kullanmayacağınıza bağlıdır (sabit genişlik daha kolaydır - etiketin uzunluğunu etiket boyutuyla çarpın etiket genişliğini edinin).

Ne yazık ki bu, etiketin oluşturulduğu gibi sınırlarını nasıl bulacağınız hakkındaki sorunuza cevap vermiyor .

4 vakanız var (NE, NW, SE, SW).

tablonuzun böyle göründüğünü varsayıyorum (özür dileriz, bazı alan adları farklıdır)

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;

Ardından, 4 ana kullanım örneğini temsil etmek için 4 nokta (hepsi aynı) ancak 4 çeyrekte etiketlerle ekleyin

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 kullandım (sol altta 0,0, m cinsinden harita birimleri) Etiket genişliği 50, yükseklik 30 harita birimleri varsaydım.

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

Afin Dönüşümler

Başka bir olasılık, tüm önde gelen hatları% 80 kısaltmaktır.

  • Çizgiyi geom_o almak için başlangıç ​​noktasına taşımak için ST_Translate (geom, -ST_X (geom), - ST_Y (geom)) kullanabilirsiniz.
  • geom_o_scaled almak için ST_Scale (geom_o, 0.8,0.8) kullanın
  • daha sonra ST_Translate (geom_o_scaled, ST_X (geom), ST_Y (geom)) kullanarak orijinal konumuna geri çevirin.

Denememiş olmama rağmen bu daha iyi olabilir.


Çabalarınız için teşekkür ederim, maalesef lider çizgileri etiketlerle çok iyi eşleşmiyor.
Lunar Sea
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.