Cómo probar si un punto está sobre una línea utilizando PyQGIS y objetos de la clase QgsGeometry

Probar si un punto está sobre una línea parece fácil y más si lo hemos digitalizado habilitando las opciones de snapping. Sin embargo, lo que parece obvio puede complicarse si no se intuye como proceder. Observemos las capas vectoriales de la siguiente imagen donde en el vectorial de puntos existen 3 features que han sido colocados sobre la línea mediante las opciones de snapping y uno de ellos se ha hecho coincidir con uno de los vértices de la línea.

pointin1

Como se tiene más de una capa éstas se van a administrar mediante un objeto ‘mapcanvas’. Por otra parte, parece lógico pensar que métodos de la clase QgsGeometry como ‘within’, intersects’ o ‘crosses’ pudiesen ayudar. Cuando los introducimos en el código siguiente:

mapcanvas = iface.mapCanvas()

layers = mapcanvas.layers()

feat_points = [ feat for feat in layers[0].getFeatures() ]

feat_line = layers[1].getFeatures().next()

for point in feat_points:
    print "within: ", point.geometry().within(feat_line.geometry())
    print "intersects: ", point.geometry().intersects(feat_line.geometry())
    print "crosses: ", point.geometry().crosses(feat_line.geometry())

al ejecutarlo en la Python Console de QGIS se obtiene:

within:  False
intersects:  True
crosses:  False
within:  False
intersects:  False
crosses:  False
within:  False
intersects:  False
crosses:  False
within:  False
intersects:  False
crosses:  False
within:  False
intersects:  False
crosses:  False
within:  False
intersects:  False
crosses:  False

es decir, la única opción que funciona es ‘intersects’ y sólo si coincide exactamente con el vértice de la línea. Aunque existe un método ‘distance’ en QgsGeometry voy a usar una definición de colinealidad que encontré en Internet y que señala que si un punto está sobre un segmento de recta entonces la suma de la distancia del punto a cada uno de sus vértices es igual a la distancia que existe entre los vértices (también se pudo escoger la opción de la tangente).

El algoritmo está recogido en el código siguiente:

from math import sqrt

mapcanvas = iface.mapCanvas()

layers = mapcanvas.layers()

feat_points = [ feat for feat in layers[0].getFeatures() ]

feat_line = layers[1].getFeatures().next()

points_line = feat_line.geometry().asPolyline() 

for point in feat_points:
    sum = 0
    for i, point_line in enumerate(points_line):
        dist = sqrt(point.geometry().asPoint().sqrDist(point_line))
        sum += dist
        print "segment[{}]: {:.5f}".format(i+1, dist)
    print "dif. sum - length_line: {}".format(sum - sqrt(points_line[0].sqrDist(points_line[1])))
    print "dif. sum - length_line equal sum? {}".format(sqrt(points_line[0].sqrDist(points_line[1])) == sum)

Cuando se ejecuta en la Python Console se observa que el criterio de colinealidad puede estar basado en la distancia. Sin embargo, el segundo punto, aún siendo colineal, produce un ‘False’ en los resultados. Esto significa que el snapping tiene una cierta tolerancia que, a pesar de ser muy baja, puede inducir errores y debe ser considerada en el código.

segment[1]: 0.00000
segment[2]: 69287.00917
dif. sum - length_line: 0.0
dif. sum - length_line equal sum? True
segment[1]: 40745.23152
segment[2]: 28541.77765
dif. sum - length_line: -1.45519152284e-11
dif. sum - length_line equal sum? False
segment[1]: 54800.06408
segment[2]: 14486.94509
dif. sum - length_line: 0.0
dif. sum - length_line equal sum? True
segment[1]: 50359.28949
segment[2]: 21239.42949
dif. sum - length_line: 2311.70980162
dif. sum - length_line equal sum? False
segment[1]: 31229.30373
segment[2]: 41162.36693
dif. sum - length_line: 3104.66148527
dif. sum - length_line equal sum? False
segment[1]: 24846.14238
segment[2]: 45456.71718
dif. sum - length_line: 1015.85039441
dif. sum - length_line equal sum? False
Esta entrada fue publicada en PyQGIS, QGIS, SIG, Software Libre. Guarda el enlace permanente.

Responder

Por favor, inicia sesión con uno de estos métodos para publicar tu comentario:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s