mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -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: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: >
|
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.
|
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 .Hillshade import Hillshade
|
||||||
from .HubDistanceLines import HubDistanceLines
|
from .HubDistanceLines import HubDistanceLines
|
||||||
from .HubDistancePoints import HubDistancePoints
|
from .HubDistancePoints import HubDistancePoints
|
||||||
from .HubLines import HubLines
|
|
||||||
from .HypsometricCurves import HypsometricCurves
|
from .HypsometricCurves import HypsometricCurves
|
||||||
from .IdwInterpolation import IdwInterpolation
|
from .IdwInterpolation import IdwInterpolation
|
||||||
from .ImportIntoPostGIS import ImportIntoPostGIS
|
from .ImportIntoPostGIS import ImportIntoPostGIS
|
||||||
@ -216,7 +215,6 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
|
|||||||
Hillshade(),
|
Hillshade(),
|
||||||
HubDistanceLines(),
|
HubDistanceLines(),
|
||||||
HubDistancePoints(),
|
HubDistancePoints(),
|
||||||
HubLines(),
|
|
||||||
HypsometricCurves(),
|
HypsometricCurves(),
|
||||||
IdwInterpolation(),
|
IdwInterpolation(),
|
||||||
ImportIntoPostGIS(),
|
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
|
name: expected/hub_distance_lines.gml
|
||||||
type: vector
|
type: vector
|
||||||
|
|
||||||
- algorithm: qgis:hublines
|
- algorithm: native:hublines
|
||||||
name: Hub lines
|
name: Hub lines
|
||||||
params:
|
params:
|
||||||
HUBS:
|
HUBS:
|
||||||
@ -2460,6 +2460,46 @@ tests:
|
|||||||
name: expected/hub_lines.gml
|
name: expected/hub_lines.gml
|
||||||
type: vector
|
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
|
- algorithm: qgis:pointstopath
|
||||||
name: Points to path (non grouped)
|
name: Points to path (non grouped)
|
||||||
params:
|
params:
|
||||||
|
@ -91,6 +91,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
|
|||||||
addAlgorithm( new QgsMeanCoordinatesAlgorithm() );
|
addAlgorithm( new QgsMeanCoordinatesAlgorithm() );
|
||||||
addAlgorithm( new QgsRasterLayerUniqueValuesReportAlgorithm() );
|
addAlgorithm( new QgsRasterLayerUniqueValuesReportAlgorithm() );
|
||||||
addAlgorithm( new QgsJoinByAttributeAlgorithm() );
|
addAlgorithm( new QgsJoinByAttributeAlgorithm() );
|
||||||
|
addAlgorithm( new QgsJoinWithLinesAlgorithm() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsSaveSelectedFeatures::initAlgorithm( const QVariantMap & )
|
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
|
///@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
|
///@endcond PRIVATE
|
||||||
|
|
||||||
#endif // QGSNATIVEALGORITHMS_H
|
#endif // QGSNATIVEALGORITHMS_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user