mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Port hub lines algorithm to c++
Also: - optimise a bit - retain z/m values - use point on surface for non-point layers, instead of center of geometry bounding box
This commit is contained in:
parent
cac171a2e0
commit
cc19d0a70d
@ -236,11 +236,6 @@ qgis:generatepointspixelcentroidsalongline:
|
||||
qgis:generatepointspixelcentroidsinsidepolygons:
|
||||
|
||||
|
||||
qgis:hublines:
|
||||
This algorithm creates hub and spoke diagrams with lines drawn from points on the Spoke Point layer to matching points in the Hub Point layer.
|
||||
|
||||
Determination of which hub goes with each point is based on a match between the Hub ID field on the hub points and the Spoke ID field on the spoke points.
|
||||
|
||||
qgis:hypsometriccurves: >
|
||||
This algorithm computes hypsometric curves for an input Digital Elevation Model. Curves are produced as table files in an output folder specified by the user.
|
||||
|
||||
|
@ -1,139 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
Gridify.py
|
||||
---------------------
|
||||
Date : May 2010
|
||||
Copyright : (C) 2010 by Michael Minn
|
||||
Email : pyqgis at michaelminn dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from builtins import str
|
||||
|
||||
__author__ = 'Michael Minn'
|
||||
__date__ = 'May 2010'
|
||||
__copyright__ = '(C) 2010, Michael Minn'
|
||||
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
from qgis.core import (QgsFeature,
|
||||
QgsFeatureSink,
|
||||
QgsGeometry,
|
||||
QgsPointXY,
|
||||
QgsWkbTypes,
|
||||
QgsFeatureRequest,
|
||||
QgsProcessing,
|
||||
QgsProcessingParameterFeatureSource,
|
||||
QgsProcessingParameterField,
|
||||
QgsProcessingParameterFeatureSink,
|
||||
QgsProcessingException,
|
||||
QgsExpression,
|
||||
QgsProcessingUtils)
|
||||
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
|
||||
from processing.tools import vector
|
||||
|
||||
|
||||
class HubLines(QgisAlgorithm):
|
||||
HUBS = 'HUBS'
|
||||
HUB_FIELD = 'HUB_FIELD'
|
||||
SPOKES = 'SPOKES'
|
||||
SPOKE_FIELD = 'SPOKE_FIELD'
|
||||
OUTPUT = 'OUTPUT'
|
||||
|
||||
def group(self):
|
||||
return self.tr('Vector analysis')
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def tags(self):
|
||||
return self.tr('join,points,lines,connect,hub,spoke').split(',')
|
||||
|
||||
def initAlgorithm(self, config=None):
|
||||
|
||||
self.addParameter(QgsProcessingParameterFeatureSource(self.HUBS,
|
||||
self.tr('Hub layer')))
|
||||
self.addParameter(QgsProcessingParameterField(self.HUB_FIELD,
|
||||
self.tr('Hub ID field'), parentLayerParameterName=self.HUBS))
|
||||
self.addParameter(QgsProcessingParameterFeatureSource(self.SPOKES,
|
||||
self.tr('Spoke layer')))
|
||||
self.addParameter(QgsProcessingParameterField(self.SPOKE_FIELD,
|
||||
self.tr('Spoke ID field'), parentLayerParameterName=self.SPOKES))
|
||||
|
||||
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Hub lines'), QgsProcessing.TypeVectorLine))
|
||||
|
||||
def name(self):
|
||||
return 'hublines'
|
||||
|
||||
def displayName(self):
|
||||
return self.tr('Hub lines')
|
||||
|
||||
def processAlgorithm(self, parameters, context, feedback):
|
||||
if parameters[self.SPOKES] == parameters[self.HUBS]:
|
||||
raise QgsProcessingException(
|
||||
self.tr('Same layer given for both hubs and spokes'))
|
||||
|
||||
hub_source = self.parameterAsSource(parameters, self.HUBS, context)
|
||||
spoke_source = self.parameterAsSource(parameters, self.SPOKES, context)
|
||||
field_hub = self.parameterAsString(parameters, self.HUB_FIELD, context)
|
||||
field_hub_index = hub_source.fields().lookupField(field_hub)
|
||||
field_spoke = self.parameterAsString(parameters, self.SPOKE_FIELD, context)
|
||||
field_spoke_index = hub_source.fields().lookupField(field_spoke)
|
||||
|
||||
fields = QgsProcessingUtils.combineFields(hub_source.fields(), spoke_source.fields())
|
||||
|
||||
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
|
||||
fields, QgsWkbTypes.LineString, hub_source.sourceCrs())
|
||||
|
||||
hubs = hub_source.getFeatures()
|
||||
total = 100.0 / hub_source.featureCount() if hub_source.featureCount() else 0
|
||||
|
||||
matching_field_types = hub_source.fields().at(field_hub_index).type() == spoke_source.fields().at(field_spoke_index).type()
|
||||
|
||||
for current, hub_point in enumerate(hubs):
|
||||
if feedback.isCanceled():
|
||||
break
|
||||
|
||||
if not hub_point.hasGeometry():
|
||||
continue
|
||||
|
||||
p = hub_point.geometry().boundingBox().center()
|
||||
hub_x = p.x()
|
||||
hub_y = p.y()
|
||||
hub_id = str(hub_point[field_hub])
|
||||
hub_attributes = hub_point.attributes()
|
||||
|
||||
request = QgsFeatureRequest().setDestinationCrs(hub_source.sourceCrs())
|
||||
if matching_field_types:
|
||||
request.setFilterExpression(QgsExpression.createFieldEqualityExpression(field_spoke, hub_attributes[field_hub_index]))
|
||||
|
||||
spokes = spoke_source.getFeatures()
|
||||
for spoke_point in spokes:
|
||||
if feedback.isCanceled():
|
||||
break
|
||||
|
||||
spoke_id = str(spoke_point[field_spoke])
|
||||
if hub_id == spoke_id:
|
||||
p = spoke_point.geometry().boundingBox().center()
|
||||
spoke_x = p.x()
|
||||
spoke_y = p.y()
|
||||
|
||||
f = QgsFeature()
|
||||
f.setAttributes(hub_attributes + spoke_point.attributes())
|
||||
f.setGeometry(QgsGeometry.fromPolyline(
|
||||
[QgsPointXY(hub_x, hub_y), QgsPointXY(spoke_x, spoke_y)]))
|
||||
sink.addFeature(f, QgsFeatureSink.FastInsert)
|
||||
|
||||
feedback.setProgress(int(current * total))
|
||||
|
||||
return {self.OUTPUT: dest_id}
|
@ -84,7 +84,6 @@ from .Heatmap import Heatmap
|
||||
from .Hillshade import Hillshade
|
||||
from .HubDistanceLines import HubDistanceLines
|
||||
from .HubDistancePoints import HubDistancePoints
|
||||
from .HubLines import HubLines
|
||||
from .HypsometricCurves import HypsometricCurves
|
||||
from .IdwInterpolation import IdwInterpolation
|
||||
from .ImportIntoPostGIS import ImportIntoPostGIS
|
||||
@ -216,7 +215,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
|
||||
Hillshade(),
|
||||
HubDistanceLines(),
|
||||
HubDistancePoints(),
|
||||
HubLines(),
|
||||
HypsometricCurves(),
|
||||
IdwInterpolation(),
|
||||
ImportIntoPostGIS(),
|
||||
|
38
python/plugins/processing/tests/testdata/expected/hub_lines_multi_to_z.gml
vendored
Normal file
38
python/plugins/processing/tests/testdata/expected/hub_lines_multi_to_z.gml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ogr:FeatureCollection
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://ogr.maptools.org/ hub_lines_multi_to_z.xsd"
|
||||
xmlns:ogr="http://ogr.maptools.org/"
|
||||
xmlns:gml="http://www.opengis.net/gml">
|
||||
<gml:boundedBy>
|
||||
<gml:Box>
|
||||
<gml:coord><gml:X>0</gml:X><gml:Y>-5</gml:Y><gml:Z>0</gml:Z></gml:coord>
|
||||
<gml:coord><gml:X>7</gml:X><gml:Y>2</gml:Y><gml:Z>4</gml:Z></gml:coord>
|
||||
</gml:Box>
|
||||
</gml:boundedBy>
|
||||
|
||||
<gml:featureMember>
|
||||
<ogr:hub_lines_multi_to_z fid="points.3">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>5,2,0 2,2,2</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:d>2</ogr:d>
|
||||
<ogr:fid_2>points.2</ogr:fid_2>
|
||||
<ogr:elev>2</ogr:elev>
|
||||
</ogr:hub_lines_multi_to_z>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:hub_lines_multi_to_z fid="points.5">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>0,-5,0 1,1,3</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:d>3</ogr:d>
|
||||
<ogr:fid_2>points.0</ogr:fid_2>
|
||||
<ogr:elev>3</ogr:elev>
|
||||
</ogr:hub_lines_multi_to_z>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:hub_lines_multi_to_z fid="points.7">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>7,-1,0 0,-1,4</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:d>4</ogr:d>
|
||||
<ogr:fid_2>points.8</ogr:fid_2>
|
||||
<ogr:elev>4</ogr:elev>
|
||||
</ogr:hub_lines_multi_to_z>
|
||||
</gml:featureMember>
|
||||
</ogr:FeatureCollection>
|
44
python/plugins/processing/tests/testdata/expected/hub_lines_multi_to_z.xsd
vendored
Normal file
44
python/plugins/processing/tests/testdata/expected/hub_lines_multi_to_z.xsd
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
|
||||
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
|
||||
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
|
||||
<xs:complexType name="FeatureCollectionType">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="gml:AbstractFeatureCollectionType">
|
||||
<xs:attribute name="lockId" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="scope" type="xs:string" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="hub_lines_multi_to_z" type="ogr:hub_lines_multi_to_z_Type" substitutionGroup="gml:_Feature"/>
|
||||
<xs:complexType name="hub_lines_multi_to_z_Type">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="gml:AbstractFeatureType">
|
||||
<xs:sequence>
|
||||
<xs:element name="geometryProperty" type="gml:LineStringPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="d" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:totalDigits value="10"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="fid_2" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:maxLength value="255"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="elev" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:totalDigits value="10"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
80
python/plugins/processing/tests/testdata/expected/hub_lines_to_poly.gml
vendored
Normal file
80
python/plugins/processing/tests/testdata/expected/hub_lines_to_poly.gml
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ogr:FeatureCollection
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://ogr.maptools.org/ hub_lines_to_poly.xsd"
|
||||
xmlns:ogr="http://ogr.maptools.org/"
|
||||
xmlns:gml="http://www.opengis.net/gml">
|
||||
<gml:boundedBy>
|
||||
<gml:Box>
|
||||
<gml:coord><gml:X>0.5</gml:X><gml:Y>0</gml:Y></gml:coord>
|
||||
<gml:coord><gml:X>4.672552783109405</gml:X><gml:Y>5.588675623800385</gml:Y></gml:coord>
|
||||
</gml:Box>
|
||||
</gml:boundedBy>
|
||||
|
||||
<gml:featureMember>
|
||||
<ogr:hub_lines_to_poly fid="polys.0">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>0.5,0.5 1,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:name>aa</ogr:name>
|
||||
<ogr:intval>1</ogr:intval>
|
||||
<ogr:floatval>44.123456</ogr:floatval>
|
||||
<ogr:fid_2>points.0</ogr:fid_2>
|
||||
<ogr:id>1</ogr:id>
|
||||
<ogr:id2>2</ogr:id2>
|
||||
</ogr:hub_lines_to_poly>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:hub_lines_to_poly fid="polys.2">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>2.5,5.5 1,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:name>bb</ogr:name>
|
||||
<ogr:intval>1</ogr:intval>
|
||||
<ogr:floatval>0.123</ogr:floatval>
|
||||
<ogr:fid_2>points.0</ogr:fid_2>
|
||||
<ogr:id>1</ogr:id>
|
||||
<ogr:id2>2</ogr:id2>
|
||||
</ogr:hub_lines_to_poly>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:hub_lines_to_poly fid="polys.4">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>4,0 1,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:name>aa</ogr:name>
|
||||
<ogr:intval>1</ogr:intval>
|
||||
<ogr:floatval>3.33</ogr:floatval>
|
||||
<ogr:fid_2>points.0</ogr:fid_2>
|
||||
<ogr:id>1</ogr:id>
|
||||
<ogr:id2>2</ogr:id2>
|
||||
</ogr:hub_lines_to_poly>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:hub_lines_to_poly fid="polys.5">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>2.94337811900192,4.92360844529751 1,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:name>bb</ogr:name>
|
||||
<ogr:intval>1</ogr:intval>
|
||||
<ogr:floatval>0.123</ogr:floatval>
|
||||
<ogr:fid_2>points.0</ogr:fid_2>
|
||||
<ogr:id>1</ogr:id>
|
||||
<ogr:id2>2</ogr:id2>
|
||||
</ogr:hub_lines_to_poly>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:hub_lines_to_poly fid="polys.6">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>4.67255278310941,5.32264875239923 1,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:name>bb</ogr:name>
|
||||
<ogr:intval>1</ogr:intval>
|
||||
<ogr:floatval>0.123</ogr:floatval>
|
||||
<ogr:fid_2>points.0</ogr:fid_2>
|
||||
<ogr:id>1</ogr:id>
|
||||
<ogr:id2>2</ogr:id2>
|
||||
</ogr:hub_lines_to_poly>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:hub_lines_to_poly fid="polys.8">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>3.12072936660269,5.58867562380038 3,3</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:name>bb</ogr:name>
|
||||
<ogr:intval>2</ogr:intval>
|
||||
<ogr:floatval>0.123</ogr:floatval>
|
||||
<ogr:fid_2>points.1</ogr:fid_2>
|
||||
<ogr:id>2</ogr:id>
|
||||
<ogr:id2>1</ogr:id2>
|
||||
</ogr:hub_lines_to_poly>
|
||||
</gml:featureMember>
|
||||
</ogr:FeatureCollection>
|
64
python/plugins/processing/tests/testdata/expected/hub_lines_to_poly.xsd
vendored
Normal file
64
python/plugins/processing/tests/testdata/expected/hub_lines_to_poly.xsd
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
|
||||
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
|
||||
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
|
||||
<xs:complexType name="FeatureCollectionType">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="gml:AbstractFeatureCollectionType">
|
||||
<xs:attribute name="lockId" type="xs:string" use="optional"/>
|
||||
<xs:attribute name="scope" type="xs:string" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="hub_lines_to_poly" type="ogr:hub_lines_to_poly_Type" substitutionGroup="gml:_Feature"/>
|
||||
<xs:complexType name="hub_lines_to_poly_Type">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="gml:AbstractFeatureType">
|
||||
<xs:sequence>
|
||||
<xs:element name="geometryProperty" type="gml:LineStringPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="name" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:maxLength value="2"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="intval" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:totalDigits value="10"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="floatval" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:decimal">
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="fid_2" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:maxLength value="255"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="id" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:totalDigits value="10"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
<xs:element name="id2" nillable="true" minOccurs="0" maxOccurs="1">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:totalDigits value="10"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
@ -2444,7 +2444,7 @@ tests:
|
||||
name: expected/hub_distance_lines.gml
|
||||
type: vector
|
||||
|
||||
- algorithm: qgis:hublines
|
||||
- algorithm: native:hublines
|
||||
name: Hub lines
|
||||
params:
|
||||
HUBS:
|
||||
@ -2460,6 +2460,46 @@ tests:
|
||||
name: expected/hub_lines.gml
|
||||
type: vector
|
||||
|
||||
- algorithm: native:hublines
|
||||
name: Hub lines (with polygons)
|
||||
params:
|
||||
HUBS:
|
||||
name: dissolve_polys.gml
|
||||
type: vector
|
||||
HUB_FIELD: intval
|
||||
SPOKES:
|
||||
name: points.gml
|
||||
type: vector
|
||||
SPOKE_FIELD: id
|
||||
results:
|
||||
OUTPUT:
|
||||
name: expected/hub_lines_to_poly.gml
|
||||
type: vector
|
||||
compare:
|
||||
fields:
|
||||
fid: skip
|
||||
fid_2: skip
|
||||
|
||||
- algorithm: native:hublines
|
||||
name: Hub lines ( with z )
|
||||
params:
|
||||
HUBS:
|
||||
name: multipoints.gml
|
||||
type: vector
|
||||
HUB_FIELD: d
|
||||
SPOKES:
|
||||
name: pointsz.gml
|
||||
type: vector
|
||||
SPOKE_FIELD: elev
|
||||
results:
|
||||
OUTPUT:
|
||||
name: expected/hub_lines_multi_to_z.gml
|
||||
type: vector
|
||||
compare:
|
||||
fields:
|
||||
fid: skip
|
||||
fid_2: skip
|
||||
|
||||
- algorithm: qgis:pointstopath
|
||||
name: Points to path (non grouped)
|
||||
params:
|
||||
|
@ -91,6 +91,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
|
||||
addAlgorithm( new QgsMeanCoordinatesAlgorithm() );
|
||||
addAlgorithm( new QgsRasterLayerUniqueValuesReportAlgorithm() );
|
||||
addAlgorithm( new QgsJoinByAttributeAlgorithm() );
|
||||
addAlgorithm( new QgsJoinWithLinesAlgorithm() );
|
||||
}
|
||||
|
||||
void QgsSaveSelectedFeatures::initAlgorithm( const QVariantMap & )
|
||||
@ -2878,4 +2879,134 @@ QVariantMap QgsJoinByAttributeAlgorithm::processAlgorithm( const QVariantMap &pa
|
||||
}
|
||||
|
||||
|
||||
void QgsJoinWithLinesAlgorithm::initAlgorithm( const QVariantMap & )
|
||||
{
|
||||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "HUBS" ),
|
||||
QObject::tr( "Hub layer" ) ) );
|
||||
addParameter( new QgsProcessingParameterField( QStringLiteral( "HUB_FIELD" ),
|
||||
QObject::tr( "Hub ID field" ), QVariant(), QStringLiteral( "HUBS" ) ) );
|
||||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "SPOKES" ),
|
||||
QObject::tr( "Spoke layer" ) ) );
|
||||
addParameter( new QgsProcessingParameterField( QStringLiteral( "SPOKE_FIELD" ),
|
||||
QObject::tr( "Spoke ID field" ), QVariant(), QStringLiteral( "SPOKES" ) ) );
|
||||
|
||||
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Hub lines" ), QgsProcessing::TypeVectorLine ) );
|
||||
}
|
||||
|
||||
QString QgsJoinWithLinesAlgorithm::shortHelpString() const
|
||||
{
|
||||
return QObject::tr( "This algorithm creates hub and spoke diagrams by connecting lines from points on the Spoke layer to matching points in the Hub layer.\n\n"
|
||||
"Determination of which hub goes with each point is based on a match between the Hub ID field on the hub points and the Spoke ID field on the spoke points.\n\n"
|
||||
"If input layers are not point layers, a point on the surface of the geometries will be taken as the connecting location." );
|
||||
}
|
||||
|
||||
QgsJoinWithLinesAlgorithm *QgsJoinWithLinesAlgorithm::createInstance() const
|
||||
{
|
||||
return new QgsJoinWithLinesAlgorithm();
|
||||
}
|
||||
|
||||
QVariantMap QgsJoinWithLinesAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
||||
{
|
||||
if ( parameters.value( QStringLiteral( "SPOKES" ) ) == parameters.value( QStringLiteral( "HUBS" ) ) )
|
||||
throw QgsProcessingException( QObject::tr( "Same layer given for both hubs and spokes" ) );
|
||||
|
||||
std::unique_ptr< QgsFeatureSource > hubSource( parameterAsSource( parameters, QStringLiteral( "HUBS" ), context ) );
|
||||
std::unique_ptr< QgsFeatureSource > spokeSource( parameterAsSource( parameters, QStringLiteral( "SPOKES" ), context ) );
|
||||
if ( !hubSource || !spokeSource )
|
||||
return QVariantMap();
|
||||
|
||||
QString fieldHubName = parameterAsString( parameters, QStringLiteral( "HUB_FIELD" ), context );
|
||||
int fieldHubIndex = hubSource->fields().lookupField( fieldHubName );
|
||||
QString fieldSpokeName = parameterAsString( parameters, QStringLiteral( "SPOKE_FIELD" ), context );
|
||||
int fieldSpokeIndex = spokeSource->fields().lookupField( fieldSpokeName );
|
||||
|
||||
if ( fieldHubIndex < 0 || fieldSpokeIndex < 0 )
|
||||
throw QgsProcessingException( QObject::tr( "Invalid ID field" ) );
|
||||
|
||||
QgsFields fields = QgsProcessingUtils::combineFields( hubSource->fields(), spokeSource->fields() );
|
||||
|
||||
QgsWkbTypes::Type outType = QgsWkbTypes::LineString;
|
||||
bool hasZ = false;
|
||||
if ( QgsWkbTypes::hasZ( hubSource->wkbType() ) || QgsWkbTypes::hasZ( spokeSource->wkbType() ) )
|
||||
{
|
||||
outType = QgsWkbTypes::addZ( outType );
|
||||
hasZ = true;
|
||||
}
|
||||
bool hasM = false;
|
||||
if ( QgsWkbTypes::hasM( hubSource->wkbType() ) || QgsWkbTypes::hasM( spokeSource->wkbType() ) )
|
||||
{
|
||||
outType = QgsWkbTypes::addM( outType );
|
||||
hasM = true;
|
||||
}
|
||||
|
||||
QString dest;
|
||||
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields,
|
||||
outType, hubSource->sourceCrs() ) );
|
||||
if ( !sink )
|
||||
return QVariantMap();
|
||||
|
||||
auto getPointFromFeature = [hasZ, hasM]( const QgsFeature & feature )->QgsPoint
|
||||
{
|
||||
QgsPoint p;
|
||||
if ( feature.geometry().type() == QgsWkbTypes::PointGeometry && !feature.geometry().isMultipart() )
|
||||
p = *static_cast< QgsPoint *>( feature.geometry().geometry() );
|
||||
else
|
||||
p = *static_cast< QgsPoint *>( feature.geometry().pointOnSurface().geometry() );
|
||||
if ( hasZ && !p.is3D() )
|
||||
p.addZValue( 0 );
|
||||
if ( hasM && !p.isMeasure() )
|
||||
p.addMValue( 0 );
|
||||
return p;
|
||||
};
|
||||
|
||||
QgsFeatureIterator hubFeatures = hubSource->getFeatures();
|
||||
double step = hubSource->featureCount() > 0 ? 100.0 / hubSource->featureCount() : 1;
|
||||
int i = 0;
|
||||
QgsFeature hubFeature;
|
||||
while ( hubFeatures.nextFeature( hubFeature ) )
|
||||
{
|
||||
i++;
|
||||
if ( feedback->isCanceled() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
feedback->setProgress( i * step );
|
||||
|
||||
if ( !hubFeature.hasGeometry() )
|
||||
continue;
|
||||
|
||||
QgsPoint hubPoint = getPointFromFeature( hubFeature );
|
||||
QgsAttributes hubAttributes = hubFeature.attributes();
|
||||
|
||||
QgsFeatureRequest spokeRequest = QgsFeatureRequest().setDestinationCrs( hubSource->sourceCrs() );
|
||||
spokeRequest.setFilterExpression( QgsExpression::createFieldEqualityExpression( fieldSpokeName, hubFeature.attribute( fieldHubIndex ) ) );
|
||||
|
||||
QgsFeatureIterator spokeFeatures = spokeSource->getFeatures( spokeRequest );
|
||||
QgsFeature spokeFeature;
|
||||
while ( spokeFeatures.nextFeature( spokeFeature ) )
|
||||
{
|
||||
if ( feedback->isCanceled() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
QgsPoint spokePoint = getPointFromFeature( spokeFeature );
|
||||
QgsGeometry line( new QgsLineString( QVector< QgsPoint >() << hubPoint << spokePoint ) );
|
||||
|
||||
QgsFeature outFeature;
|
||||
QgsAttributes outAttributes = hubAttributes;
|
||||
outAttributes.append( spokeFeature.attributes() );
|
||||
outFeature.setAttributes( outAttributes );
|
||||
outFeature.setGeometry( line );
|
||||
sink->addFeature( outFeature, QgsFeatureSink::FastInsert );
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap outputs;
|
||||
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
|
||||
return outputs;
|
||||
}
|
||||
|
||||
|
||||
///@endcond
|
||||
|
@ -871,6 +871,30 @@ class QgsJoinByAttributeAlgorithm : public QgsProcessingAlgorithm
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Native join by lines ("hub lines") algorithm.
|
||||
*/
|
||||
class QgsJoinWithLinesAlgorithm : public QgsProcessingAlgorithm
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
QgsJoinWithLinesAlgorithm() = default;
|
||||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
|
||||
QString name() const override { return QStringLiteral( "hublines" ); }
|
||||
QString displayName() const override { return QObject::tr( "Join by lines (hub lines)" ); }
|
||||
virtual QStringList tags() const override { return QObject::tr( "join,connect,lines,points,hub,spoke" ).split( ',' ); }
|
||||
QString group() const override { return QObject::tr( "Vector analysis" ); }
|
||||
QString shortHelpString() const override;
|
||||
QgsJoinWithLinesAlgorithm *createInstance() const override SIP_FACTORY;
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariantMap processAlgorithm( const QVariantMap ¶meters,
|
||||
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
#endif // QGSNATIVEALGORITHMS_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user