Preámbulo
Propongo una solución utilizando PyQGIS. Este es un tema complejo y estoy seguro de que hay (muchos) casos específicos que no consideré en mi enfoque (así, tome esta respuesta como un punto de partida y no como una solución completa ).
No he probado a fondo el código ya que no estaba seguro sobre el problema específico (ver mi comentario sin respuesta donde pedí algunas explicaciones), así que por favor haga algunas pruebas para validarlo: si va a utilizar el primer código adjunto, cree una copia de seguridad de sus capas, ya que las ediciones se registran inmediatamente en la geometría; en cambio, si desea hacer alguna prueba sin afectar a la capa original, intente utilizar el último código adjunto (ambos códigos son iguales, la única diferencia es que el primero registra las ediciones en la propia capa, mientras que el segundo las registra en una nueva capa de memoria).
Por último, supongamos que partimos de esta situación, en la que la línea roja es la directriz y los dos polígonos son dos capas separadas (cada una con una característica):
Explicación del flujo de trabajo
La solución requiere una capa de líneas como directriz y un conjunto de capas de polígonos que deben ser snapped . Mi enfoque itera sobre los nodos de cada característica poligonal e intenta hacer una collage con la geometría de la línea eliminando los vértices innecesarios y añadiendo otros nuevos.
Este planteamiento es bastante sencillo de entender, pero hay que tener en cuenta algunas cosas:
- Mientras que los vértices de los polígonos se ordenan automáticamente en el sentido de las agujas del reloj al digitalizar (es decir, sólo hay una dirección posible), una capa de líneas puede digitalizarse en dos direcciones diferentes según el lado desde el que se empiece (la dirección final es obviamente única, pero no se sabe si sigue el mismo orden de los vértices del polígono);
- Como consecuencia del punto anterior, es muy fácil crear características que son geométricamente erróneas y depende básicamente de cómo de las diferentes situaciones que pueden producirse (esta es la parte en la que mi solución no tiene sentido). (esta es la parte en la que hay que mejorar mi solución).
Dicho esto, mi flujo de trabajo podría resumirse así:
- leer la geometría de la característica de línea actual como una secuencia de coordenadas (se devuelven en una lista);
- leer la geometría actual del polígono como una secuencia de coordenadas (se devuelven en una lista);
- de la característica actual del polígono, encuentra el vértice más cercano tanto al inicio como al final de la línea;
- edita la lista desde el punto 2 asignando las coordenadas dentro de la lista desde el punto 1 en la posición adecuada.
Como puede comprender, los principales problemas se encuentran en el último paso, ya que no parecía demasiado fácil enumerar todos los casos posibles (sería más fácil si proporcionara un conjunto de datos de muestra).
Solución con ediciones aplicadas a la capa original
Puede ejecutar el siguiente código como un nuevo script desde la Caja de herramientas de procesamiento:
##input_guideline=vector line
##input_polygons=multiple vector
from qgis.core import *
from qgis.utils import iface
polygons = input_polygons.split(';')
line = QgsVectorLayer(input_guideline, input_guideline, 'ogr')
for linefeat in line.getFeatures():
line_geom = linefeat.geometry()
geom_to_add = line_geom.asPolyline()
(first_line_point, end_line_point) = (line_geom.interpolate(0), line_geom.interpolate(-1))
for polygon in polygons:
poly = QgsVectorLayer(polygon, polygon, 'ogr')
for feat in poly.getFeatures():
attrs = feat.attributes()
geom = feat.geometry()
coords = geom.asPolygon()[0]
first_vertex=geom.closestVertex(first_line_point.asPoint())[0]
last_vertex=geom.closestVertex(end_line_point.asPoint())[0]
first_indices = [i for i, x in enumerate(coords) if x == first_vertex]
first_index = first_indices[0]
second_index = coords.index(last_vertex)
if first_index < second_index:
coords[first_index:second_index] = geom_to_add
new_geom = QgsGeometry.fromPolygon([coords])
if not new_geom.isGeosValid():
first_index = first_indices[-1]
if first_index < second_index:
coords = geom.asPolygon()[0]
coords[second_index:first_index] = (geom_to_add)
new_geom = QgsGeometry.fromPolygon([coords])
else:
coords = geom.asPolygon()[0]
coords[second_index:first_index] = reversed(geom_to_add)
new_geom = QgsGeometry.fromPolygon([coords])
else:
coords[first_index:second_index] = reversed(geom_to_add)
new_geom = QgsGeometry.fromPolygon([coords])
if not new_geom.isGeosValid():
first_index = first_indices[-1]
coords = geom.asPolygon()[0]
coords[second_index:first_index] = reversed(geom_to_add)
new_geom = QgsGeometry.fromPolygon([coords])
poly.dataProvider().changeGeometryValues({feat.id(): new_geom})
iface.mapCanvas().refreshAllLayers()
y obtendrá este resultado:
Si todo funciona, los polígonos editados coincidirán perfectamente con la directriz y tendrán tanto las aristas como los vértices perfectamente alineados con ella.
Solución con ediciones aplicadas a una nueva capa
Esta solución difiere de la anterior porque guarda las ediciones en una nueva capa de memoria en lugar de grabar los cambios en la capa original (podría ser útil para hacer pruebas):
##input_guideline=vector line
##input_polygons=multiple vector
from qgis.core import *
polygons = input_polygons.split(';')
line = QgsVectorLayer(input_guideline, input_guideline, 'ogr')
for linefeat in line.getFeatures():
line_geom = linefeat.geometry()
geom_to_add = line_geom.asPolyline()
(first_line_point, end_line_point) = (line_geom.interpolate(0), line_geom.interpolate(-1))
for polygon in polygons:
poly = QgsVectorLayer(polygon, polygon, 'ogr')
# Create the output layer
crs = poly.crs().toWkt()
outLayer = QgsVectorLayer('Polygon?crs='+ crs, 'snapped' , 'memory')
prov = outLayer.dataProvider()
fields = poly.pendingFields() # Fields from the input layer
prov.addAttributes(fields) # Add input layer fields to the outLayer
outLayer.updateFields()
for feat in poly.getFeatures():
attrs = feat.attributes()
geom = feat.geometry()
coords = geom.asPolygon()[0]
first_vertex=geom.closestVertex(first_line_point.asPoint())[0]
last_vertex=geom.closestVertex(end_line_point.asPoint())[0]
first_indices = [i for i, x in enumerate(coords) if x == first_vertex]
first_index = first_indices[0]
second_index = coords.index(last_vertex)
if first_index < second_index:
coords[first_index:second_index] = geom_to_add
new_geom = QgsGeometry.fromPolygon([coords])
if not new_geom.isGeosValid():
first_index = first_indices[-1]
if first_index < second_index:
coords = geom.asPolygon()[0]
coords[second_index:first_index] = (geom_to_add)
new_geom = QgsGeometry.fromPolygon([coords])
else:
coords = geom.asPolygon()[0]
coords[second_index:first_index] = reversed(geom_to_add)
new_geom = QgsGeometry.fromPolygon([coords])
else:
coords[first_index:second_index] = reversed(geom_to_add)
new_geom = QgsGeometry.fromPolygon([coords])
if not new_geom.isGeosValid():
first_index = first_indices[-1]
coords = geom.asPolygon()[0]
coords[second_index:first_index] = reversed(geom_to_add)
new_geom = QgsGeometry.fromPolygon([coords])
outGeom = QgsFeature()
outGeom.setAttributes(attrs)
outGeom.setGeometry(new_geom)
prov.addFeatures([outGeom])
# Add the layer to the Layers Panel
QgsMapLayerRegistry.instance().addMapLayer(outLayer)