[processing] port delaunay triangulation algorithm to C++

This commit is contained in:
Alexander Bruy 2023-07-11 15:45:16 +03:00 committed by Martin Dobias
parent d8243141cc
commit 451d1ba5ce
11 changed files with 376 additions and 265 deletions

View File

@ -45,9 +45,6 @@ qgis:definecurrentprojection: >
The .prj and .qpj files associated with the Shapefile will be overwritten - or created if missing - to match the provided CRS.
qgis:delaunaytriangulation: >
This algorithm creates a polygon layer with the delaunay triangulation corresponding to a points layer.
qgis:distancematrix: >
This algorithm creates a table containing a distance matrix, with distances between all the points in a points layer.

View File

@ -1,157 +0,0 @@
"""
***************************************************************************
Delaunay.py
---------------------
Date : August 2012
Copyright : (C) 2012 by Victor Olaya
Email : volayaf at gmail 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. *
* *
***************************************************************************
"""
__author__ = 'Victor Olaya'
__date__ = 'August 2012'
__copyright__ = '(C) 2012, Victor Olaya'
import os
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsApplication,
QgsField,
QgsFeatureRequest,
QgsFeatureSink,
QgsFeature,
QgsGeometry,
QgsPointXY,
QgsWkbTypes,
QgsProcessing,
QgsFields,
QgsProcessingException,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from . import voronoi
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class Delaunay(QgisAlgorithm):
INPUT = 'INPUT'
OUTPUT = 'OUTPUT'
def icon(self):
return QgsApplication.getThemeIcon("/algorithms/mAlgorithmDelaunay.svg")
def svgIconPath(self):
return QgsApplication.iconPath("/algorithms/mAlgorithmDelaunay.svg")
def group(self):
return self.tr('Vector geometry')
def groupId(self):
return 'vectorgeometry'
def __init__(self):
super().__init__()
def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'), [QgsProcessing.TypeVectorPoint]))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Delaunay triangulation'), type=QgsProcessing.TypeVectorPolygon))
def name(self):
return 'delaunaytriangulation'
def displayName(self):
return self.tr('Delaunay triangulation')
def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context)
if source is None:
raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))
fields = QgsFields()
fields.append(QgsField('POINTA', QVariant.Double, '', 24, 15))
fields.append(QgsField('POINTB', QVariant.Double, '', 24, 15))
fields.append(QgsField('POINTC', QVariant.Double, '', 24, 15))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.Polygon, source.sourceCrs())
if sink is None:
raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))
pts = []
ptDict = {}
ptNdx = -1
c = voronoi.Context()
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0
for current, inFeat in enumerate(features):
if feedback.isCanceled():
break
geom = QgsGeometry(inFeat.geometry())
if geom.isNull():
continue
if geom.isMultipart():
points = geom.asMultiPoint()
else:
points = [geom.asPoint()]
for n, point in enumerate(points):
x = point.x()
y = point.y()
pts.append((x, y))
ptNdx += 1
ptDict[ptNdx] = (inFeat.id(), n)
feedback.setProgress(int(current * total))
if len(pts) < 3:
raise QgsProcessingException(
self.tr('Input file should contain at least 3 points. Choose '
'another file and try again.'))
uniqueSet = set(pts)
ids = [pts.index(item) for item in uniqueSet]
sl = voronoi.SiteList([voronoi.Site(*i) for i in uniqueSet])
c.triangulate = True
voronoi.voronoi(sl, c)
triangles = c.triangles
feat = QgsFeature()
total = 100.0 / len(triangles) if triangles else 1
for current, triangle in enumerate(triangles):
if feedback.isCanceled():
break
indices = list(triangle)
indices.append(indices[0])
polygon = []
attrs = []
for step, index in enumerate(indices):
fid, n = ptDict[ids[index]]
request = QgsFeatureRequest().setFilterFid(fid)
inFeat = next(source.getFeatures(request))
geom = QgsGeometry(inFeat.geometry())
if geom.isMultipart():
point = QgsPointXY(geom.asMultiPoint()[n])
else:
point = QgsPointXY(geom.asPoint())
polygon.append(point)
if step <= 3:
attrs.append(ids[index])
feat.setAttributes(attrs)
geometry = QgsGeometry().fromPolygonXY([polygon])
feat.setGeometry(geometry)
sink.addFeature(feat, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
return {self.OUTPUT: dest_id}

View File

@ -34,7 +34,6 @@ from .CheckValidity import CheckValidity
from .Climb import Climb
from .ConcaveHull import ConcaveHull
from .DefineProjection import DefineProjection
from .Delaunay import Delaunay
from .EliminateSelection import EliminateSelection
from .ExecuteSQL import ExecuteSQL
from .ExportGeometryInfo import ExportGeometryInfo
@ -97,7 +96,6 @@ class QgisAlgorithmProvider(QgsProcessingProvider):
Climb(),
ConcaveHull(),
DefineProjection(),
Delaunay(),
EliminateSelection(),
ExecuteSQL(),
ExportGeometryInfo(),

View File

@ -1,31 +0,0 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>multipoint_delaunay</Name>
<ElementPath>multipoint_delaunay</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>9</FeatureCount>
<ExtentXMin>0.00000</ExtentXMin>
<ExtentXMax>8.00000</ExtentXMax>
<ExtentYMin>-5.00000</ExtentYMin>
<ExtentYMax>3.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>POINTA</Name>
<ElementPath>POINTA</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>POINTB</Name>
<ElementPath>POINTB</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>POINTC</Name>
<ElementPath>POINTC</ElementPath>
<Type>Real</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>

View File

@ -1,86 +1,91 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
gml:id="aFeatureCollection"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ multipoint_delaunay.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:coord>
<gml:coord><gml:X>8</gml:X><gml:Y>3</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>
<gml:featureMember>
<ogr:multipoint_delaunay fid="multipoint_delaunay.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>7,-1 8,-1 0,-5 7,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>7.000000000000000</ogr:POINTA>
<ogr:POINTB>6.000000000000000</ogr:POINTB>
<ogr:POINTC>5.000000000000000</ogr:POINTC>
xmlns:gml="http://www.opengis.net/gml/3.2">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-5 0</gml:lowerCorner><gml:upperCorner>3 8</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:featureMember>
<ogr:multipoint_delaunay gml:id="multipoint_delaunay.0">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-5 0</gml:lowerCorner><gml:upperCorner>1 4</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326" gml:id="multipoint_delaunay.geom.0"><gml:exterior><gml:LinearRing><gml:posList>-1 0 -5 0 1 4 -1 0</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>7</ogr:POINTA>
<ogr:POINTB>5</ogr:POINTB>
<ogr:POINTC>3</ogr:POINTC>
</ogr:multipoint_delaunay>
</gml:featureMember>
<gml:featureMember>
<ogr:multipoint_delaunay fid="multipoint_delaunay.1">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>0,-5 4,1 7,-1 0,-5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>5.000000000000000</ogr:POINTA>
<ogr:POINTB>4.000000000000000</ogr:POINTB>
<ogr:POINTC>7.000000000000000</ogr:POINTC>
</ogr:featureMember>
<ogr:featureMember>
<ogr:multipoint_delaunay gml:id="multipoint_delaunay.1">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-1 0</gml:lowerCorner><gml:upperCorner>1 4</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326" gml:id="multipoint_delaunay.geom.1"><gml:exterior><gml:LinearRing><gml:posList>-1 0 1 4 1 1 -1 0</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>0</ogr:POINTA>
<ogr:POINTB>7</ogr:POINTB>
<ogr:POINTC>3</ogr:POINTC>
</ogr:multipoint_delaunay>
</gml:featureMember>
<gml:featureMember>
<ogr:multipoint_delaunay fid="multipoint_delaunay.2">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>0,-1 4,1 0,-5 0,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>8.000000000000000</ogr:POINTA>
<ogr:POINTB>4.000000000000000</ogr:POINTB>
<ogr:POINTC>5.000000000000000</ogr:POINTC>
</ogr:featureMember>
<ogr:featureMember>
<ogr:multipoint_delaunay gml:id="multipoint_delaunay.2">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>1 1</gml:lowerCorner><gml:upperCorner>2 4</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326" gml:id="multipoint_delaunay.geom.2"><gml:exterior><gml:LinearRing><gml:posList>1 1 1 4 2 2 1 1</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>0</ogr:POINTA>
<ogr:POINTB>3</ogr:POINTB>
<ogr:POINTC>7</ogr:POINTC>
</ogr:multipoint_delaunay>
</gml:featureMember>
<gml:featureMember>
<ogr:multipoint_delaunay fid="multipoint_delaunay.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,1 4,1 0,-1 1,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>0.000000000000000</ogr:POINTA>
<ogr:POINTB>4.000000000000000</ogr:POINTB>
<ogr:POINTC>8.000000000000000</ogr:POINTC>
</ogr:featureMember>
<ogr:featureMember>
<ogr:multipoint_delaunay gml:id="multipoint_delaunay.3">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>1 2</gml:lowerCorner><gml:upperCorner>3 4</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326" gml:id="multipoint_delaunay.geom.3"><gml:exterior><gml:LinearRing><gml:posList>2 2 1 4 3 3 2 2</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>0</ogr:POINTA>
<ogr:POINTB>3</ogr:POINTB>
<ogr:POINTC>7</ogr:POINTC>
</ogr:multipoint_delaunay>
</gml:featureMember>
<gml:featureMember>
<ogr:multipoint_delaunay fid="multipoint_delaunay.4">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,2 4,1 1,1 2,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>1.000000000000000</ogr:POINTA>
<ogr:POINTB>4.000000000000000</ogr:POINTB>
<ogr:POINTC>0.000000000000000</ogr:POINTC>
</ogr:featureMember>
<ogr:featureMember>
<ogr:multipoint_delaunay gml:id="multipoint_delaunay.4">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>1 3</gml:lowerCorner><gml:upperCorner>3 5</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326" gml:id="multipoint_delaunay.geom.4"><gml:exterior><gml:LinearRing><gml:posList>3 3 1 4 2 5 3 3</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>0</ogr:POINTA>
<ogr:POINTB>3</ogr:POINTB>
<ogr:POINTC>7</ogr:POINTC>
</ogr:multipoint_delaunay>
</gml:featureMember>
<gml:featureMember>
<ogr:multipoint_delaunay fid="multipoint_delaunay.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,2 7,-1 4,1 5,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>3.000000000000000</ogr:POINTA>
<ogr:POINTB>7.000000000000000</ogr:POINTB>
<ogr:POINTC>4.000000000000000</ogr:POINTC>
</ogr:featureMember>
<ogr:featureMember>
<ogr:multipoint_delaunay gml:id="multipoint_delaunay.5">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-1 4</gml:lowerCorner><gml:upperCorner>2 7</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326" gml:id="multipoint_delaunay.geom.5"><gml:exterior><gml:LinearRing><gml:posList>2 5 1 4 -1 7 2 5</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>7</ogr:POINTA>
<ogr:POINTB>3</ogr:POINTB>
<ogr:POINTC>5</ogr:POINTC>
</ogr:multipoint_delaunay>
</gml:featureMember>
<gml:featureMember>
<ogr:multipoint_delaunay fid="multipoint_delaunay.6">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,3 4,1 2,2 3,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>2.000000000000000</ogr:POINTA>
<ogr:POINTB>4.000000000000000</ogr:POINTB>
<ogr:POINTC>1.000000000000000</ogr:POINTC>
</ogr:featureMember>
<ogr:featureMember>
<ogr:multipoint_delaunay gml:id="multipoint_delaunay.6">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-1 5</gml:lowerCorner><gml:upperCorner>2 8</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326" gml:id="multipoint_delaunay.geom.6"><gml:exterior><gml:LinearRing><gml:posList>2 5 -1 7 -1 8 2 5</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>7</ogr:POINTA>
<ogr:POINTB>5</ogr:POINTB>
<ogr:POINTC>3</ogr:POINTC>
</ogr:multipoint_delaunay>
</gml:featureMember>
<gml:featureMember>
<ogr:multipoint_delaunay fid="multipoint_delaunay.7">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,3 5,2 4,1 3,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>2.000000000000000</ogr:POINTA>
<ogr:POINTB>3.000000000000000</ogr:POINTB>
<ogr:POINTC>4.000000000000000</ogr:POINTC>
</ogr:featureMember>
<ogr:featureMember>
<ogr:multipoint_delaunay gml:id="multipoint_delaunay.7">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-5 0</gml:lowerCorner><gml:upperCorner>-1 8</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326" gml:id="multipoint_delaunay.geom.7"><gml:exterior><gml:LinearRing><gml:posList>-1 8 -1 7 -5 0 -1 8</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>7</ogr:POINTA>
<ogr:POINTB>5</ogr:POINTB>
<ogr:POINTC>3</ogr:POINTC>
</ogr:multipoint_delaunay>
</gml:featureMember>
<gml:featureMember>
<ogr:multipoint_delaunay fid="multipoint_delaunay.8">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,2 8,-1 7,-1 5,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>3.000000000000000</ogr:POINTA>
<ogr:POINTB>6.000000000000000</ogr:POINTB>
<ogr:POINTC>7.000000000000000</ogr:POINTC>
</ogr:featureMember>
<ogr:featureMember>
<ogr:multipoint_delaunay gml:id="multipoint_delaunay.8">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-5 0</gml:lowerCorner><gml:upperCorner>1 7</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326" gml:id="multipoint_delaunay.geom.8"><gml:exterior><gml:LinearRing><gml:posList>-5 0 -1 7 1 4 -5 0</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></ogr:geometryProperty>
<ogr:POINTA>7</ogr:POINTA>
<ogr:POINTB>5</ogr:POINTB>
<ogr:POINTC>3</ogr:POINTC>
</ogr:multipoint_delaunay>
</gml:featureMember>
</ogr:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,68 @@
<?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/3.2"
xmlns:gmlsf="http://www.opengis.net/gmlsf/2.0"
elementFormDefault="qualified"
version="1.0">
<xs:annotation>
<xs:appinfo source="http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd">
<gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>
</xs:appinfo>
</xs:annotation>
<xs:import namespace="http://www.opengis.net/gml/3.2" schemaLocation="http://schemas.opengis.net/gml/3.2.1/gml.xsd"/>
<xs:import namespace="http://www.opengis.net/gmlsf/2.0" schemaLocation="http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:AbstractFeature"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="featureMember">
<xs:complexType>
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureMemberType">
<xs:sequence>
<xs:element ref="gml:AbstractFeature"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="multipoint_delaunay" type="ogr:multipoint_delaunay_Type" substitutionGroup="gml:AbstractFeature"/>
<xs:complexType name="multipoint_delaunay_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:SurfacePropertyType" nillable="true" minOccurs="0" maxOccurs="1"/> <!-- restricted to Polygon --><!-- srsName="urn:ogc:def:crs:EPSG::4326" -->
<xs:element name="POINTA" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="POINTB" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="POINTC" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -2547,7 +2547,7 @@ tests:
fields:
fid: skip
- algorithm: qgis:delaunaytriangulation
- algorithm: native:delaunaytriangulation
name: Delaunay triangulation (multipoint data)
params:
INPUT:

View File

@ -67,6 +67,7 @@ set(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmconvexhull.cpp
processing/qgsalgorithmcreatedirectory.cpp
processing/qgsalgorithmdbscanclustering.cpp
processing/qgsalgorithmdelaunaytriangulation.cpp
processing/qgsalgorithmdeleteduplicategeometries.cpp
processing/qgsalgorithmdensifygeometriesbycount.cpp
processing/qgsalgorithmdensifygeometriesbyinterval.cpp

View File

@ -0,0 +1,172 @@
/***************************************************************************
qgsalgorithmdelaunaytriangulation.cpp
---------------------
begin : July 2023
copyright : (C) 2023 by Alexander Bruy
email : alexander dot bruy at gmail 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. *
* *
***************************************************************************/
#include "qgsalgorithmdelaunaytriangulation.h"
#include "qgsspatialindex.h"
#include "qgsmultipoint.h"
#include "qgsattributes.h"
///@cond PRIVATE
QString QgsDelaunayTriangulationAlgorithm::name() const
{
return QStringLiteral( "delaunaytriangulation" );
}
QString QgsDelaunayTriangulationAlgorithm::displayName() const
{
return QObject::tr( "Delaunay triangulation" );
}
QStringList QgsDelaunayTriangulationAlgorithm::tags() const
{
return QObject::tr( "delaunay,triangulation,polygons,voronoi" ).split( ',' );
}
QString QgsDelaunayTriangulationAlgorithm::group() const
{
return QObject::tr( "Vector geometry" );
}
QString QgsDelaunayTriangulationAlgorithm::groupId() const
{
return QStringLiteral( "vectorgeometry" );
}
QString QgsDelaunayTriangulationAlgorithm::shortHelpString() const
{
return QObject::tr( "Creates a polygon layer with the Delaunay triangulation corresponding to a points layer." );
}
QgsDelaunayTriangulationAlgorithm *QgsDelaunayTriangulationAlgorithm::createInstance() const
{
return new QgsDelaunayTriangulationAlgorithm();
}
void QgsDelaunayTriangulationAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorPoint ) );
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), QgsProcessingParameterNumber::Double, 0, true, 0 ) );
addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "ADD_ATTRIBUTES" ), QObject::tr( "Add point IDs to output" ), true ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Delaunay triangulation" ), QgsProcessing::TypeVectorPolygon ) );
}
QVariantMap QgsDelaunayTriangulationAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
if ( source->featureCount() < 3 )
throw QgsProcessingException( QObject::tr( "Input layer should contain at least 3 points." ) );
const double tolerance = parameterAsDouble( parameters, QStringLiteral( "TOLERANCE" ), context );
const bool addAttributes = parameterAsBool( parameters, QStringLiteral( "ADD_ATTRIBUTES" ), context );
QgsFields fields;
if ( addAttributes )
{
fields.append( QgsField( QStringLiteral( "POINTA" ), QVariant::LongLong ) );
fields.append( QgsField( QStringLiteral( "POINTB" ), QVariant::LongLong ) );
fields.append( QgsField( QStringLiteral( "POINTC" ), QVariant::LongLong ) );
}
else
{
fields.append( QgsField( QStringLiteral( "id" ), QVariant::LongLong ) );
}
QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, Qgis::WkbType::Polygon, source->sourceCrs() ) );
if ( !sink )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
QgsFeatureIterator it = source->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QList< int >() ), QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
QgsGeometry allPoints;
long i = 0;
double step = source->featureCount() > 0 ? 50.0 / source->featureCount() : 1;
const QgsSpatialIndex index( it, [&]( const QgsFeature & f )->bool
{
i++;
if ( feedback->isCanceled() )
return false;
feedback->setProgress( i * step );
if ( !f.hasGeometry() )
return true;
const QgsAbstractGeometry *geom = f.geometry().constGet();
if ( QgsWkbTypes::isMultiType( geom->wkbType() ) )
{
const QgsMultiPoint mp( *qgsgeometry_cast< const QgsMultiPoint * >( geom ) );
for ( auto pit = mp.const_parts_begin(); pit != mp.const_parts_end(); ++pit )
{
allPoints.addPart( qgsgeometry_cast< QgsPoint * >( *pit )->clone(), Qgis::GeometryType::Point );
}
}
else
{
allPoints.addPart( qgsgeometry_cast< QgsPoint * >( geom )->clone(), Qgis::GeometryType::Point );
}
return true;
}, QgsSpatialIndex::FlagStoreFeatureGeometries );
QgsGeometry triangulation = allPoints.delaunayTriangulation( tolerance );
if ( !triangulation.isEmpty() )
{
QVector< QgsGeometry > collection = triangulation.asGeometryCollection();
for ( int i = 0; i < collection.length(); i++ )
{
if ( feedback->isCanceled() )
{
break;
}
QgsFeature f;
f.setFields( fields );
f.setGeometry( collection[i] );
if ( addAttributes )
{
const QList< QgsFeatureId > nearest = index.nearestNeighbor( collection[i], 3 );
QgsAttributes attrs;
for ( int j = 0; j < 3; j++ )
{
attrs << nearest.at( j );
}
f.setAttributes( attrs );
}
else
{
f.setAttributes( QgsAttributes() << i );
}
if ( !sink->addFeature( f, QgsFeatureSink::FastInsert ) )
throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
feedback->setProgress( 50 + i * step );
}
}
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
}
///@endcond

View File

@ -0,0 +1,56 @@
/***************************************************************************
qgsalgorithmdelaunaytriangulation.h
---------------------
begin : July 2023
copyright : (C) 2023 by Alexander Bruy
email : alexander dot bruy at gmail 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. *
* *
***************************************************************************/
#ifndef QGSALGORITHMDELAUNAYTRIANGULATION_H
#define QGSALGORITHMDELAUNAYTRIANGULATION_H
#define SIP_NO_FILE
#include "qgis_sip.h"
#include "qgsprocessingalgorithm.h"
#include "qgsapplication.h"
///@cond PRIVATE
/**
* Native delaunay triangulation algorithm.
*/
class QgsDelaunayTriangulationAlgorithm : public QgsProcessingAlgorithm
{
public:
QgsDelaunayTriangulationAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmDelaunay.svg" ) ); }
QString svgIconPath() const override { return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmDelaunay.svg" ) ); }
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QString shortHelpString() const override;
QgsDelaunayTriangulationAlgorithm *createInstance() const override SIP_FACTORY;
protected:
QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
};
///@endcond PRIVATE
#endif // QGSALGORITHMDELAUNAYTRIANGULATION_H

View File

@ -48,6 +48,7 @@
#include "qgsalgorithmconvexhull.h"
#include "qgsalgorithmcreatedirectory.h"
#include "qgsalgorithmdbscanclustering.h"
#include "qgsalgorithmdelaunaytriangulation.h"
#include "qgsalgorithmdeleteduplicategeometries.h"
#include "qgsalgorithmdensifygeometriesbycount.h"
#include "qgsalgorithmdensifygeometriesbyinterval.h"
@ -312,6 +313,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsConvexHullAlgorithm() );
addAlgorithm( new QgsCreateDirectoryAlgorithm() );
addAlgorithm( new QgsDbscanClusteringAlgorithm() );
addAlgorithm( new QgsDelaunayTriangulationAlgorithm() );
addAlgorithm( new QgsDeleteDuplicateGeometriesAlgorithm() );
addAlgorithm( new QgsDetectVectorChangesAlgorithm() );
addAlgorithm( new QgsDifferenceAlgorithm() );