¿Cortar cadenas lineales con puntos?

10

Estaba comprobando la mejor manera de cortar las cadenas de líneas por puntos.

El escenario es: muchas calles, necesitan los segmentos cortados por puntos de intersección, algo así:

ejemplo

tengo

  • tabla de cadenas lineales (sin cortar por puntos)

  • tabla de puntos st_intersection

Necesito obtener los segmentos de cadena de líneas independientes cortados por la tabla de puntos de intersección.

Estoy usando las funciones de PostGIS y encontré varios enfoques, pero cada uno de ellos me ha dado algún tipo de problema.

Esto es lo que ya probé:

1

Tabla de líneas: 1 fila, st_memunion de 1200 líneas Tabla de puntos: 1700 filas (puntos)

Lo que es malo: realmente lleva mucho tiempo y memoria. No se pueden crear varias tablas al mismo tiempo porque la memoria simplemente no puede manejarlo. Y el resultado es sucio y desordenado. En lugar de darme el número de fila correcto, y necesito limpiarlo más tarde (bien explicado aquí Divida las líneas en los puntos de intersección )

CREATE TABLE lines_with_messy_result AS (
SELECT
((ST_DUMP(ST_SPLIT(a.geom,b.ix))).geom) as geom
FROM st_union_lines a
INNER JOIN lots_of_points b
ON ST_INTERSECTS(a.geom, b.ix)
);

--then need to clean this up
create table lines_segments_cleaned as (
SELECT DISTINCT ON (ST_AsBinary(geom)) 
geom 
FROM 
lines_with_messy_result
);

fuente de esta manera / enfoque: /programming/25753348/how-do-i-divide-city-streets-by-intersection-using-postgis


2

Misma tabla de líneas / puntos. Aún resultados desordenados y necesitan limpiar esto. Todavía hay mucho tiempo para finalizar la consulta.

--messy table    
Create table messy_lines_segments as 
Select 
row_number() over() as id,
(st_dump(st_split(input.geom, blade.ix))).geom as geom
from st_union_lines input
inner join lots_of_points blade on st_intersects(input.geom, blade.ix);

--then cleaning the messy table
delete from messy_lines_segments a
where exists 
(
select 1 from messy_lines_segments b where a.id != b.id and st_coveredby(b.geom,a.geom)
);

fuente de este camino / enfoque: dividir líneas en puntos de intersección


3

También encontré esta función: https://github.com/Remi-C/PPPP_utilities/blob/master/postgis/rc_Split_Line_By_Points.sql

que tiene la ventaja de que no deja un resultado messy_result que luego necesito limpiarlo. Pero necesita st_memunion de ambos lados (tabla de líneas y tabla de puntos)

Es un tipo de:

create table osm.calles_cortadas_03segmentos_sanluis as (
SELECT result.geom
FROM 
osm.calles_cortadas_01uniones_sanluis AS line, 
osm.calles_cortadas_00intersecciones_sanluis AS point,
rc_split_line_by_points(
input_line:=line.geom
,input_points:=point.ix
,tolerance:=4
) AS result
);

Pero también son horas muy largas para obtener los resultados. Y también probé con tablas más largas (10k líneas, 14k puntos) y solo tengo problemas de memoria.

También probé ArcGIS de Esri con también malos resultados ...

Entonces, ¿cuál es la mejor manera de hacer esto con las funciones geom de PostGIS?

Quiero decir, sin entrar en topología.

¿O cuál es tu mejor recomendación?

vlasvlasvlas
fuente
1
puntos de búfer con una tolerancia muy pequeña, borre las polilíneas dentro de los búferes, junte los nuevos puntos finales. .
Michael Stimson
44
Debería poner la actualización final como respuesta, ya que responde la pregunta
raphael
Está bien responder preguntas y puede ganar su reputación para desbloquear funcionalidades adicionales en este sitio.
PolyGeo

Respuestas:

5

¡Esta es la forma!

Ok, recibí una excelente respuesta de Remi-C y ahora esto funciona de maravilla:

La mejor solución sin topología de la historia ... REALMENTE funciona de manera rápida y fácil (créame, probé muchas formas de hacerlo):

--please add this function:
https://github.com/Remi-C/PPPP_utilities/blob/master/postgis/rc_split_multi.sql

-- please create a universal sequence for unique id multipurpose
CREATE SEQUENCE select_id
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 1
  CACHE 1
  CYCLE;



-- lines (non st_unioned linestrings, with gist index) : linetable
-- points (non st_unioned points, with gist index) : pointtable

-- first, lines with cuts (pointtable)
create table segment_lines_00 as (
WITH points_intersecting_lines AS( 
   SELECT lines.id AS lines_id, lines.geom AS line_geom,  ST_Collect(points.ix) AS blade
   FROM 
   linetable as lines, 
   pointtable as points
   WHERE st_dwithin(lines.geom, points.ix, 4) = true
   GROUP BY lines.id, lines.geom
)
SELECT lines_id, rc_Split_multi(line_geom, blade, 4)
FROM points_intersecting_lines
);
CREATE INDEX ON segment_lines_00 USING gist(rc_split_multi);


-- then, segments cutted by points
create table segment_lines_01 as (
select 
(ST_Dump(rc_split_multi)).geom as geom,  nextval('SELECT_id'::regclass) AS gid  from segment_lines_00
);
CREATE INDEX ON segment_lines_01 USING gist(geom);

¡Eso es!.

vlasvlasvlas
fuente
1

También agrego, además, mi propio método, sin usar st_split ():

Para cada línea verifico si hay algunos puntos de intersección.

Si es así, mi tentador se verá así:

[line_id, fraction]
[1      , 0       ]
[1      , 0.3     ]
[1      , 1       ]
[2      , 0       ]
[2      , 0.88    ]
[2      , 1       ]
...

Una tabla que contiene la ID de la línea y la fracción de la longitud de la línea donde un punto se cruza con la línea.

Luego combino la fracción por par para crear las nuevas líneas cortadas

Adquisición:

  • La tabla "puntos" contiene los puntos finales de cada línea.
  • Los puntos se cruzan perfectamente con las líneas.

La consulta:

DROP TABLE IF EXISTS temptable;

CREATE TABLE temptable as 
(
    SELECT ST_LineLocatePoint(a.geom,b.geom) as fraction,a.id, a.geom as shape
    FROM lines a, points b
    WHERE st_intersects(a.geom,b.geom)
    UNION
    SELECT 1 as fraction ,a.id, a.geom as shape -- It avoid to get an incomplete line when the original line is circular
    FROM lines a
    ORDER BY a.id, fraction -- important to keep the same order
);

ALTER TABLE temptable  ADD COLUMN gid SERIAL PRIMARY KEY;

DROP TABLE IF EXISTS LINE_SPLIT;
CREATE TABLE LINE_SPLIT AS
(
SELECT b.fend,a.*, ST_LineSubstring(a.shape,a.fstart,b.fend) as geom
FROM
    (
    SELECT *, fraction as fstart FROM temptable
    LIMIT (SELECT COUNT(*) FROM temptable)-1
    ) a,
    (
    SELECT fraction as fend,gid-1 as gid FROM temptable
    OFFSET 1
    ) b
WHERE a.gid = b.gid
AND a.fstart < b.fend
);

ALTER TABLE LINE_SPLIT DROP COLUMN IF EXISTS shape;
DROP TABLE IF EXISTS temptable;
obchardon
fuente
¡bonito! txs! también comprobaré este
vlasvlasvlas