7 votos

Clasificación de líneas curvas y rectas en QGIS

Tengo una capa en QGIS con miles de líneas y necesitaría separar las líneas curvas de las líneas rectas. ¿Puedo hacer esto fácilmente en QGIS?

8voto

Anton8000 Puntos 165

Puedes usar pyqgis y comparar la longitud de cada línea con la distancia entre los puntos de inicio y fin:

introducir descripción de la imagen aquí

lyr = QgsProject.instance().mapLayersByName('Partes individuales')[0]

recto = [] #Una lista para contener los IDs de las líneas rectas
curvado = []

for f in lyr.getFeatures():
    línea = f.geometry()
    longitudlínea = línea.length()
    vértices = [v for v in línea.vertices()]
    línea_más_corta = QgsLineString(vértices[0], vértices[-1])

    if longitudlínea > 1.03 * línea_más_corta.length(): #Si la longitud de la línea es mayor que la longitud de la línea más corta + 3 % entonces es curva. (Simplemente elegí 3 % subjetivamente)
        curvado.append(f.id()) #Agregar ID a la lista
    else:
        recto.append(f.id())

lyr.select(recto)

introducir descripción de la imagen aquí

8voto

Mr. Che Puntos 356
  1. Utilice la herramienta de simplificación para eliminar los vértices innecesarios que se encuentran en una línea recta como se describe en esta respuesta.

  2. Usando la calculadora de campos, cree un nuevo campo de tipo texto con la siguiente expresión:

if( num_points( nodes_to_points($geometry, True) )> 2, 'Curvo', 'Recto' )

Resultados: introduzca aquí la descripción de la imagen

5voto

chhh Puntos 1154

Dada una línea curva, se puede aplicar la siguiente expresión para identificar y visualizar aquellos subtramos de la línea que son (en absoluto o más o menos) rectos; o bien: cuáles son, por el contrario, los curvos. Tanto "recta" como "curva" pueden definirse libremente en función de dos parámetros: dist y tolerance :

  • dist establece una distancia. A continuación, la línea se segmenta en segmentos de esta longitud
  • tolerance se basa en la "rectitud"/"curvatura" de cada uno de estos segmentos.

Identificar los segmentos rectos (rojo), generados con una longitud de 100 [metros] y una tolerancia de <5 . La línea se segmenta en segmentos de 100 m. Sólo se dibujan en rojo los segmentos cuya diferencia acumulada de ángulos para 10 puntos distribuidos uniformemente en este segmento es inferior a 5:

enter image description here

Resalte los segmentos fuertemente curvados con los ajustes dist: 70 la tolerancia: >5 : enter image description here

Identifica los puntos en los que la línea gira hacia una dirección susbtantemente nueva con ajustes dist: 150 la tolerancia: >60 : enter image description here

Utilizando este método para identificar las curvas (rojo) de una carretera en un mapa base de OpenStreetMap con dist: 90 la tolerancia: >100 enter image description here

"Contando" las curvas en U con dist: 20 y la tolerancia: >100 : enter image description here

En la capa de línea, utilice esta expresión con generador de geometría y adaptar los valores de dist en la línea 3 (cambiar el valor de 150) y tolerance en la línea 6 (cambiar >30 y mantener las comillas simples ' ). Si está satisfecho con el resultado, puede crear geometrías reales a partir de él utilizando Geometría por expresión (véase a continuación una variante ligeramente simplificada):

with_variable(
    'dist',
    150,
    with_variable (
        'tolerance',
        '>30',
        collect_geometries (
            array_foreach (
                generate_series (0, length($geometry)/@dist),
                if (
                    eval (
                        (abs((line_interpolate_angle( $geometry, @element*@dist + 0)-line_interpolate_angle( $geometry, @element*@dist +@dist*0.1)))+
                        abs((line_interpolate_angle( $geometry, @element*@dist +@dist*0.1)-line_interpolate_angle( $geometry, @element*@dist +@dist*0.2)))+
                        abs((line_interpolate_angle( $geometry, @element*@dist +@dist*0.2)-line_interpolate_angle( $geometry, @element*@dist +@dist*0.3)))+
                        abs((line_interpolate_angle( $geometry, @element*@dist +@dist*0.3)-line_interpolate_angle( $geometry, @element*@dist +@dist*0.4)))+
                        abs((line_interpolate_angle( $geometry, @element*@dist +@dist*0.4)-line_interpolate_angle( $geometry, @element*@dist +@dist*0.5)))+
                        abs((line_interpolate_angle( $geometry, @element*@dist +@dist*0.5)-line_interpolate_angle( $geometry, @element*@dist +@dist*0.6)))+
                        abs((line_interpolate_angle( $geometry, @element*@dist +@dist*0.6)-line_interpolate_angle( $geometry, @element*@dist +@dist*0.7)))+
                        abs((line_interpolate_angle( $geometry, @element*@dist +@dist*0.7)-line_interpolate_angle( $geometry, @element*@dist +@dist*0.8)))+
                        abs((line_interpolate_angle( $geometry, @element*@dist +@dist*0.8)-line_interpolate_angle( $geometry, @element*@dist +@dist*0.9)))+
                        abs((line_interpolate_angle( $geometry, @element*@dist +@dist*0.9)-line_interpolate_angle( $geometry, @element*@dist +@dist)))) 
                        || @tolerance
                    ),
                    line_substring( $geometry, @element*@dist, @element*@dist+@dist),
                    make_line (make_point (0,0),make_point (0,0))
                )
            )
        )
    )
)

Cómo funciona:

  1. Segmenta la línea en segmentos de longitud libremente seleccionable con array_foreach () y generate_series()

  2. Cada segmento se subdivide de nuevo en 10 partes iguales (líneas 12 a 21).

  3. En cada uno de estos puntos, el ángulo (tangente, utilizando line_interpolate_angle() ) se mide.

  4. El ángulo se compara con el de la parte siguiente: se resta el ángulo de la parte siguiente al ángulo de la parte actual. Si la línea aquí es completamente recta, será la misma, por lo que la diferencia es 0. Cuanto mayor sea la diferencia, más se desvía la línea de una línea recta.

  5. Utilisez abs() para evitar los valores negativos.

  6. Suma todas las diferencias para las 10 partes de cada segmento.

  7. Compara el valor resultante con una condición. Para poder utilizar la condición sobre la expresión como variable, he tenido que introducirla después utilizando un eval() concatenar el número resultante con la cadena de la condición.

  8. Si esta condición es verdadera, se dibujará una geometría para el segmento actual, basada en el line_substring() función. En caso contrario, se crea una línea vacía.

  9. Con collect_geometries() El conjunto de geometrías se transfiere a una geometría (línea).


Variante

En la expresión anterior, hay una parte de repetición (líneas 12-21) que comienza con abs((line_interpolate_angle(... . Para evitar repetir la misma cadena una y otra vez, puedes crearla como una variable y llamarla dentro del eval() función. Cree las partes repetitivas de estas partes repetitivas como una cadena y concaténela con el @elemento variable de un array_foreach() que crea una línea individual para cada uno de los 10 subsegmentos. La expresión tiene el siguiente aspecto con la variable adicional expression (líneas 7-13) que se llama en la línea 19:

with_variable(
    'dist',
    100,
    with_variable (
        'tolerance',
        '>30',
        with_variable(
            'expression',
            replace (
                array_to_string (
                    array_foreach (
                        generate_series (0,9),
                        'abs((line_interpolate_angle( $geometry, @element *@dist + @dist*'  || (@element/10)  || ' )-line_interpolate_angle( $geometry,  @element  *@dist +@dist*'  ||  (@element/10+0.1)  || ')))'  || if (@element <9, '+', ''))), '+,','+'),
            collect_geometries (
                array_foreach (
                    generate_series (0, length($geometry)/@dist),
                    if (
                        eval (
                            (@expression) || @tolerance
                        ),
                        line_substring( $geometry, @element*@dist, @element*@dist+@dist),
                        make_line (make_point (0,0),make_point (0,0))
                    )
                )
            )
        )
    )
)

Observación sobre los propios conpetos de "curvo" y "recto":

Lo que significa "curvo" y "recto" depende mucho de la definición. ¿Una línea en zig-zag con ángulos de 90 grados (como una escalera) debe considerarse curva o recta? ¿Debe considerarse la "línea completa"? Entonces, sólo una línea matemática cumpliría esta condición. ¿O debemos ir a identificar los segmentos rectos que forman parte de la línea? ¿Qué longitud deben tener éstos?

Como esta solución y el ejemplo del mundo real (calle extraída de OpenStreetMap: Nueva carretera del paso del Gotardo ) muestran, cambiando los valores de entrada (y por lo tanto la definición), el resultado cambia dramáticamente.

Como la pregunta no proporcionaba ninguna idea sobre cómo debería definirse "curvo" o "recto" en el contexto dado, hay varias soluciones estupendas aquí, cada una con un enfoque diferente. Así que, de hecho, cada solución es de alguna manera una respuesta a una definición diferente.

Esta solución trata de flexibilizar la definición, ya que se pueden definir dos parámetros para adaptar la definición a diferentes necesidades y visualizar el resultado.

4voto

txindoki Puntos 146

Si la curvatura es solo una declaración verdadera/falsa por característica, se puede convertir en un valor de campo usando una expresión como:

if(round(area(oriented_bbox($geometry))) <> 0,'curvado','recto')

La caja delimitadora orientada para una polyline curvada tendrá un área distinta de cero, a diferencia de las líneas rectas con dos o más vértices alineados, como se ilustra a continuación (oriented_bbox como simbología y el área de ese polígono como etiqueta).

enter image description here

2voto

Mohammad Hammad Puntos 31

Crea un segmento desde el primer al último vértice y luego, para cada punto interior, determina su distancia de este segmento. Establece un umbral. Por debajo del umbral, la línea se considera recta. Por encima del umbral, está curvada.

Probablemente hay medidas de colinealidad más simples.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X