mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
[processing] port delaunay triangulation algorithm to C++
This commit is contained in:
parent
d8243141cc
commit
451d1ba5ce
@ -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.
|
||||
|
||||
|
@ -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}
|
@ -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(),
|
||||
|
@ -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>
|
@ -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>
|
||||
|
68
python/plugins/processing/tests/testdata/expected/multipoint_delaunay.xsd
vendored
Normal file
68
python/plugins/processing/tests/testdata/expected/multipoint_delaunay.xsd
vendored
Normal 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>
|
@ -2547,7 +2547,7 @@ tests:
|
||||
fields:
|
||||
fid: skip
|
||||
|
||||
- algorithm: qgis:delaunaytriangulation
|
||||
- algorithm: native:delaunaytriangulation
|
||||
name: Delaunay triangulation (multipoint data)
|
||||
params:
|
||||
INPUT:
|
||||
|
@ -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
|
||||
|
172
src/analysis/processing/qgsalgorithmdelaunaytriangulation.cpp
Normal file
172
src/analysis/processing/qgsalgorithmdelaunaytriangulation.cpp
Normal 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 ¶meters, 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
|
56
src/analysis/processing/qgsalgorithmdelaunaytriangulation.h
Normal file
56
src/analysis/processing/qgsalgorithmdelaunaytriangulation.h
Normal 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 ¶meters,
|
||||
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
#endif // QGSALGORITHMDELAUNAYTRIANGULATION_H
|
@ -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() );
|
||||
|
Loading…
x
Reference in New Issue
Block a user