Puede añadir las siguientes secuencias de comandos como Script de procesamiento a Processing Toolbox. A continuación, se pueden utilizar en un modelo gráfico. Los scripts no incluyen otra herramienta de Processing. Por lo tanto, puedes usarlos en diferentes versiones de QGIS 3.x sin problemas. Lo he probado en 3.4, 3.8, 3.10, 3.11, 3.12. (Por favor, lea también los comentarios bajo processAlgorithm
método)
Solución 1:
Creando una nueva capa, copiando el punto(NP) más cercano en la "capa de posibilidades" a la nueva capa y añadiendo nuevos campos. Creo que eso es lo que necesita.
from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsField, QgsFeature, QgsProcessing, QgsExpression,
QgsFeatureSink, QgsFeatureRequest, QgsProcessingAlgorithm,
QgsProcessingParameterFeatureSink, QgsProcessingParameterFeatureSource)
class ClosestPointOnAnotherPolygon(QgsProcessingAlgorithm):
POSSIBILITY_LYR = 'POSSIBILITY_LYR'
STOP_LYR = 'STOP_LYR'
OUTPUT = 'OUTPUT'
def initAlgorithm(self, config=None):
self.addParameter(
QgsProcessingParameterFeatureSource(
self.POSSIBILITY_LYR, self.tr('Possibility layer'), [QgsProcessing.TypeVectorPoint]))
self.addParameter(
QgsProcessingParameterFeatureSource(
self.STOP_LYR, self.tr('Stop layer'), [QgsProcessing.TypeVectorPoint]))
self.addParameter(
QgsProcessingParameterFeatureSink(
self.OUTPUT, self.tr('Output Layer'), QgsProcessing.TypeVectorPoint))
def processAlgorithm(self, parameters, context, feedback):
# Get Parameters
possibility_layer = self.parameterAsSource(parameters, self.POSSIBILITY_LYR, context)
stop_layer = self.parameterAsSource(parameters, self.STOP_LYR, context)
fields = possibility_layer.fields()
fields.append(QgsField("stopid", QVariant.Int, len=10))
fields.append(QgsField("join_dist", QVariant.Double, len=20, prec=5))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, possibility_layer.wkbType(),
possibility_layer.sourceCrs())
# iterate over stop features
for stop_feat in stop_layer.getFeatures():
# request string for points which have different polygonid
request = QgsFeatureRequest(QgsExpression('polygonid != ' + str(stop_feat["polygonid"])))
distances = {p: stop_feat.geometry().distance(p.geometry())
for p in possibility_layer.getFeatures(request)}
# get the feature which has the minimum distance value
nearest_point = min(distances, key=distances.get)
# create a new feature, set geometry and populate the fields
new_feat = QgsFeature(fields)
new_feat.setGeometry(nearest_point.geometry())
new_feat["possibilid"] = nearest_point["possibilid"]
new_feat["polygonid"] = nearest_point["polygonid"]
new_feat["stopid"] = stop_feat["stopid"]
new_feat["join_dist"] = distances[nearest_point]
# add nearest_point feature to the new layer
sink.addFeature(new_feat, QgsFeatureSink.FastInsert)
return {self.OUTPUT: dest_id}
def tr(self, string):
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return ClosestPointOnAnotherPolygon()
def name(self):
return 'ClosestPointOnAnotherPolygon'
def displayName(self):
return self.tr('Closest Point on Another Polygon')
def group(self):
return self.tr('FROM GISSE')
def groupId(self):
return 'from_gisse'
def shortHelpString(self):
return self.tr('finds closest point on another polygon')
La roja es la nueva capa.
Aquí está la Solución 1 un poco más universal:
from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsField, QgsFeature, QgsProcessing, QgsExpression,
QgsFeatureSink, QgsFeatureRequest, QgsProcessingAlgorithm,
QgsProcessingParameterFeatureSink, QgsProcessingParameterField, QgsProcessingParameterFeatureSource, QgsProcessingParameterEnum)
class ClosestPointWithAttributeCondition(QgsProcessingAlgorithm):
POSSIBILITY_LYR = 'POSSIBILITY_LYR'
POSSIBILITY_IDFIELD = 'POSSIBILITY_IDFIELD'
POSSIBILITY_POLYGONFIELD = 'POSSIBILITY_POLYGONFIELD'
STOP_LYR = 'STOP_LYR'
STOP_IDFIELD = 'STOP_IDFIELD'
STOP_POLYGONFIELD = 'STOP_POLYGONFIELD'
OPERATION = 'OPERATION'
OUTPUT = 'OUTPUT'
def initAlgorithm(self, config=None):
self.addParameter(
QgsProcessingParameterFeatureSource(
self.STOP_LYR, self.tr('Source (Find nearest Points for this Layer)'), [QgsProcessing.TypeVectorPoint]))
self.addParameter(
QgsProcessingParameterField(
self.STOP_IDFIELD, self.tr('Unique ID Field of Source Layer (Any Datatype)'),'ANY','STOP_LYR'))
self.addParameter(
QgsProcessingParameterField(
self.STOP_POLYGONFIELD, self.tr('Matching ID Field of Source Layer (Numerical)'),'ANY','STOP_LYR',0))
self.addParameter(
QgsProcessingParameterFeatureSource(
self.POSSIBILITY_LYR, self.tr('Possibilities (Find Points on this Layer)'), [QgsProcessing.TypeVectorPoint]))
self.addParameter(
QgsProcessingParameterField(
self.POSSIBILITY_IDFIELD, self.tr('Unique Possibility ID Field (Any Datatype)'),'ANY','POSSIBILITY_LYR'))
self.addParameter(
QgsProcessingParameterField(
self.POSSIBILITY_POLYGONFIELD, self.tr('Matching ID Field of Possibilities Layer (Numerical)'),'ANY','POSSIBILITY_LYR',0))
self.addParameter(
QgsProcessingParameterEnum(
self.OPERATION, self.tr('Matching ID Operation (Currently only != and = do work)'), ['!=','=','<','>','<=','>='])) #Only != and = will work here due to expression below
self.addParameter(
QgsProcessingParameterFeatureSink(
self.OUTPUT, self.tr('Output Layer'), QgsProcessing.TypeVectorPoint))
def processAlgorithm(self, parameters, context, feedback):
# Get Parameters
possibility_layer = self.parameterAsSource(parameters, self.POSSIBILITY_LYR, context)
possibility_idfield = self.parameterAsFields(parameters, self.POSSIBILITY_IDFIELD, context)
possibility_polygonfield = self.parameterAsFields(parameters, self.POSSIBILITY_POLYGONFIELD, context)
stop_layer = self.parameterAsSource(parameters, self.STOP_LYR, context)
stop_polygonfield = self.parameterAsFields(parameters, self.STOP_POLYGONFIELD, context)
stop_idfield = self.parameterAsFields(parameters, self.STOP_IDFIELD, context)
operation = self.parameterAsString(parameters, self.OPERATION, context)
operationlist = [' != ',' = ',' < ',' > ',' <= ',' >= ']
expressionoperator = str(operationlist[int(operation[0])])
fields = possibility_layer.fields()
fields.append(QgsField(stop_idfield[0]))
fields.append(QgsField("join_dist", QVariant.Double, len=20, prec=5))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, possibility_layer.wkbType(),
possibility_layer.sourceCrs())
# iterate over stop features
for stop_feat in stop_layer.getFeatures():
# request string for points which have different polygonid
request = QgsFeatureRequest(QgsExpression(possibility_polygonfield[0] + expressionoperator + str(stop_feat[stop_polygonfield[0]])))
distances = {p: stop_feat.geometry().distance(p.geometry())
for p in possibility_layer.getFeatures(request)}
# get the feature which has the minimum distance value
nearest_point = min(distances, key=distances.get)
# create a new feature, set geometry and populate the fields
new_feat = QgsFeature(fields)
new_feat.setGeometry(nearest_point.geometry())
new_feat[possibility_idfield[0]] = nearest_point[possibility_idfield[0]]
new_feat[possibility_polygonfield[0]] = nearest_point[possibility_polygonfield[0]]
new_feat[stop_idfield[0]] = stop_feat[stop_idfield[0]]
new_feat["join_dist"] = distances[nearest_point]
# add nearest_point feature to the new layer
sink.addFeature(new_feat, QgsFeatureSink.FastInsert)
return {self.OUTPUT: dest_id}
def tr(self, string):
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return ClosestPointWithAttributeCondition()
def name(self):
return 'ClosestPointWithAttributeCondition'
def displayName(self):
return self.tr('Closest Point With Attribute Condition')
def group(self):
return self.tr('FROM GISSE')
def groupId(self):
return 'from_gisse'
def shortHelpString(self):
return self.tr('This Algorithm finds the Sourcelayer`s closest Possibility-Points according the Operation on the Matching ID. The result is an extraction of the Possibilitylayer having the Possibility ID, Matching ID, Source ID and Join Distance.')
Solución 2
Puede obtener información sobre NP creando una nueva "capa de paradas" y añadiendo possibilityid
y polygonid
de NP a la nueva capa como nuevos campos.
from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsField, QgsProcessing, QgsExpression,
QgsFeatureSink, QgsFeatureRequest, QgsProcessingUtils,
QgsProcessingAlgorithm, QgsProcessingParameterString,
QgsProcessingParameterFeatureSink, QgsProcessingParameterFeatureSource)
class ClosestPointOnAnotherPolygon2(QgsProcessingAlgorithm):
POSSIBILITY_LYR = 'POSSIBILITY_LYR'
STOP_LYR = 'STOP_LYR'
OUTPUT = 'OUTPUT'
def initAlgorithm(self, config=None):
self.addParameter(
QgsProcessingParameterFeatureSource(
self.POSSIBILITY_LYR, self.tr('Possibility layer'), [QgsProcessing.TypeVectorPoint]))
self.addParameter(
QgsProcessingParameterFeatureSource(
self.STOP_LYR, self.tr('Stop layer'), [QgsProcessing.TypeVectorPoint]))
self.addParameter(
QgsProcessingParameterFeatureSink(
self.OUTPUT, self.tr('Output Layer'), QgsProcessing.TypeVectorPoint))
def processAlgorithm(self, parameters, context, feedback):
# Get Parameters
possibility_layer = self.parameterAsSource(parameters, self.POSSIBILITY_LYR, context)
stop_layer = self.parameterAsSource(parameters, self.STOP_LYR, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
stop_layer.fields(), stop_layer.wkbType(),
stop_layer.sourceCrs())
# Copy stop features to new stop layer
for feature in stop_layer.getFeatures():
sink.addFeature(feature, QgsFeatureSink.FastInsert)
new_stop_layer = QgsProcessingUtils.mapLayerFromString(dest_id, context)
new_stop_layer.startEditing()
# if not exist, add new fields
if new_stop_layer.fields().indexFromName("near_ps_id") == -1:
# field for possibilityid of the nearest point
new_stop_layer.addAttribute(QgsField("near_ps_id", QVariant.Int, len=10))
if new_stop_layer.fields().indexFromName("near_pl_id") == -1:
# field for polygonid of the nearest point
new_stop_layer.addAttribute(QgsField("near_pl_id", QVariant.Int, len=10))
for feature in new_stop_layer.getFeatures():
# get points which have different polygon-id than the stop-point itself has
request = QgsFeatureRequest(QgsExpression('polygonid != ' + str(feature["polygonid"])))
distances = {p: feature.geometry().distance(p.geometry())
for p in possibility_layer.getFeatures(request)}
# get the feature which has the minimum distance value
nearest_point = min(distances, key=distances.get)
# you may need to change 'possibilit' as 'possibilityid', (character limit of shapefile)
feature['near_ps_id'] = nearest_point["possibilit"]
feature['near_pl_id'] = nearest_point["polygonid"]
new_stop_layer.updateFeature(feature)
new_stop_layer.commitChanges()
return {self.OUTPUT: dest_id}
def tr(self, string):
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return ClosestPointOnAnotherPolygon2()
def name(self):
return 'ClosestPointOnAnotherPolygon2'
def displayName(self):
return self.tr('Closest Point on Another Polygon 2')
def group(self):
return self.tr('FROM GISSE')
def groupId(self):
return 'from_gisse'
def shortHelpString(self):
return self.tr('finds closest point on another polygon')
Un modelo de ejemplo: