mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
[FEATURE][Processing] Minimal enclosing circle
This commit is contained in:
parent
f51244c98b
commit
e30f7044c9
@ -112,6 +112,19 @@ class QgsCircle : QgsEllipse
|
||||
:rtype: QgsCircle
|
||||
%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 perimeter() const;
|
||||
@ -159,6 +172,11 @@ Set the radius of the circle
|
||||
:rtype: QgsCircularString
|
||||
%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;
|
||||
|
||||
|
@ -577,6 +577,16 @@ Returns true if WKB of the geometry is of WKBMulti* type
|
||||
:rtype: QgsGeometry
|
||||
%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;
|
||||
%Docstring
|
||||
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.
|
||||
|
||||
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: >
|
||||
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 .Merge import Merge
|
||||
from .MergeLines import MergeLines
|
||||
from .MinimalEnclosingCircle import MinimalEnclosingCircle
|
||||
from .NearestNeighbourAnalysis import NearestNeighbourAnalysis
|
||||
from .OffsetLine import OffsetLine
|
||||
from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox
|
||||
@ -253,6 +254,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
|
||||
MeanCoords(),
|
||||
Merge(),
|
||||
MergeLines(),
|
||||
MinimalEnclosingCircle(),
|
||||
NearestNeighbourAnalysis(),
|
||||
OffsetLine(),
|
||||
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:
|
||||
name: expected/raster_extent.gml
|
||||
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 );
|
||||
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 );
|
||||
}
|
||||
@ -189,6 +189,22 @@ QgsCircle QgsCircle::from3Tangents( const QgsPoint &pt1_tg1, const QgsPoint &pt2
|
||||
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 )
|
||||
{
|
||||
double delta_x = std::fabs( pt1.x() - pt2.x() );
|
||||
@ -245,6 +261,11 @@ QgsCircularString *QgsCircle::toCircularString( bool oriented ) const
|
||||
return circString.release();
|
||||
}
|
||||
|
||||
bool QgsCircle::contains( const QgsPoint &point, double epsilon ) const
|
||||
{
|
||||
return ( mCenter.distance( point ) <= mSemiMajorAxis + epsilon );
|
||||
}
|
||||
|
||||
QgsRectangle QgsCircle::boundingBox() const
|
||||
{
|
||||
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 );
|
||||
|
||||
/**
|
||||
* 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 perimeter() const override;
|
||||
|
||||
@ -168,6 +180,8 @@ class CORE_EXPORT QgsCircle : public QgsEllipse
|
||||
*/
|
||||
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;
|
||||
|
||||
|
@ -44,6 +44,7 @@ email : morb at ozemail dot com dot au
|
||||
#include "qgspoint.h"
|
||||
#include "qgspolygon.h"
|
||||
#include "qgslinestring.h"
|
||||
#include "qgscircle.h"
|
||||
|
||||
struct QgsGeometryPrivate
|
||||
{
|
||||
@ -1008,6 +1009,81 @@ QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double &area, double &angle
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 createCollectionOfType();
|
||||
|
||||
void minimalEnclosingCircle( );
|
||||
|
||||
private:
|
||||
//! 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 );
|
||||
@ -4161,8 +4163,8 @@ void TestQgsGeometry::circle()
|
||||
|
||||
//test "alt" constructors
|
||||
// by2Points
|
||||
QVERIFY( QgsCircle().from2Points( QgsPoint( -5, 0 ), QgsPoint( 5, 0 ) ) == QgsCircle( QgsPoint( 0, 0 ), 10, 90 ) );
|
||||
QVERIFY( QgsCircle().from2Points( QgsPoint( 0, -5 ), QgsPoint( 0, 5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 10, 0 ) );
|
||||
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 ), 5, 0 ) );
|
||||
// byExtent
|
||||
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() );
|
||||
@ -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 ) );
|
||||
QGSCOMPARENEARPOINT( circ_tgt.center(), QgsPoint( 1.4645, 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
|
||||
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 );
|
||||
// perimeter
|
||||
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()
|
||||
@ -5577,5 +5597,73 @@ void TestQgsGeometry::createCollectionOfType()
|
||||
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 )
|
||||
#include "testqgsgeometry.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user