동적 지시선을 작성하는 방법은 무엇입니까?


10

QGIS“레이블 이동”도구 외에 PostGIS 뷰를 사용하여 동적 지시선을 작성하려고합니다.

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

라벨 크기는지도 단위입니다.
Lunar Sea

답변:


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에서 다음을 추가하십시오.

  • pointsgeom
  • leader_linegeom– 기본 키는gid
  • point_labelsgeom– 기본 키는gid

QGIS

이제 다음에 대한 레이어 속성을 구성하십시오 point_labels.

  • 점이 그려지지 않도록 스타일 변경 (예 : 크기를 0.0으로 변경)
  • 이 레이어에 레이블 label을 지정하고 속성 필드를 사용하도록 "사분면"을 수정하여 배치를 "포인트에서 오프셋"으로 변경하십시오.quadrant

사분면

빙고!

빙고

ST_Azimuth가 다르게 동작 geography하므로 유형에 대해 약간 다른 접근 방식이 필요합니다 .


업데이트 :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 pointsand CREATE OR REPLACE VIEW leader_line문을 다시 실행하십시오 . 그러나 leader_line견해를 무시하십시오 .

그런 다음 QGIS에서 다음을 추가하십시오.

  • pointsgeom
  • pointslabel_geom
  • leader_linegeom– 기본 키는gid

이제 첫 번째 예 에서 pointslabel_geom같이로 레이어 속성을 구성합니다 point_labels. quadrant지정은 새로운 이동 점에 대해 자동으로 수정됩니다,하지만 당신 만이 당신이 당신의 편집 내용을 저장 때마다 변경 알 수 있습니다.


훌륭하지만, 하나의 PostGIS 테이블에 두 개의 지오메트리 열이있는 QGIS에 새로운 포인트 피처를 추가하는 방법은 무엇입니까?
Lunar Sea

@Lunar Sea-흥미롭게도 테이블 당 2 개의 지오메트리 당 항목을 얻었으나 qgis를 사용하면 콤보에서 지오메트리 필드를 설정할 수 없습니까? 가져 오기 대화 상자에서 수동 SQL 쿼리를 사용해 보셨습니까 (가장 오른쪽 열이며 종종 보이지 않습니다 ...)?
Steven Kay

QGIS ( gid | label_geom | labelgid, geom, label) 에 두 개의 '포인트'레이어가 있습니다.
Lunar Sea

@LunarSea 하나의 테이블과 하나의 뷰가있는 두 번째 예제를 다시 작성했습니다. 이 테이블에는의 기본값을 결정하는 트리거 기능이 label_geom있으며 quadrant값 도 업데이트 되므로 point_label레이어 / 뷰가 더 이상 필요하지 않습니다.
Mike T

좋은 해결 방법 Mike! 이동 후 label_geom레이어 편집을 저장하고 캔버스를 새로 고쳐 레이블의 실제 위치를 봅니다. QGIS "레이블 이동"도구와 함께 사분면 지정자를 사용하는 방법이 없다는 것은 유감입니다.
음력 바다

1

알겠습니다 ..지도 단위에 있기 때문에 제한이 없어도 매우 간단합니다. 이미 라벨의 높이를 알고 있습니다. 그것이 포인트에 있다면 규모에 따라 다릅니다.

이것은 고정 된 레이블 크기를 가정하므로 레이블의 균일 성 및 비례 또는 고정 너비 글꼴 사용 여부에 따라 달라집니다 (고정 너비가 더 쉽습니다)-레이블 길이에 레이블 길이를 곱하십시오. 라벨 너비를 얻습니다).

슬프게도 이것은 실제로 렌더링 된 레이블의 경계를 찾는 방법에 대한 귀하의 질문에 대답하지 않습니다 .

네 경우가 있습니다 (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 포인트 (모두 동일)를 추가하여 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, 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))을 사용하여 원래 위치로 다시 번역하십시오.

시도하지는 않았지만 더 잘 작동 할 수 있습니다.


불행히도 리더 라인은 레이블과 잘 일치하지 않습니다.
Lunar Sea
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.