mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-27 00:03:38 -04:00
[FEATURE][Processing] Minimal enclosing circle
This commit is contained in:
parent
f51244c98b
commit
e30f7044c9
python
core/geometry
plugins/processing
src/core/geometry
tests/src/core
@ -112,6 +112,19 @@ class QgsCircle : QgsEllipse
|
|||||||
:rtype: QgsCircle
|
:rtype: QgsCircle
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
static QgsCircle minimalCircleFrom3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon = 1E-8 );
|
||||||
|
%Docstring
|
||||||
|
Constructs the smallest circle from 3 points.
|
||||||
|
Z and m values are dropped for the center point.
|
||||||
|
The azimuth always takes the default value.
|
||||||
|
If the points are colinear an empty circle is returned.
|
||||||
|
\param pt1 First point.
|
||||||
|
\param pt2 Second point.
|
||||||
|
\param pt3 Third point.
|
||||||
|
\param epsilon Value used to compare point.
|
||||||
|
:rtype: QgsCircle
|
||||||
|
%End
|
||||||
|
|
||||||
virtual double area() const;
|
virtual double area() const;
|
||||||
|
|
||||||
virtual double perimeter() const;
|
virtual double perimeter() const;
|
||||||
@ -159,6 +172,11 @@ Set the radius of the circle
|
|||||||
:rtype: QgsCircularString
|
:rtype: QgsCircularString
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
bool contains( const QgsPoint &point, double epsilon = 1E-8 ) const;
|
||||||
|
%Docstring
|
||||||
|
Returns true if the circle contains the ``point``.
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
virtual QgsRectangle boundingBox() const;
|
virtual QgsRectangle boundingBox() const;
|
||||||
|
|
||||||
|
@ -577,6 +577,16 @@ Returns true if WKB of the geometry is of WKBMulti* type
|
|||||||
:rtype: QgsGeometry
|
:rtype: QgsGeometry
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
QgsGeometry minimalEnclosingCircle( QgsPointXY ¢er /Out/, double &radius /Out/, unsigned int segments = 36 ) const;
|
||||||
|
%Docstring
|
||||||
|
Returns the minimal enclosing circle for the geometry.
|
||||||
|
\param center Center of the minimal enclosing circle returneds
|
||||||
|
\param radius Radius of the minimal enclosing circle returned
|
||||||
|
.. seealso:: QgsEllipse.toPolygon()
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
:rtype: QgsGeometry
|
||||||
|
%End
|
||||||
|
|
||||||
QgsGeometry orthogonalize( double tolerance = 1.0E-8, int maxIterations = 1000, double angleThreshold = 15.0 ) const;
|
QgsGeometry orthogonalize( double tolerance = 1.0E-8, int maxIterations = 1000, double angleThreshold = 15.0 ) const;
|
||||||
%Docstring
|
%Docstring
|
||||||
Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries
|
Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries
|
||||||
|
@ -354,6 +354,11 @@ qgis:orientedminimumboundingbox: >
|
|||||||
|
|
||||||
As an alternative, the output layer can contain not just a single rectangle, but one for each input feature, representing the minimum rectangle that covers each of them.
|
As an alternative, the output layer can contain not just a single rectangle, but one for each input feature, representing the minimum rectangle that covers each of them.
|
||||||
|
|
||||||
|
qgis:minimalenclosingcircle: >
|
||||||
|
This algorithm takes a vector layer and generate a new one with the minimum enclosing circle that covers all the input features.
|
||||||
|
|
||||||
|
As an alternative, the output layer can contain not just a single circle, but one for each input feature, representing the minimum enclosing circle that covers each of them.
|
||||||
|
|
||||||
qgis:orthogonalize: >
|
qgis:orthogonalize: >
|
||||||
This algorithm takes a line or polygon layer and attempts to orthogonalize all the geometries in the layer. This process shifts the nodes in the geometries to try to make every angle in the geometry either a right angle or a straight line.
|
This algorithm takes a line or polygon layer and attempts to orthogonalize all the geometries in the layer. This process shifts the nodes in the geometries to try to make every angle in the geometry either a right angle or a straight line.
|
||||||
|
|
||||||
|
142
python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py
Normal file
142
python/plugins/processing/algs/qgis/MinimalEnclosingCircle.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
***************************************************************************
|
||||||
|
MinimalEnclosingCircle.py
|
||||||
|
---------------------
|
||||||
|
Date : September 2017
|
||||||
|
Copyright : (C) 2017, Loïc BARTOLETTI
|
||||||
|
Email : lbartoletti at tuxfamily dot org
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* 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__ = 'Loïc BARTOLETTI'
|
||||||
|
__date__ = 'September 2017'
|
||||||
|
__copyright__ = '(C) 2017, Loïc BARTOLETTI'
|
||||||
|
|
||||||
|
# This will get replaced with a git SHA1 when you do a git archive
|
||||||
|
|
||||||
|
__revision__ = '$Format:%H$'
|
||||||
|
|
||||||
|
from qgis.PyQt.QtCore import QVariant
|
||||||
|
from qgis.core import (QgsField,
|
||||||
|
QgsFields,
|
||||||
|
QgsFeatureSink,
|
||||||
|
QgsGeometry,
|
||||||
|
QgsFeature,
|
||||||
|
QgsWkbTypes,
|
||||||
|
QgsFeatureRequest,
|
||||||
|
QgsProcessing,
|
||||||
|
QgsProcessingParameterFeatureSource,
|
||||||
|
QgsProcessingParameterBoolean,
|
||||||
|
QgsProcessingParameterFeatureSink,
|
||||||
|
QgsProcessingException)
|
||||||
|
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
|
||||||
|
|
||||||
|
|
||||||
|
class MinimalEnclosingCircle(QgisAlgorithm):
|
||||||
|
|
||||||
|
INPUT = 'INPUT'
|
||||||
|
BY_FEATURE = 'BY_FEATURE'
|
||||||
|
|
||||||
|
OUTPUT = 'OUTPUT'
|
||||||
|
|
||||||
|
def group(self):
|
||||||
|
return self.tr('Vector general')
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def initAlgorithm(self, config=None):
|
||||||
|
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
|
||||||
|
self.tr('Input layer'), [QgsProcessing.TypeVectorAnyGeometry]))
|
||||||
|
self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE,
|
||||||
|
self.tr('Calculate bounds for each feature separately'), defaultValue=True))
|
||||||
|
|
||||||
|
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Enclosing circles'), QgsProcessing.TypeVectorPolygon))
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return 'minimalenclosingcircle'
|
||||||
|
|
||||||
|
def displayName(self):
|
||||||
|
return self.tr('Minimal enclosing circle')
|
||||||
|
|
||||||
|
def processAlgorithm(self, parameters, context, feedback):
|
||||||
|
source = self.parameterAsSource(parameters, self.INPUT, context)
|
||||||
|
by_feature = self.parameterAsBool(parameters, self.BY_FEATURE, context)
|
||||||
|
|
||||||
|
if not by_feature and QgsWkbTypes.geometryType(source.wkbType()) == QgsWkbTypes.PointGeometry and source.featureCount() <= 2:
|
||||||
|
raise QgsProcessingException(self.tr("Can't calculate a minimal enclosing circle for each point, it's a point. The number of points must be greater than 2"))
|
||||||
|
|
||||||
|
if by_feature:
|
||||||
|
fields = source.fields()
|
||||||
|
else:
|
||||||
|
fields = QgsFields()
|
||||||
|
fields.append(QgsField('center_x', QVariant.Double))
|
||||||
|
fields.append(QgsField('center_y', QVariant.Double))
|
||||||
|
fields.append(QgsField('radius', QVariant.Double))
|
||||||
|
|
||||||
|
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
|
||||||
|
fields, QgsWkbTypes.Polygon, source.sourceCrs())
|
||||||
|
|
||||||
|
if by_feature:
|
||||||
|
self.featureMec(source, context, sink, feedback)
|
||||||
|
else:
|
||||||
|
self.layerMec(source, context, sink, feedback)
|
||||||
|
|
||||||
|
return {self.OUTPUT: dest_id}
|
||||||
|
|
||||||
|
def layerMec(self, source, context, sink, feedback):
|
||||||
|
req = QgsFeatureRequest().setSubsetOfAttributes([])
|
||||||
|
features = source.getFeatures(req)
|
||||||
|
total = 100.0 / source.featureCount() if source.featureCount() else 0
|
||||||
|
newgeometry = QgsGeometry()
|
||||||
|
first = True
|
||||||
|
geometries = []
|
||||||
|
for current, inFeat in enumerate(features):
|
||||||
|
if feedback.isCanceled():
|
||||||
|
break
|
||||||
|
|
||||||
|
if inFeat.hasGeometry():
|
||||||
|
geometries.append(inFeat.geometry())
|
||||||
|
feedback.setProgress(int(current * total))
|
||||||
|
|
||||||
|
newgeometry = QgsGeometry.unaryUnion(geometries)
|
||||||
|
geometry, center, radius = newgeometry.minimalEnclosingCircle()
|
||||||
|
|
||||||
|
if geometry:
|
||||||
|
outFeat = QgsFeature()
|
||||||
|
|
||||||
|
outFeat.setGeometry(geometry)
|
||||||
|
outFeat.setAttributes([center.x(),
|
||||||
|
center.y(),
|
||||||
|
radius])
|
||||||
|
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
||||||
|
|
||||||
|
def featureMec(self, source, context, sink, feedback):
|
||||||
|
features = source.getFeatures()
|
||||||
|
total = 100.0 / source.featureCount() if source.featureCount() else 0
|
||||||
|
outFeat = QgsFeature()
|
||||||
|
for current, inFeat in enumerate(features):
|
||||||
|
if feedback.isCanceled():
|
||||||
|
break
|
||||||
|
|
||||||
|
geometry, center, radius = inFeat.geometry().minimalEnclosingCircle()
|
||||||
|
if geometry:
|
||||||
|
outFeat.setGeometry(geometry)
|
||||||
|
attrs = inFeat.attributes()
|
||||||
|
attrs.extend([center.x(),
|
||||||
|
center.y(),
|
||||||
|
radius])
|
||||||
|
outFeat.setAttributes(attrs)
|
||||||
|
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
||||||
|
else:
|
||||||
|
feedback.pushInfo(self.tr("Can't calculate a minimal enclosing circle for feature {0}.").format(inFeat.id()))
|
||||||
|
feedback.setProgress(int(current * total))
|
@ -100,6 +100,7 @@ from .LinesToPolygons import LinesToPolygons
|
|||||||
from .MeanCoords import MeanCoords
|
from .MeanCoords import MeanCoords
|
||||||
from .Merge import Merge
|
from .Merge import Merge
|
||||||
from .MergeLines import MergeLines
|
from .MergeLines import MergeLines
|
||||||
|
from .MinimalEnclosingCircle import MinimalEnclosingCircle
|
||||||
from .NearestNeighbourAnalysis import NearestNeighbourAnalysis
|
from .NearestNeighbourAnalysis import NearestNeighbourAnalysis
|
||||||
from .OffsetLine import OffsetLine
|
from .OffsetLine import OffsetLine
|
||||||
from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox
|
from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox
|
||||||
@ -253,6 +254,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
|
|||||||
MeanCoords(),
|
MeanCoords(),
|
||||||
Merge(),
|
Merge(),
|
||||||
MergeLines(),
|
MergeLines(),
|
||||||
|
MinimalEnclosingCircle(),
|
||||||
NearestNeighbourAnalysis(),
|
NearestNeighbourAnalysis(),
|
||||||
OffsetLine(),
|
OffsetLine(),
|
||||||
OrientedMinimumBoundingBox(),
|
OrientedMinimumBoundingBox(),
|
||||||
|
31
python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gfs
vendored
Normal file
31
python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gfs
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<GMLFeatureClassList>
|
||||||
|
<GMLFeatureClass>
|
||||||
|
<Name>enclosing_circles_all</Name>
|
||||||
|
<ElementPath>enclosing_circles_all</ElementPath>
|
||||||
|
<!--POLYGON-->
|
||||||
|
<GeometryType>3</GeometryType>
|
||||||
|
<SRSName>EPSG:4326</SRSName>
|
||||||
|
<DatasetSpecificInfo>
|
||||||
|
<FeatureCount>1</FeatureCount>
|
||||||
|
<ExtentXMin>-1.50891</ExtentXMin>
|
||||||
|
<ExtentXMax>10.30638</ExtentXMax>
|
||||||
|
<ExtentYMin>-5.30638</ExtentYMin>
|
||||||
|
<ExtentYMax>6.50891</ExtentYMax>
|
||||||
|
</DatasetSpecificInfo>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>center_x</Name>
|
||||||
|
<ElementPath>center_x</ElementPath>
|
||||||
|
<Type>Real</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>center_y</Name>
|
||||||
|
<ElementPath>center_y</ElementPath>
|
||||||
|
<Type>Real</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>radius</Name>
|
||||||
|
<ElementPath>radius</ElementPath>
|
||||||
|
<Type>Real</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
</GMLFeatureClass>
|
||||||
|
</GMLFeatureClassList>
|
22
python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gml
vendored
Normal file
22
python/plugins/processing/tests/testdata/expected/enclosing_circles_all.gml
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<ogr:FeatureCollection
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation=""
|
||||||
|
xmlns:ogr="http://ogr.maptools.org/"
|
||||||
|
xmlns:gml="http://www.opengis.net/gml">
|
||||||
|
<gml:boundedBy>
|
||||||
|
<gml:Box>
|
||||||
|
<gml:coord><gml:X>-1.508909716010908</gml:X><gml:Y>-5.306378070441288</gml:Y></gml:coord>
|
||||||
|
<gml:coord><gml:X>10.30637807044129</gml:X><gml:Y>6.508909716010908</gml:Y></gml:coord>
|
||||||
|
</gml:Box>
|
||||||
|
</gml:boundedBy>
|
||||||
|
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:enclosing_circles_all fid="enclosing_circles_all.0">
|
||||||
|
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>4.39873417721519,6.50890971601091 5.42458577357907,6.4191593308691 6.41926738829339,6.15263519548031 7.35255612382824,5.71743551083061 8.19609447422128,5.12678359911643 8.92425195354681,4.39862611979089 9.51490386526099,3.55508776939786 9.95010354991069,2.62179903386301 10.2166276852995,1.62711741914869 10.3063780704413,0.601265822784808 10.2166276852995,-0.424585773579072 9.95010354991069,-1.41926738829339 9.51490386526099,-2.35255612382824 8.92425195354681,-3.19609447422128 8.19609447422127,-3.92425195354681 7.35255612382824,-4.514903865261 6.41926738829339,-4.95010354991069 5.42458577357907,-5.21662768529948 4.39873417721519,-5.30637807044129 3.37288258085131,-5.21662768529948 2.37820096613699,-4.95010354991069 1.44491223060214,-4.51490386526099 0.601373880209104,-3.92425195354681 -0.126783599116428,-3.19609447422127 -0.717435510830614,-2.35255612382824 -1.15263519548031,-1.41926738829339 -1.4191593308691,-0.42458577357907 -1.50890971601091,0.60126582278481 -1.4191593308691,1.62711741914869 -1.15263519548031,2.62179903386301 -0.717435510830614,3.55508776939786 -0.126783599116427,4.3986261197909 0.601373880209104,5.12678359911643 1.44491223060214,5.71743551083061 2.37820096613699,6.15263519548031 3.37288258085131,6.4191593308691 4.39873417721519,6.50890971601091</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||||
|
<ogr:center_x>4.39873417721519</ogr:center_x>
|
||||||
|
<ogr:center_y>0.60126582278481</ogr:center_y>
|
||||||
|
<ogr:radius>5.9076438932261</ogr:radius>
|
||||||
|
</ogr:enclosing_circles_all>
|
||||||
|
</gml:featureMember>
|
||||||
|
</ogr:FeatureCollection>
|
47
python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs
vendored
Normal file
47
python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gfs
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<GMLFeatureClassList>
|
||||||
|
<GMLFeatureClass>
|
||||||
|
<Name>enclosing_circles_each</Name>
|
||||||
|
<ElementPath>enclosing_circles_each</ElementPath>
|
||||||
|
<!--POLYGON-->
|
||||||
|
<GeometryType>3</GeometryType>
|
||||||
|
<SRSName>EPSG:4326</SRSName>
|
||||||
|
<DatasetSpecificInfo>
|
||||||
|
<FeatureCount>6</FeatureCount>
|
||||||
|
<ExtentXMin>-1.81766</ExtentXMin>
|
||||||
|
<ExtentXMax>10.59982</ExtentXMax>
|
||||||
|
<ExtentYMin>-3.43247</ExtentYMin>
|
||||||
|
<ExtentYMax>6.32190</ExtentYMax>
|
||||||
|
</DatasetSpecificInfo>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>intval</Name>
|
||||||
|
<ElementPath>intval</ElementPath>
|
||||||
|
<Type>Integer</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>floatval</Name>
|
||||||
|
<ElementPath>floatval</ElementPath>
|
||||||
|
<Type>Real</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>center_x</Name>
|
||||||
|
<ElementPath>center_x</ElementPath>
|
||||||
|
<Type>Real</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>center_y</Name>
|
||||||
|
<ElementPath>center_y</ElementPath>
|
||||||
|
<Type>Real</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>radius</Name>
|
||||||
|
<ElementPath>radius</ElementPath>
|
||||||
|
<Type>Real</Type>
|
||||||
|
</PropertyDefn>
|
||||||
|
<PropertyDefn>
|
||||||
|
<Name>name</Name>
|
||||||
|
<ElementPath>name</ElementPath>
|
||||||
|
<Type>String</Type>
|
||||||
|
<Width>5</Width>
|
||||||
|
</PropertyDefn>
|
||||||
|
</GMLFeatureClass>
|
||||||
|
</GMLFeatureClassList>
|
77
python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml
vendored
Normal file
77
python/plugins/processing/tests/testdata/expected/enclosing_circles_each.gml
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<ogr:FeatureCollection
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation=""
|
||||||
|
xmlns:ogr="http://ogr.maptools.org/"
|
||||||
|
xmlns:gml="http://www.opengis.net/gml">
|
||||||
|
<gml:boundedBy>
|
||||||
|
<gml:Box>
|
||||||
|
<gml:coord><gml:X>-1.817664105611035</gml:X><gml:Y>-3.43247280352443</gml:Y></gml:coord>
|
||||||
|
<gml:coord><gml:X>10.59981974229994</gml:X><gml:Y>6.321903675995209</gml:Y></gml:coord>
|
||||||
|
</gml:Box>
|
||||||
|
</gml:boundedBy>
|
||||||
|
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:enclosing_circles_each fid="polys.4">
|
||||||
|
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3.8,2.2 4.26819673362059,2.35151315326536 4.75559048978224,2.4194229717016 5.24737205583712,2.40166604983954 5.72859889775413,2.29878192276454 6.18464918145415,2.11389667261588 6.60166604983954,1.85262794416288 6.96697865638513,1.52291425551124 7.26948716239812,1.13477379024745 7.5,0.7 7.65151315326536,0.23180326637941 7.7194229717016,-0.255590489782239 7.70166604983954,-0.747372055837116 7.59878192276454,-1.22859889775413 7.41389667261588,-1.68464918145415 7.15262794416288,-2.10166604983954 6.82291425551124,-2.46697865638513 6.43477379024745,-2.76948716239812 6.0,-3.0 5.53180326637941,-3.15151315326536 5.04440951021776,-3.2194229717016 4.55262794416288,-3.20166604983954 4.07140110224587,-3.09878192276454 3.61535081854585,-2.91389667261588 3.19833395016046,-2.65262794416288 2.83302134361487,-2.32291425551124 2.53051283760188,-1.93477379024745 2.3,-1.5 2.14848684673464,-1.03180326637941 2.0805770282984,-0.544409510217762 2.09833395016046,-0.052627944162882 2.20121807723546,0.428598897754127 2.38610332738412,0.884649181454149 2.64737205583712,1.30166604983954 2.97708574448876,1.66697865638513 3.36522620975255,1.96948716239812 3.8,2.2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||||
|
<ogr:intval>120</ogr:intval>
|
||||||
|
<ogr:floatval>-100291.43213</ogr:floatval>
|
||||||
|
<ogr:center_x>4.9</ogr:center_x>
|
||||||
|
<ogr:center_y>-0.4</ogr:center_y>
|
||||||
|
<ogr:radius>2.82311884269862</ogr:radius>
|
||||||
|
</ogr:enclosing_circles_each>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:enclosing_circles_each fid="polys.1">
|
||||||
|
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5.0,5.08319489631876 5.17420296559052,5.06795411167699 5.34311286222252,5.02269484128082 5.50159744815938,4.94879226515894 5.64484124945447,4.8484918756903 5.7684918756903,4.72484124945447 5.86879226515894,4.58159744815938 5.94269484128082,4.42311286222252 5.98795411167699,4.25420296559052 6.00319489631876,4.08 5.98795411167699,3.90579703440948 5.94269484128082,3.73688713777748 5.86879226515894,3.57840255184062 5.7684918756903,3.43515875054553 5.64484124945447,3.3115081243097 5.50159744815938,3.21120773484106 5.34311286222252,3.13730515871918 5.17420296559052,3.09204588832301 5.0,3.07680510368124 4.82579703440948,3.09204588832301 4.65688713777748,3.13730515871918 4.49840255184062,3.21120773484106 4.35515875054553,3.3115081243097 4.2315081243097,3.43515875054553 4.13120773484106,3.57840255184062 4.05730515871918,3.73688713777748 4.01204588832301,3.90579703440948 3.99680510368124,4.08 4.01204588832301,4.25420296559052 4.05730515871918,4.42311286222252 4.13120773484106,4.58159744815938 4.2315081243097,4.72484124945447 4.35515875054553,4.8484918756903 4.49840255184062,4.94879226515894 4.65688713777748,5.02269484128082 4.82579703440948,5.06795411167699 5.0,5.08319489631876</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||||
|
<ogr:intval>-33</ogr:intval>
|
||||||
|
<ogr:floatval>0</ogr:floatval>
|
||||||
|
<ogr:name>Aaaaa</ogr:name>
|
||||||
|
<ogr:center_x>5</ogr:center_x>
|
||||||
|
<ogr:center_y>4.08</ogr:center_y>
|
||||||
|
<ogr:radius>1.00319489631876</ogr:radius>
|
||||||
|
</ogr:enclosing_circles_each>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:enclosing_circles_each fid="polys.0">
|
||||||
|
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3.0,3.0 3.31691186135828,2.62231915069056 3.56342552822315,2.19534495492048 3.73205080756888,1.73205080756888 3.81766410561104,1.24651366686488 3.81766410561104,0.753486333135122 3.73205080756888,0.267949192431123 3.56342552822315,-0.195344954920481 3.31691186135828,-0.622319150690556 3.0,-1.0 2.62231915069056,-1.31691186135828 2.19534495492048,-1.56342552822315 1.73205080756888,-1.73205080756888 1.24651366686488,-1.81766410561103 0.753486333135123,-1.81766410561103 0.267949192431122,-1.73205080756888 -0.195344954920479,-1.56342552822315 -0.622319150690555,-1.31691186135828 -1.0,-1.0 -1.31691186135828,-0.622319150690556 -1.56342552822315,-0.195344954920479 -1.73205080756888,0.267949192431122 -1.81766410561103,0.753486333135123 -1.81766410561103,1.24651366686488 -1.73205080756888,1.73205080756888 -1.56342552822316,2.19534495492048 -1.31691186135828,2.62231915069056 -1.0,3.0 -0.622319150690556,3.31691186135828 -0.195344954920481,3.56342552822315 0.267949192431124,3.73205080756888 0.753486333135123,3.81766410561104 1.24651366686488,3.81766410561104 1.73205080756888,3.73205080756888 2.19534495492048,3.56342552822316 2.62231915069056,3.31691186135828 3.0,3.0</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||||
|
<ogr:intval>33</ogr:intval>
|
||||||
|
<ogr:floatval>44.12346</ogr:floatval>
|
||||||
|
<ogr:name>aaaaa</ogr:name>
|
||||||
|
<ogr:center_x>1</ogr:center_x>
|
||||||
|
<ogr:center_y>1</ogr:center_y>
|
||||||
|
<ogr:radius>2.82842712474619</ogr:radius>
|
||||||
|
</ogr:enclosing_circles_each>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:enclosing_circles_each fid="polys.3">
|
||||||
|
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>7.8734693877551,2.02022790556525 8.3468951585034,1.97880851760375 8.80593612677252,1.85580886086324 9.23664456502752,1.65496621767295 9.62593361532103,1.38238309011494 9.96197492684963,1.04634177858634 10.2345580544076,0.657052728292829 10.4354006975979,0.226344290037822 10.5584003543384,-0.232696678231291 10.5998197422999,-0.706122448979591 10.5584003543384,-1.17954821972789 10.4354006975979,-1.638589187997 10.2345580544076,-2.06929762625201 9.96197492684963,-2.45858667654552 9.62593361532103,-2.79462798807412 9.23664456502752,-3.06721111563213 8.80593612677252,-3.26805375882242 8.3468951585034,-3.39105341556293 7.8734693877551,-3.43247280352443 7.4000436170068,-3.39105341556293 6.94100264873769,-3.26805375882242 6.51029421048268,-3.06721111563213 6.12100516018918,-2.79462798807412 5.78496384866057,-2.45858667654552 5.51238072110256,-2.06929762625201 5.31153807791227,-1.63858918799701 5.18853842117176,-1.17954821972789 5.14711903321026,-0.70612244897959 5.18853842117176,-0.23269667823129 5.31153807791227,0.226344290037823 5.51238072110256,0.65705272829283 5.78496384866057,1.04634177858634 6.12100516018918,1.38238309011494 6.51029421048268,1.65496621767295 6.94100264873769,1.85580886086324 7.4000436170068,1.97880851760375 7.8734693877551,2.02022790556525</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||||
|
<ogr:intval>0</ogr:intval>
|
||||||
|
<ogr:name>ASDF</ogr:name>
|
||||||
|
<ogr:center_x>7.8734693877551</ogr:center_x>
|
||||||
|
<ogr:center_y>-0.706122448979591</ogr:center_y>
|
||||||
|
<ogr:radius>2.72635035454484</ogr:radius>
|
||||||
|
</ogr:enclosing_circles_each>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:enclosing_circles_each fid="polys.2">
|
||||||
|
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,6 3.0935543337087,5.86933092744047 3.16299692054554,5.72440147214358 3.20621778264911,5.56961524227066 3.22190367599521,5.40967533909081 3.20957799265196,5.24944145562864 3.16961524227066,5.09378221735089 3.10322967279951,4.94742725144526 3.01243837617418,4.81482347949161 2.9,4.7 2.76933092744047,4.6064456662913 2.62440147214358,4.53700307945446 2.46961524227066,4.49378221735089 2.30967533909081,4.47809632400479 2.14944145562864,4.49042200734804 1.99378221735089,4.53038475772934 1.84742725144526,4.59677032720049 1.71482347949161,4.68756162382582 1.6,4.8 1.5064456662913,4.93066907255953 1.43700307945446,5.07559852785642 1.39378221735089,5.23038475772934 1.37809632400479,5.39032466090919 1.39042200734804,5.55055854437136 1.43038475772934,5.70621778264911 1.49677032720049,5.85257274855473 1.58756162382582,5.98517652050839 1.7,6.1 1.83066907255953,6.1935543337087 1.97559852785642,6.26299692054554 2.13038475772934,6.30621778264911 2.29032466090919,6.32190367599521 2.45055854437136,6.30957799265196 2.60621778264911,6.26961524227066 2.75257274855473,6.20322967279951 2.88517652050839,6.11243837617418 3,6</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||||
|
<ogr:floatval>0.123</ogr:floatval>
|
||||||
|
<ogr:name>bbaaa</ogr:name>
|
||||||
|
<ogr:center_x>2.3</ogr:center_x>
|
||||||
|
<ogr:center_y>5.4</ogr:center_y>
|
||||||
|
<ogr:radius>0.921954445729289</ogr:radius>
|
||||||
|
</ogr:enclosing_circles_each>
|
||||||
|
</gml:featureMember>
|
||||||
|
<gml:featureMember>
|
||||||
|
<ogr:enclosing_circles_each fid="polys.5">
|
||||||
|
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3.8,2.2 4.26819673362059,2.35151315326536 4.75559048978224,2.4194229717016 5.24737205583712,2.40166604983954 5.72859889775413,2.29878192276454 6.18464918145415,2.11389667261588 6.60166604983954,1.85262794416288 6.96697865638513,1.52291425551124 7.26948716239812,1.13477379024745 7.5,0.7 7.65151315326536,0.23180326637941 7.7194229717016,-0.255590489782239 7.70166604983954,-0.747372055837116 7.59878192276454,-1.22859889775413 7.41389667261588,-1.68464918145415 7.15262794416288,-2.10166604983954 6.82291425551124,-2.46697865638513 6.43477379024745,-2.76948716239812 6.0,-3.0 5.53180326637941,-3.15151315326536 5.04440951021776,-3.2194229717016 4.55262794416288,-3.20166604983954 4.07140110224587,-3.09878192276454 3.61535081854585,-2.91389667261588 3.19833395016046,-2.65262794416288 2.83302134361487,-2.32291425551124 2.53051283760188,-1.93477379024745 2.3,-1.5 2.14848684673464,-1.03180326637941 2.0805770282984,-0.544409510217762 2.09833395016046,-0.052627944162882 2.20121807723546,0.428598897754127 2.38610332738412,0.884649181454149 2.64737205583712,1.30166604983954 2.97708574448876,1.66697865638513 3.36522620975255,1.96948716239812 3.8,2.2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||||
|
<ogr:intval>2</ogr:intval>
|
||||||
|
<ogr:floatval>3.33</ogr:floatval>
|
||||||
|
<ogr:name>elim</ogr:name>
|
||||||
|
<ogr:center_x>4.9</ogr:center_x>
|
||||||
|
<ogr:center_y>-0.4</ogr:center_y>
|
||||||
|
<ogr:radius>2.82311884269862</ogr:radius>
|
||||||
|
</ogr:enclosing_circles_each>
|
||||||
|
</gml:featureMember>
|
||||||
|
</ogr:FeatureCollection>
|
@ -3239,3 +3239,33 @@ tests:
|
|||||||
OUTPUT:
|
OUTPUT:
|
||||||
name: expected/raster_extent.gml
|
name: expected/raster_extent.gml
|
||||||
type: vector
|
type: vector
|
||||||
|
|
||||||
|
- algorithm: qgis:minimalenclosingcircle
|
||||||
|
name: Minimal enclosing circle each features
|
||||||
|
params:
|
||||||
|
BY_FEATURE: true
|
||||||
|
INPUT:
|
||||||
|
name: custom/oriented_bbox.gml
|
||||||
|
type: vector
|
||||||
|
results:
|
||||||
|
OUTPUT:
|
||||||
|
name: expected/enclosing_circles_each.gml
|
||||||
|
type: vector
|
||||||
|
compare:
|
||||||
|
geometry:
|
||||||
|
precision: 7
|
||||||
|
|
||||||
|
- algorithm: qgis:minimalenclosingcircle
|
||||||
|
name: Minimal enclosing circle all features
|
||||||
|
params:
|
||||||
|
BY_FEATURE: false
|
||||||
|
INPUT:
|
||||||
|
name: custom/oriented_bbox.gml
|
||||||
|
type: vector
|
||||||
|
results:
|
||||||
|
OUTPUT:
|
||||||
|
name: expected/enclosing_circles_all.gml
|
||||||
|
type: vector
|
||||||
|
compare:
|
||||||
|
geometry:
|
||||||
|
precision: 7
|
||||||
|
@ -38,7 +38,7 @@ QgsCircle QgsCircle::from2Points( const QgsPoint &pt1, const QgsPoint &pt2 )
|
|||||||
{
|
{
|
||||||
QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 );
|
QgsPoint center = QgsGeometryUtils::midpoint( pt1, pt2 );
|
||||||
double azimuth = QgsGeometryUtils::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() ) * 180.0 / M_PI;
|
double azimuth = QgsGeometryUtils::lineAngle( pt1.x(), pt1.y(), pt2.x(), pt2.y() ) * 180.0 / M_PI;
|
||||||
double radius = pt1.distance( pt2 );
|
double radius = pt1.distance( pt2 ) / 2.0;
|
||||||
|
|
||||||
return QgsCircle( center, radius, azimuth );
|
return QgsCircle( center, radius, azimuth );
|
||||||
}
|
}
|
||||||
@ -189,6 +189,22 @@ QgsCircle QgsCircle::from3Tangents( const QgsPoint &pt1_tg1, const QgsPoint &pt2
|
|||||||
return QgsTriangle( p1, p2, p3 ).inscribedCircle();
|
return QgsTriangle( p1, p2, p3 ).inscribedCircle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QgsCircle QgsCircle::minimalCircleFrom3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon )
|
||||||
|
{
|
||||||
|
double l1 = pt2.distance( pt3 );
|
||||||
|
double l2 = pt3.distance( pt1 );
|
||||||
|
double l3 = pt1.distance( pt2 );
|
||||||
|
|
||||||
|
if ( ( l1 * l1 ) - ( l2 * l2 + l3 * l3 ) >= epsilon )
|
||||||
|
return QgsCircle().from2Points( pt2, pt3 );
|
||||||
|
else if ( ( l2 * l2 ) - ( l1 * l1 + l3 * l3 ) >= epsilon )
|
||||||
|
return QgsCircle().from2Points( pt3, pt1 );
|
||||||
|
else if ( ( l3 * l3 ) - ( l1 * l1 + l2 * l2 ) >= epsilon )
|
||||||
|
return QgsCircle().from2Points( pt1, pt2 );
|
||||||
|
else
|
||||||
|
return QgsCircle().from3Points( pt1, pt2, pt3, epsilon );
|
||||||
|
}
|
||||||
|
|
||||||
QgsCircle QgsCircle::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 )
|
QgsCircle QgsCircle::fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 )
|
||||||
{
|
{
|
||||||
double delta_x = std::fabs( pt1.x() - pt2.x() );
|
double delta_x = std::fabs( pt1.x() - pt2.x() );
|
||||||
@ -245,6 +261,11 @@ QgsCircularString *QgsCircle::toCircularString( bool oriented ) const
|
|||||||
return circString.release();
|
return circString.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QgsCircle::contains( const QgsPoint &point, double epsilon ) const
|
||||||
|
{
|
||||||
|
return ( mCenter.distance( point ) <= mSemiMajorAxis + epsilon );
|
||||||
|
}
|
||||||
|
|
||||||
QgsRectangle QgsCircle::boundingBox() const
|
QgsRectangle QgsCircle::boundingBox() const
|
||||||
{
|
{
|
||||||
return QgsRectangle( mCenter.x() - mSemiMajorAxis, mCenter.y() - mSemiMajorAxis, mCenter.x() + mSemiMajorAxis, mCenter.y() + mSemiMajorAxis );
|
return QgsRectangle( mCenter.x() - mSemiMajorAxis, mCenter.y() - mSemiMajorAxis, mCenter.x() + mSemiMajorAxis, mCenter.y() + mSemiMajorAxis );
|
||||||
|
@ -119,6 +119,18 @@ class CORE_EXPORT QgsCircle : public QgsEllipse
|
|||||||
*/
|
*/
|
||||||
static QgsCircle fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 );
|
static QgsCircle fromExtent( const QgsPoint &pt1, const QgsPoint &pt2 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the smallest circle from 3 points.
|
||||||
|
* Z and m values are dropped for the center point.
|
||||||
|
* The azimuth always takes the default value.
|
||||||
|
* If the points are colinear an empty circle is returned.
|
||||||
|
* \param pt1 First point.
|
||||||
|
* \param pt2 Second point.
|
||||||
|
* \param pt3 Third point.
|
||||||
|
* \param epsilon Value used to compare point.
|
||||||
|
*/
|
||||||
|
static QgsCircle minimalCircleFrom3Points( const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double epsilon = 1E-8 );
|
||||||
|
|
||||||
double area() const override;
|
double area() const override;
|
||||||
double perimeter() const override;
|
double perimeter() const override;
|
||||||
|
|
||||||
@ -168,6 +180,8 @@ class CORE_EXPORT QgsCircle : public QgsEllipse
|
|||||||
*/
|
*/
|
||||||
QgsCircularString *toCircularString( bool oriented = false ) const;
|
QgsCircularString *toCircularString( bool oriented = false ) const;
|
||||||
|
|
||||||
|
//! Returns true if the circle contains the \a point.
|
||||||
|
bool contains( const QgsPoint &point, double epsilon = 1E-8 ) const;
|
||||||
|
|
||||||
QgsRectangle boundingBox() const override;
|
QgsRectangle boundingBox() const override;
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ email : morb at ozemail dot com dot au
|
|||||||
#include "qgspoint.h"
|
#include "qgspoint.h"
|
||||||
#include "qgspolygon.h"
|
#include "qgspolygon.h"
|
||||||
#include "qgslinestring.h"
|
#include "qgslinestring.h"
|
||||||
|
#include "qgscircle.h"
|
||||||
|
|
||||||
struct QgsGeometryPrivate
|
struct QgsGeometryPrivate
|
||||||
{
|
{
|
||||||
@ -1008,6 +1009,81 @@ QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle
|
|||||||
return minBounds;
|
return minBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QgsCircle __recMinimalEnclosingCircle( QgsMultiPoint points, QgsMultiPoint boundary )
|
||||||
|
{
|
||||||
|
auto l_boundary = boundary.length();
|
||||||
|
QgsCircle circ_mec;
|
||||||
|
if ( ( points.length() == 0 ) || ( l_boundary == 3 ) )
|
||||||
|
{
|
||||||
|
switch ( l_boundary )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
circ_mec = QgsCircle();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
circ_mec = QgsCircle( QgsPoint( boundary.last() ), 0 );
|
||||||
|
boundary.pop_back();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
QgsPointXY p1 = boundary.last();
|
||||||
|
boundary.pop_back();
|
||||||
|
QgsPointXY p2 = boundary.last();
|
||||||
|
boundary.pop_back();
|
||||||
|
circ_mec = QgsCircle().from2Points( QgsPoint( p1 ), QgsPoint( p2 ) );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
QgsPoint p1( boundary.at( 0 ) );
|
||||||
|
QgsPoint p2( boundary.at( 1 ) );
|
||||||
|
QgsPoint p3( boundary.at( 2 ) );
|
||||||
|
circ_mec = QgsCircle().minimalCircleFrom3Points( p1, p2, p3 );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return circ_mec;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QgsPointXY pxy = points.last();
|
||||||
|
points.pop_back();
|
||||||
|
circ_mec = __recMinimalEnclosingCircle( points, boundary );
|
||||||
|
QgsPoint p( pxy );
|
||||||
|
if ( !circ_mec.contains( p ) )
|
||||||
|
{
|
||||||
|
boundary.append( pxy );
|
||||||
|
circ_mec = __recMinimalEnclosingCircle( points, boundary );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return circ_mec;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsGeometry QgsGeometry::minimalEnclosingCircle( QgsPointXY ¢er, double &radius, unsigned int segments ) const
|
||||||
|
{
|
||||||
|
center = QgsPointXY( );
|
||||||
|
radius = 0;
|
||||||
|
|
||||||
|
if ( !d->geometry )
|
||||||
|
{
|
||||||
|
return QgsGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* optimization */
|
||||||
|
QgsGeometry hull = convexHull();
|
||||||
|
if ( hull.isNull() )
|
||||||
|
return QgsGeometry();
|
||||||
|
|
||||||
|
QgsMultiPoint P = hull.convertToPoint( true ).asMultiPoint();
|
||||||
|
QgsMultiPoint R;
|
||||||
|
|
||||||
|
QgsCircle circ = __recMinimalEnclosingCircle( P, R );
|
||||||
|
center = QgsPointXY( circ.center() );
|
||||||
|
radius = circ.radius();
|
||||||
|
QgsGeometry geom;
|
||||||
|
geom.setGeometry( circ.toPolygon( segments ) );
|
||||||
|
return geom;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
|
QgsGeometry QgsGeometry::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
|
||||||
{
|
{
|
||||||
QgsInternalGeometryEngine engine( *this );
|
QgsInternalGeometryEngine engine( *this );
|
||||||
|
@ -599,6 +599,15 @@ class CORE_EXPORT QgsGeometry
|
|||||||
*/
|
*/
|
||||||
QgsGeometry orientedMinimumBoundingBox( double &area SIP_OUT, double &angle SIP_OUT, double &width SIP_OUT, double &height SIP_OUT ) const;
|
QgsGeometry orientedMinimumBoundingBox( double &area SIP_OUT, double &angle SIP_OUT, double &width SIP_OUT, double &height SIP_OUT ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimal enclosing circle for the geometry.
|
||||||
|
* \param center Center of the minimal enclosing circle returneds
|
||||||
|
* \param radius Radius of the minimal enclosing circle returned
|
||||||
|
* \param segments Number of segments used to segment geometry. \see QgsEllipse::toPolygon()
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
QgsGeometry minimalEnclosingCircle( QgsPointXY ¢er SIP_OUT, double &radius SIP_OUT, unsigned int segments = 36 ) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries
|
* Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries
|
||||||
* angles either right angles or flat lines. This is an iterative algorithm which will loop until
|
* angles either right angles or flat lines. This is an iterative algorithm which will loop until
|
||||||
|
@ -128,6 +128,8 @@ class TestQgsGeometry : public QObject
|
|||||||
void reshapeGeometryLineMerge();
|
void reshapeGeometryLineMerge();
|
||||||
void createCollectionOfType();
|
void createCollectionOfType();
|
||||||
|
|
||||||
|
void minimalEnclosingCircle( );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//! A helper method to do a render check to see if the geometry op is as expected
|
//! A helper method to do a render check to see if the geometry op is as expected
|
||||||
bool renderCheck( const QString &testName, const QString &comment = QLatin1String( QLatin1String( "" ) ), int mismatchCount = 0 );
|
bool renderCheck( const QString &testName, const QString &comment = QLatin1String( QLatin1String( "" ) ), int mismatchCount = 0 );
|
||||||
@ -4161,8 +4163,8 @@ void TestQgsGeometry::circle()
|
|||||||
|
|
||||||
//test "alt" constructors
|
//test "alt" constructors
|
||||||
// by2Points
|
// by2Points
|
||||||
QVERIFY( QgsCircle().from2Points( QgsPoint( -5, 0 ), QgsPoint( 5, 0 ) ) == QgsCircle( QgsPoint( 0, 0 ), 10, 90 ) );
|
QVERIFY( QgsCircle().from2Points( QgsPoint( -5, 0 ), QgsPoint( 5, 0 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 90 ) );
|
||||||
QVERIFY( QgsCircle().from2Points( QgsPoint( 0, -5 ), QgsPoint( 0, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 10, 0 ) );
|
QVERIFY( QgsCircle().from2Points( QgsPoint( 0, -5 ), QgsPoint( 0, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) );
|
||||||
// byExtent
|
// byExtent
|
||||||
QVERIFY( QgsCircle().fromExtent( QgsPoint( -5, -5 ), QgsPoint( 5, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) );
|
QVERIFY( QgsCircle().fromExtent( QgsPoint( -5, -5 ), QgsPoint( 5, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 5, 0 ) );
|
||||||
QVERIFY( QgsCircle().fromExtent( QgsPoint( -7.5, -2.5 ), QgsPoint( 2.5, 200.5 ) ) == QgsCircle() );
|
QVERIFY( QgsCircle().fromExtent( QgsPoint( -7.5, -2.5 ), QgsPoint( 2.5, 200.5 ) ) == QgsCircle() );
|
||||||
@ -4182,6 +4184,15 @@ void TestQgsGeometry::circle()
|
|||||||
QgsCircle circ_tgt = QgsCircle().from3Tangents( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 2, 0 ), QgsPoint( 3, 0 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) );
|
QgsCircle circ_tgt = QgsCircle().from3Tangents( QgsPoint( 0, 0 ), QgsPoint( 0, 1 ), QgsPoint( 2, 0 ), QgsPoint( 3, 0 ), QgsPoint( 5, 0 ), QgsPoint( 0, 5 ) );
|
||||||
QGSCOMPARENEARPOINT( circ_tgt.center(), QgsPoint( 1.4645, 1.4645 ), 0.0001 );
|
QGSCOMPARENEARPOINT( circ_tgt.center(), QgsPoint( 1.4645, 1.4645 ), 0.0001 );
|
||||||
QGSCOMPARENEAR( circ_tgt.radius(), 1.4645, 0.0001 );
|
QGSCOMPARENEAR( circ_tgt.radius(), 1.4645, 0.0001 );
|
||||||
|
// minimalCircleFrom3points
|
||||||
|
QgsCircle minCircle3Points = QgsCircle().minimalCircleFrom3Points( QgsPoint( 0, 5 ), QgsPoint( 0, -5 ), QgsPoint( 1, 2 ) );
|
||||||
|
QGSCOMPARENEARPOINT( minCircle3Points.center(), QgsPoint( 0, 0 ), 0.0001 );
|
||||||
|
QGSCOMPARENEAR( minCircle3Points.radius(), 5.0, 0.0001 );
|
||||||
|
minCircle3Points = QgsCircle().minimalCircleFrom3Points( QgsPoint( 0, 5 ), QgsPoint( 5, 0 ), QgsPoint( -5, 0 ) );
|
||||||
|
QGSCOMPARENEARPOINT( minCircle3Points.center(), QgsPoint( 0, 0 ), 0.0001 );
|
||||||
|
QGSCOMPARENEAR( minCircle3Points.radius(), 5.0, 0.0001 );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// test quadrant
|
// test quadrant
|
||||||
QVector<QgsPoint> quad = QgsCircle( QgsPoint( 0, 0 ), 5 ).northQuadrant();
|
QVector<QgsPoint> quad = QgsCircle( QgsPoint( 0, 0 ), 5 ).northQuadrant();
|
||||||
@ -4286,6 +4297,15 @@ void TestQgsGeometry::circle()
|
|||||||
QGSCOMPARENEAR( 314.1593, QgsCircle( QgsPoint( 0, 0 ), 10 ).area(), 0.0001 );
|
QGSCOMPARENEAR( 314.1593, QgsCircle( QgsPoint( 0, 0 ), 10 ).area(), 0.0001 );
|
||||||
// perimeter
|
// perimeter
|
||||||
QGSCOMPARENEAR( 31.4159, QgsCircle( QgsPoint( 0, 0 ), 5 ).perimeter(), 0.0001 );
|
QGSCOMPARENEAR( 31.4159, QgsCircle( QgsPoint( 0, 0 ), 5 ).perimeter(), 0.0001 );
|
||||||
|
|
||||||
|
// contains
|
||||||
|
QgsPoint pc;
|
||||||
|
pc = QgsPoint( 1, 1 );
|
||||||
|
QVERIFY( QgsCircle( QgsPoint( 0, 0 ), 5 ).contains( pc ) );
|
||||||
|
pc = QgsPoint( 0, 5 );
|
||||||
|
QVERIFY( QgsCircle( QgsPoint( 0, 0 ), 5 ).contains( pc ) );
|
||||||
|
pc = QgsPoint( 6, 1 );
|
||||||
|
QVERIFY( !QgsCircle( QgsPoint( 0, 0 ), 5 ).contains( pc ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestQgsGeometry::regularPolygon()
|
void TestQgsGeometry::regularPolygon()
|
||||||
@ -5577,5 +5597,73 @@ void TestQgsGeometry::createCollectionOfType()
|
|||||||
QVERIFY( dynamic_cast< QgsMultiSurface *>( collect.get() ) );
|
QVERIFY( dynamic_cast< QgsMultiSurface *>( collect.get() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestQgsGeometry::minimalEnclosingCircle()
|
||||||
|
{
|
||||||
|
QgsGeometry geomTest;
|
||||||
|
QgsGeometry result, resultTest;
|
||||||
|
QgsPointXY center;
|
||||||
|
double radius;
|
||||||
|
|
||||||
|
// empty
|
||||||
|
result = geomTest.minimalEnclosingCircle( center, radius );
|
||||||
|
QCOMPARE( center, QgsPointXY() );
|
||||||
|
QCOMPARE( radius, 0.0 );
|
||||||
|
QCOMPARE( result, QgsGeometry() );
|
||||||
|
|
||||||
|
// caase 1
|
||||||
|
geomTest = QgsGeometry::fromPoint( QgsPointXY( 5, 5 ) );
|
||||||
|
result = geomTest.minimalEnclosingCircle( center, radius );
|
||||||
|
QCOMPARE( center, QgsPointXY( 5, 5 ) );
|
||||||
|
QCOMPARE( radius, 0.0 );
|
||||||
|
resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) );
|
||||||
|
QCOMPARE( result, resultTest );
|
||||||
|
|
||||||
|
// case 2
|
||||||
|
geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT( 3 8, 7 4 )" ) );
|
||||||
|
result = geomTest.minimalEnclosingCircle( center, radius );
|
||||||
|
QGSCOMPARENEARPOINT( center, QgsPointXY( 5, 6 ), 0.0001 );
|
||||||
|
QGSCOMPARENEAR( radius, sqrt( 2 ) * 2, 0.0001 );
|
||||||
|
resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) );
|
||||||
|
QCOMPARE( result, resultTest );
|
||||||
|
|
||||||
|
geomTest = QgsGeometry::fromWkt( QString( "LINESTRING( 0 5, 2 2, 0 -5, -1 -1 )" ) );
|
||||||
|
result = geomTest.minimalEnclosingCircle( center, radius );
|
||||||
|
QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 );
|
||||||
|
QGSCOMPARENEAR( radius, 5, 0.0001 );
|
||||||
|
resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) );
|
||||||
|
QCOMPARE( result, resultTest );
|
||||||
|
|
||||||
|
geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT( 0 5, 2 2, 0 -5, -1 -1 )" ) );
|
||||||
|
result = geomTest.minimalEnclosingCircle( center, radius );
|
||||||
|
QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 );
|
||||||
|
QGSCOMPARENEAR( radius, 5, 0.0001 );
|
||||||
|
resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) );
|
||||||
|
QCOMPARE( result, resultTest );
|
||||||
|
|
||||||
|
geomTest = QgsGeometry::fromWkt( QString( "POLYGON(( 0 5, 2 2, 0 -5, -1 -1 ))" ) );
|
||||||
|
result = geomTest.minimalEnclosingCircle( center, radius );
|
||||||
|
QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 );
|
||||||
|
QGSCOMPARENEAR( radius, 5, 0.0001 );
|
||||||
|
resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) );
|
||||||
|
QCOMPARE( result, resultTest );
|
||||||
|
|
||||||
|
geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT( 0 5, 0 -5, 0 0 )" ) );
|
||||||
|
result = geomTest.minimalEnclosingCircle( center, radius );
|
||||||
|
QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 );
|
||||||
|
QGSCOMPARENEAR( radius, 5, 0.0001 );
|
||||||
|
resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) );
|
||||||
|
QCOMPARE( result, resultTest );
|
||||||
|
|
||||||
|
// case 3
|
||||||
|
geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT((0 0), (5 5), (0 -5), (0 5), (-5 0))" ) );
|
||||||
|
result = geomTest.minimalEnclosingCircle( center, radius );
|
||||||
|
QGSCOMPARENEARPOINT( center, QgsPointXY( 0.8333, 0.8333 ), 0.0001 );
|
||||||
|
QGSCOMPARENEAR( radius, 5.8926, 0.0001 );
|
||||||
|
resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) );
|
||||||
|
QCOMPARE( result, resultTest );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QGSTEST_MAIN( TestQgsGeometry )
|
QGSTEST_MAIN( TestQgsGeometry )
|
||||||
#include "testqgsgeometry.moc"
|
#include "testqgsgeometry.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user