Como criar linhas de líderes dinâmicas?

10

Estou tentando criar linhas de liderança dinâmicas usando uma exibição PostGIS, além da ferramenta QGIS „Move 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;

Isso funciona bem para todos os rótulos, WHERE ST_X(geom) < xcord_labelmas cria linhas de líder com aparência errada para rótulos WHERE ST_X(geom) > xcord_label.

insira a descrição da imagem aqui insira a descrição da imagem aqui

Alguém sabe como obter linhas de liderança corretamente colocadas para etiquetas WHERE ST_X(geom) > xcord_label? Existe alguma maneira de se referir à coordenada xmax dos rótulos?

insira a descrição da imagem aqui

Mar Lunar
fonte
1
são seus rótulos em pontos ou mapear unidades Se é unidades do mapa deve ser bastante fácil de adivinhar a altura, e, assim, diminuir a sua linha de chamada para compensar)?
Steven Kay
O tamanho da etiqueta está em unidades de mapa.
Mar Lunar

Respostas:

9

Você pode usar o especificador de posicionamento de quadrante do QGIS determinado a partir do azimute da linha para colocar um rótulo melhor. O quadrante especifica 8 posições em torno de um ponto:

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

Aqui está um exemplo em torno da Ilha Null , criando uma tabela e duas visualizações.

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;

Em seguida, no QGIS, adicione:

  • points - geom
  • leader_line- geom- a chave primária precisa sergid
  • point_labels- geom- a chave primária precisa sergid

QGIS

Agora configure as propriedades da camada para point_labels:

  • Mude o estilo para que o ponto não seja desenhado, por exemplo, mude o tamanho para 0,0
  • Rotule essa camada com labele altere o posicionamento para "Offset from point", modificando o "Quadrante" para usar o campo de atributoquadrant

quadrante

Bingo!

Bingo

Observe que é necessária uma abordagem ligeiramente diferente para os geographytipos, pois o ST_Azimuth se comporta de maneira diferente.


Atualização: ao adicionar novos pontos à pointscamada, o geomcampo é atualizado normalmente, mas label_geomnão é. Para preencher um valor padrão de label_geomcom novos pontos, um gatilho precisa ser criado . Mas se uma função de disparo for usada, o quadrantespecificador poderá ser armazenado na pointstabela e a point_labelsexibição poderá ser ignorada:

Por exemplo, vamos começar novamente com um exemplo ligeiramente diferente com uma tabela e uma exibição:

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

A partir do primeiro exemplo, refaça as instruções INSERT INTO pointse CREATE OR REPLACE VIEW leader_line, pois elas não requerem modificação. Mas ignore a leader_linevista.

Em seguida, no QGIS, adicione:

  • points - geom
  • points - label_geom
  • leader_line- geom- a chave primária precisa sergid

Agora configure as propriedades da camada para pointscom label_geomcomo no primeiro exemplo point_labels. O quadrantespecificador será modificado automaticamente para pontos novos e movidos, mas você notará essas alterações sempre que salvar suas edições.

Mike T
fonte
Bom trabalho, mas como adicionar um novo recurso de ponto no QGIS com duas colunas de geometria em uma tabela PostGIS?
Mar Lunar
@Lunar Sea - interessante, você recebe duas entradas para a tabela, uma por geometria, mas o qgis não permite definir o campo de geometria no combo? Você já tentou usar uma consulta sql manual na caixa de diálogo de importação (é a coluna mais à direita e geralmente fica oculta ...)?
Steven Kay
Eu tenho duas camadas de 'pontos' no QGIS ( gid | label_geom | labele gid, geom, label).
Mar Lunar
@LunarSea Retrabalhei um segundo exemplo que tem uma tabela e uma exibição. A tabela possui funções de acionamento para determinar um valor padrão para label_geome também atualiza o quadrantvalor, para que a point_labelcamada / visualização não seja mais necessária.
Mike T
Solução agradável Mike! Depois de mover um label_geomeu tenho que salvar a edição de camada e atualizar a tela para ver a posição real do rótulo. É uma pena que não seja possível usar um especificador de quadrante com a ferramenta "Mover rótulo" do QGIS.
Mar Lunar
1

ok .. como está nas unidades do mapa, isso deve ser bastante direto, dentro de limitações. Você já sabe a altura do rótulo. Se estivesse em pontos, seria dependente da escala.

Isso pressupõe um tamanho fixo de etiqueta, portanto, o quão bem isso funciona depende de quão uniformes são suas etiquetas e se você usa ou não uma fonte proporcional ou de largura fixa (largura fixa é mais fácil - multiplique o comprimento da etiqueta pelo tamanho da etiqueta para obtenha a largura da etiqueta).

Infelizmente, isso não responde à sua pergunta sobre como encontrar os limites do rótulo como renderizados .

você tem 4 casos (NE, NW, SE, SW).

Presumo que sua tabela se parece com isso (desculpas, alguns nomes de campo são diferentes)

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;

Em seguida, adicione 4 pontos (todos idênticos), mas com rótulos nos 4 quadrantes para representar os 4 principais casos de uso

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

Usei o CRS 27700 (0,0 em unidades do mapa inferior esquerdo, em m). Assumi uma largura de etiqueta 50, altura 30 unidades de mapa.

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

Transformações Afins

Outra possibilidade é reduzir todas as linhas principais, ou seja, 80%.

  • Você pode usar ST_Translate (geom, -ST_X (geom), - ST_Y (geom)) para mover a linha para a origem e obter geom_o
  • use ST_Scale (geom_o, 0.8,0.8) para obter geom_o_scaled
  • retranslate usando ST_Translate (geom_o_scaled, ST_X (geom), ST_Y (geom)) de volta à posição original.

Isso pode funcionar melhor, embora eu não tenha tentado.

Steven Kay
fonte
Obrigado por seus esforços, infelizmente as linhas de líderes não correspondem muito bem aos rótulos.
Mar Lunar