如何创建动态领导线?


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

标签大小以地图单位为单位。
月球海

Answers:


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

地理信息系统

现在为以下配置图层属性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 pointsand CREATE OR REPLACE VIEW leader_line语句,因为这些语句不需要修改。但是,请忽略该leader_line视图。

然后在QGIS中添加:

  • pointsgeom
  • pointslabel_geom
  • leader_linegeom–主键需要gid

现在,points使用label_geom的第一个示例配置的图层属性point_labels。该quadrant说明符将自动为新的和移动点进行修改,但你只会注意到这些每次保存您的修改时间的变化。


很棒的工作,但是如何在一个PostGIS表中具有两个几何列的QGIS中添加新的点要素呢?
月球海

@Lunar Sea-有趣的是,您是否为表格获得两个条目,每个几何图形一个,但是qgis不允许您从组合中设置几何图形字段?您是否尝试过在导入对话框中使用手动sql查询(它是最右边的列,通常隐藏在视图之外...)?
史蒂文·凯

我在QGIS(gid | label_geom | labelgid, geom, label)中有两个“点”层。
月球海

@LunarSea我重做了另一个示例,该示例具有一个表和一个视图。该表具有触发函数来确定的默认值label_geom,并且还更新该quadrant值,因此point_label不再需要图层/视图。
Mike T

不错的解决方法Mike!移动后,label_geom我必须保存图层编辑并刷新画布以查看标签的实际位置。遗憾的是,无法在QGIS“移动标签”工具中使用象限说明符。
月球海

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个象限中带有标签以表示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在左下,地图单位为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))重新转换回原始位置。

尽管我还没有尝试过,但这可能会更好。


谢谢您的努力,很遗憾,领导线与标签不符。
月球海
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.