mirror of
https://github.com/qgis/QGIS.git
synced 2025-11-22 00:14:55 -05:00
[FEATURE][processing] New algorithm to orthagonalize geometries
Adds a new QgsGeometry::orthagonalize method which tries to make angles in geometries either right angles or straight lines Also adds a processing algorithm exposing this feature.
This commit is contained in:
parent
379e7a42e6
commit
4b6f3a3ee5
@ -420,6 +420,16 @@ class QgsGeometry
|
||||
*/
|
||||
QgsGeometry orientedMinimumBoundingBox( double& area /Out/, double &angle /Out/, double& width /Out/, double& height /Out/ ) const;
|
||||
|
||||
/**
|
||||
* Attempts to orthagonalize 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
|
||||
* either the vertices are within a specified tolerance of right angles or a set number of maximum
|
||||
* iterations is reached. The angle threshold parameter specifies how close to a right angle or
|
||||
* straight line an angle must be before it is attempted to be straightened.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
QgsGeometry orthagonalize( double tolerance = 1.0E-8, int maxIterations = 1000, double angleThreshold = 15.0 ) const;
|
||||
|
||||
/** Test for intersection with a rectangle (uses GEOS) */
|
||||
bool intersects( const QgsRectangle& r ) const;
|
||||
|
||||
|
||||
@ -320,6 +320,13 @@ 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:orthagonalize: >
|
||||
This algorithm takes a line or polygon layer and attempts to orthagonalize 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.
|
||||
|
||||
The angle tolerance parameter is used to specify the maximum deviation from a right angle or straight line a node can have for it to be adjusted. Smaller tolerances mean that only nodes which are already closer to right angles will be adjusted, and larger tolerances mean that nodes which deviate further from right angles will also be adjusted.
|
||||
|
||||
The algorithm is iterative. Setting a larger number for the maximum iterations will result in a more orthagonal geometry at the cost of extra processing time.
|
||||
|
||||
qgis:pointsalonglines: >
|
||||
Creates points at regular intervals along line or polygon geometries. Created points will have new attributes added for the distance along the geometry and the angle of the line at the point.
|
||||
|
||||
|
||||
92
python/plugins/processing/algs/qgis/Orthagonalize.py
Normal file
92
python/plugins/processing/algs/qgis/Orthagonalize.py
Normal file
@ -0,0 +1,92 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
Orthagonalize.py
|
||||
----------------
|
||||
Date : December 2016
|
||||
Copyright : (C) 2016 by Nyall Dawson
|
||||
Email : nyall dot dawson 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__ = 'Nyall Dawson'
|
||||
__date__ = 'December 2016'
|
||||
__copyright__ = '(C) 2016, Nyall Dawson'
|
||||
|
||||
# This will get replaced with a git SHA1 when you do a git archive323
|
||||
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
|
||||
from processing.core.GeoAlgorithm import GeoAlgorithm
|
||||
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
|
||||
from processing.core.parameters import ParameterVector, ParameterNumber
|
||||
from processing.core.outputs import OutputVector
|
||||
from processing.tools import dataobjects, vector
|
||||
|
||||
|
||||
class Orthagonalize(GeoAlgorithm):
|
||||
|
||||
INPUT_LAYER = 'INPUT_LAYER'
|
||||
OUTPUT_LAYER = 'OUTPUT_LAYER'
|
||||
MAX_ITERATIONS = 'MAX_ITERATIONS'
|
||||
DISTANCE_THRESHOLD = 'DISTANCE_THRESHOLD'
|
||||
ANGLE_TOLERANCE = 'ANGLE_TOLERANCE'
|
||||
|
||||
def defineCharacteristics(self):
|
||||
self.name, self.i18n_name = self.trAlgorithm('Orthagonalize')
|
||||
self.group, self.i18n_group = self.trAlgorithm('Vector geometry tools')
|
||||
self.tags = self.tr('rectangle,perpendicular,right,angles,square,quadrilateralise')
|
||||
|
||||
self.addParameter(ParameterVector(self.INPUT_LAYER,
|
||||
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_LINE,
|
||||
dataobjects.TYPE_VECTOR_POLYGON]))
|
||||
self.addParameter(ParameterNumber(self.ANGLE_TOLERANCE,
|
||||
self.tr('Maximum angle tolerance (degrees)'),
|
||||
0.0, 45.0, 15.0))
|
||||
|
||||
max_iterations = ParameterNumber(self.MAX_ITERATIONS,
|
||||
self.tr('Maximum algorithm iterations'),
|
||||
1, 10000, 1000)
|
||||
max_iterations.isAdvanced = True
|
||||
self.addParameter(max_iterations)
|
||||
|
||||
self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Orthagonalized')))
|
||||
|
||||
def processAlgorithm(self, progress):
|
||||
layer = dataobjects.getObjectFromUri(
|
||||
self.getParameterValue(self.INPUT_LAYER))
|
||||
max_iterations = self.getParameterValue(self.MAX_ITERATIONS)
|
||||
angle_tolerance = self.getParameterValue(self.ANGLE_TOLERANCE)
|
||||
writer = self.getOutputFromName(
|
||||
self.OUTPUT_LAYER).getVectorWriter(
|
||||
layer.fields(),
|
||||
layer.wkbType(),
|
||||
layer.crs())
|
||||
|
||||
features = vector.features(layer)
|
||||
total = 100.0 / len(features)
|
||||
|
||||
for current, input_feature in enumerate(features):
|
||||
output_feature = input_feature
|
||||
input_geometry = input_feature.geometry()
|
||||
if input_geometry:
|
||||
output_geometry = input_geometry.orthagonalize(1.0e-8, max_iterations, angle_tolerance)
|
||||
if not output_geometry:
|
||||
raise GeoAlgorithmExecutionException(
|
||||
self.tr('Error orthagonalizing geometry'))
|
||||
|
||||
output_feature.setGeometry(output_geometry)
|
||||
|
||||
writer.addFeature(output_feature)
|
||||
progress.setPercentage(int(current * total))
|
||||
|
||||
del writer
|
||||
@ -183,6 +183,7 @@ from .CreateAttributeIndex import CreateAttributeIndex
|
||||
from .DropGeometry import DropGeometry
|
||||
from .BasicStatistics import BasicStatisticsForField
|
||||
from .Heatmap import Heatmap
|
||||
from .Orthagonalize import Orthagonalize
|
||||
|
||||
pluginPath = os.path.normpath(os.path.join(
|
||||
os.path.split(os.path.dirname(__file__))[0], os.pardir))
|
||||
@ -247,7 +248,8 @@ class QGISAlgorithmProvider(AlgorithmProvider):
|
||||
RemoveNullGeometry(), ExtractByExpression(), ExtendLines(),
|
||||
ExtractSpecificNodes(), GeometryByExpression(), SnapGeometriesToLayer(),
|
||||
PoleOfInaccessibility(), CreateAttributeIndex(), DropGeometry(),
|
||||
BasicStatisticsForField(), RasterCalculator(), Heatmap()
|
||||
BasicStatisticsForField(), RasterCalculator(), Heatmap(),
|
||||
Orthagonalize()
|
||||
]
|
||||
|
||||
if hasMatplotlib:
|
||||
|
||||
20
python/plugins/processing/tests/testdata/custom/lines_to_orth.gfs
vendored
Normal file
20
python/plugins/processing/tests/testdata/custom/lines_to_orth.gfs
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<GMLFeatureClassList>
|
||||
<GMLFeatureClass>
|
||||
<Name>lines_to_orth</Name>
|
||||
<ElementPath>lines_to_orth</ElementPath>
|
||||
<GeometryType>2</GeometryType>
|
||||
<SRSName>EPSG:4326</SRSName>
|
||||
<DatasetSpecificInfo>
|
||||
<FeatureCount>4</FeatureCount>
|
||||
<ExtentXMin>-1.17586</ExtentXMin>
|
||||
<ExtentXMax>0.68704</ExtentXMax>
|
||||
<ExtentYMin>-0.96433</ExtentYMin>
|
||||
<ExtentYMax>1.11582</ExtentYMax>
|
||||
</DatasetSpecificInfo>
|
||||
<PropertyDefn>
|
||||
<Name>intval</Name>
|
||||
<ElementPath>intval</ElementPath>
|
||||
<Type>Integer</Type>
|
||||
</PropertyDefn>
|
||||
</GMLFeatureClass>
|
||||
</GMLFeatureClassList>
|
||||
38
python/plugins/processing/tests/testdata/custom/lines_to_orth.gml
vendored
Normal file
38
python/plugins/processing/tests/testdata/custom/lines_to_orth.gml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ogr:FeatureCollection
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation=""
|
||||
xmlns:ogr="http://ogr.maptools.org/"
|
||||
xmlns:gml="http://www.opengis.net/gml">
|
||||
<gml:boundedBy>
|
||||
<gml:Box>
|
||||
<gml:coord><gml:X>-1.175859979156524</gml:X><gml:Y>-0.9643318489227042</gml:Y></gml:coord>
|
||||
<gml:coord><gml:X>0.6870430854602452</gml:X><gml:Y>1.115824129342566</gml:Y></gml:coord>
|
||||
</gml:Box>
|
||||
</gml:boundedBy>
|
||||
|
||||
<gml:featureMember>
|
||||
<ogr:lines_to_orth fid="lines_to_orth.0">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1.074456310482982,-0.916199588298252 0.040225681809122,-0.955727318521376 0.04741254184969,-0.617944896614678 0.687043085460245,-0.661066056858086</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:intval>0</ogr:intval>
|
||||
</ogr:lines_to_orth>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:lines_to_orth fid="lines_to_orth.1">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1.074456310482982,-0.916199588298252 0.048128551164702,-0.964331848922704 0.062280009502849,-0.634278538511395 0.687043085460245,-0.661066056858086</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:intval>1</ogr:intval>
|
||||
</ogr:lines_to_orth>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:lines_to_orth fid="lines_to_orth.2">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1.027472879898916,-0.278453309909787 -0.290913154921217,0.321316751857768 0.531737966482446,-0.156968783842037</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:intval>2</ogr:intval>
|
||||
</ogr:lines_to_orth>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:lines_to_orth fid="lines_to_orth.3">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1.1679544331119,0.483380445772577 -1.175859979156524,1.115824129342566 0.231327216786701,1.044674214940942 0.215516124697451,0.522908175995701 -1.065182334531776,0.412230531370954</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:intval>3</ogr:intval>
|
||||
</ogr:lines_to_orth>
|
||||
</gml:featureMember>
|
||||
</ogr:FeatureCollection>
|
||||
20
python/plugins/processing/tests/testdata/custom/polys_to_orth.gfs
vendored
Normal file
20
python/plugins/processing/tests/testdata/custom/polys_to_orth.gfs
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<GMLFeatureClassList>
|
||||
<GMLFeatureClass>
|
||||
<Name>polys_to_orth</Name>
|
||||
<ElementPath>polys_to_orth</ElementPath>
|
||||
<GeometryType>3</GeometryType>
|
||||
<SRSName>EPSG:4326</SRSName>
|
||||
<DatasetSpecificInfo>
|
||||
<FeatureCount>4</FeatureCount>
|
||||
<ExtentXMin>-0.70300</ExtentXMin>
|
||||
<ExtentXMax>0.92360</ExtentXMax>
|
||||
<ExtentYMin>-1.55335</ExtentYMin>
|
||||
<ExtentYMax>0.89200</ExtentYMax>
|
||||
</DatasetSpecificInfo>
|
||||
<PropertyDefn>
|
||||
<Name>intval</Name>
|
||||
<ElementPath>intval</ElementPath>
|
||||
<Type>Integer</Type>
|
||||
</PropertyDefn>
|
||||
</GMLFeatureClass>
|
||||
</GMLFeatureClassList>
|
||||
38
python/plugins/processing/tests/testdata/custom/polys_to_orth.gml
vendored
Normal file
38
python/plugins/processing/tests/testdata/custom/polys_to_orth.gml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ogr:FeatureCollection
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation=""
|
||||
xmlns:ogr="http://ogr.maptools.org/"
|
||||
xmlns:gml="http://www.opengis.net/gml">
|
||||
<gml:boundedBy>
|
||||
<gml:Box>
|
||||
<gml:coord><gml:X>-0.703</gml:X><gml:Y>-1.553346855983773</gml:Y></gml:coord>
|
||||
<gml:coord><gml:X>0.923603171676194</gml:X><gml:Y>0.892</gml:Y></gml:coord>
|
||||
</gml:Box>
|
||||
</gml:boundedBy>
|
||||
|
||||
<gml:featureMember>
|
||||
<ogr:polys_to_orth fid="polys_to_orth.0">
|
||||
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-0.48158215010142,0.086166328600406 -0.182150101419878,0.081257606490872 -0.201784989858012,-0.12 0.568884381338742,-0.203448275862069 0.568884381338742,-0.772860040567951 -0.545395537525355,-0.689411764705882 -0.48158215010142,0.086166328600406</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>-0.266466849320251,-0.285441896123125 -0.286067376703618,-0.546782261234689 0.132077207474883,-0.605583843384791 0.132077207474883,-0.46184664257343 0.347683008691924,-0.455313133445642 0.341149499564134,-0.305042423506492 -0.266466849320251,-0.285441896123125</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||
<ogr:intval>3</ogr:intval>
|
||||
</ogr:polys_to_orth>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:polys_to_orth fid="polys_to_orth.1">
|
||||
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-0.550304259634889,-1.553346855983773 -0.182150101419878,-0.95448275862069 -0.182150101419878,-0.95448275862069 0.186004056795132,-1.538620689655172 -0.550304259634889,-1.553346855983773</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||
<ogr:intval>1</ogr:intval>
|
||||
</ogr:polys_to_orth>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:polys_to_orth fid="polys_to_orth.2">
|
||||
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>0.506859671768394,-1.376348884381339 0.433099760280288,-1.081309238428914 0.765019361976765,-0.900597455283054 0.923603171676194,-1.132941176470588 0.923603171676194,-1.39110086667896 0.506859671768394,-1.376348884381339</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||
<ogr:intval>2</ogr:intval>
|
||||
</ogr:polys_to_orth>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:polys_to_orth fid="polys_to_orth.3">
|
||||
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-0.699,0.892 -0.703,0.405 -0.022,0.361 0.014,0.851 -0.699,0.892</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>-0.619341074030194,0.777531730269581 -0.619341074030194,0.574992947308119 -0.515804608307302,0.5674228478321 -0.517697133176307,0.516324676368964 -0.411715740512025,0.499291952547919 -0.37965636923108,0.76721669825296 -0.619341074030194,0.777531730269581</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>-0.322407491943678,0.506161817822407 -0.185010186453913,0.735157326972015 -0.046855871016547,0.428568298193201 -0.322407491943678,0.506161817822407</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||
<ogr:intval>4</ogr:intval>
|
||||
</ogr:polys_to_orth>
|
||||
</gml:featureMember>
|
||||
</ogr:FeatureCollection>
|
||||
20
python/plugins/processing/tests/testdata/expected/orthagonal_lines.gfs
vendored
Normal file
20
python/plugins/processing/tests/testdata/expected/orthagonal_lines.gfs
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<GMLFeatureClassList>
|
||||
<GMLFeatureClass>
|
||||
<Name>orthagonal_lines</Name>
|
||||
<ElementPath>orthagonal_lines</ElementPath>
|
||||
<GeometryType>2</GeometryType>
|
||||
<SRSName>EPSG:4326</SRSName>
|
||||
<DatasetSpecificInfo>
|
||||
<FeatureCount>4</FeatureCount>
|
||||
<ExtentXMin>-1.17356</ExtentXMin>
|
||||
<ExtentXMax>0.68704</ExtentXMax>
|
||||
<ExtentYMin>-0.96433</ExtentYMin>
|
||||
<ExtentYMax>1.12603</ExtentYMax>
|
||||
</DatasetSpecificInfo>
|
||||
<PropertyDefn>
|
||||
<Name>intval</Name>
|
||||
<ElementPath>intval</ElementPath>
|
||||
<Type>Integer</Type>
|
||||
</PropertyDefn>
|
||||
</GMLFeatureClass>
|
||||
</GMLFeatureClassList>
|
||||
38
python/plugins/processing/tests/testdata/expected/orthagonal_lines.gml
vendored
Normal file
38
python/plugins/processing/tests/testdata/expected/orthagonal_lines.gml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ogr:FeatureCollection
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation=""
|
||||
xmlns:ogr="http://ogr.maptools.org/"
|
||||
xmlns:gml="http://www.opengis.net/gml">
|
||||
<gml:boundedBy>
|
||||
<gml:Box>
|
||||
<gml:coord><gml:X>-1.173559243727069</gml:X><gml:Y>-0.9643318490464938</gml:Y></gml:coord>
|
||||
<gml:coord><gml:X>0.687043085460245</gml:X><gml:Y>1.126027868738941</gml:Y></gml:coord>
|
||||
</gml:Box>
|
||||
</gml:boundedBy>
|
||||
|
||||
<gml:featureMember>
|
||||
<ogr:orthagonal_lines fid="lines_to_orth.0">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1.074456310482982,-0.916199588298252 0.048128551164703,-0.964331848922704 0.062280009502849,-0.634278538511395 0.687043085460245,-0.661066056858086</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:intval>0</ogr:intval>
|
||||
</ogr:orthagonal_lines>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:orthagonal_lines fid="lines_to_orth.1">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1.074456310482982,-0.916199588298252 0.048128551278313,-0.964331849046494 0.062280009603929,-0.634278538621531 0.687043085460245,-0.661066056858086</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:intval>1</ogr:intval>
|
||||
</ogr:orthagonal_lines>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:orthagonal_lines fid="lines_to_orth.2">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1.027472879898916,-0.278453309909787 -0.290913154921217,0.321316751857768 0.531737966482446,-0.156968783842037</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:intval>2</ogr:intval>
|
||||
</ogr:orthagonal_lines>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:orthagonal_lines fid="lines_to_orth.3">
|
||||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1.1679544331119,0.483380445772577 -1.173559243727069,1.112790863745332 0.312933083255933,1.126027868738941 0.319179574408909,0.42455808473015 -1.065182334531776,0.412230531370954</gml:coordinates></gml:LineString></ogr:geometryProperty>
|
||||
<ogr:intval>3</ogr:intval>
|
||||
</ogr:orthagonal_lines>
|
||||
</gml:featureMember>
|
||||
</ogr:FeatureCollection>
|
||||
20
python/plugins/processing/tests/testdata/expected/orthagonal_polys.gfs
vendored
Normal file
20
python/plugins/processing/tests/testdata/expected/orthagonal_polys.gfs
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<GMLFeatureClassList>
|
||||
<GMLFeatureClass>
|
||||
<Name>orthagonal_polys</Name>
|
||||
<ElementPath>orthagonal_polys</ElementPath>
|
||||
<GeometryType>3</GeometryType>
|
||||
<SRSName>EPSG:4326</SRSName>
|
||||
<DatasetSpecificInfo>
|
||||
<FeatureCount>4</FeatureCount>
|
||||
<ExtentXMin>-0.72569</ExtentXMin>
|
||||
<ExtentXMax>0.92360</ExtentXMax>
|
||||
<ExtentYMin>-1.55335</ExtentYMin>
|
||||
<ExtentYMax>0.89200</ExtentYMax>
|
||||
</DatasetSpecificInfo>
|
||||
<PropertyDefn>
|
||||
<Name>intval</Name>
|
||||
<ElementPath>intval</ElementPath>
|
||||
<Type>Integer</Type>
|
||||
</PropertyDefn>
|
||||
</GMLFeatureClass>
|
||||
</GMLFeatureClassList>
|
||||
38
python/plugins/processing/tests/testdata/expected/orthagonal_polys.gml
vendored
Normal file
38
python/plugins/processing/tests/testdata/expected/orthagonal_polys.gml
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ogr:FeatureCollection
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation=""
|
||||
xmlns:ogr="http://ogr.maptools.org/"
|
||||
xmlns:gml="http://www.opengis.net/gml">
|
||||
<gml:boundedBy>
|
||||
<gml:Box>
|
||||
<gml:coord><gml:X>-0.7256871363573774</gml:X><gml:Y>-1.553346855983773</gml:Y></gml:coord>
|
||||
<gml:coord><gml:X>0.923603171676194</gml:X><gml:Y>0.892</gml:Y></gml:coord>
|
||||
</gml:Box>
|
||||
</gml:boundedBy>
|
||||
|
||||
<gml:featureMember>
|
||||
<ogr:orthagonal_polys fid="polys_to_orth.0">
|
||||
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-0.48158215010142,0.086166328600406 -0.198422114542599,0.066751829457284 -0.211825279251737,-0.128733024349837 0.591070649358435,-0.183782540726045 0.551967683831585,-0.75409838540479 -0.534088281509359,-0.679634367142093 -0.48158215010142,0.086166328600406</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>-0.266466849320251,-0.285441896123125 -0.278158782310973,-0.539523073523498 0.088773800722712,-0.556408034545474 0.094964270726191,-0.421880862195709 0.326599544077082,-0.432539902641404 0.332100997948332,-0.312985856306224 -0.266466849320251,-0.285441896123125</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||
<ogr:intval>3</ogr:intval>
|
||||
</ogr:orthagonal_polys>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:orthagonal_polys fid="polys_to_orth.1">
|
||||
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-0.550304259634889,-1.553346855983773 -0.182150101419878,-0.95448275862069 -0.182150101419878,-0.95448275862069 0.186004056795132,-1.538620689655172 -0.550304259634889,-1.553346855983773</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||
<ogr:intval>1</ogr:intval>
|
||||
</ogr:orthagonal_polys>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:orthagonal_polys fid="polys_to_orth.2">
|
||||
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>0.506859671768394,-1.376348884381339 0.349506988258821,-1.047663631223475 0.783347761187655,-0.839969464295238 0.923603171676194,-1.132941176470588 0.918075884883814,-1.385353630032711 0.506859671768394,-1.376348884381339</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||
<ogr:intval>2</ogr:intval>
|
||||
</ogr:orthagonal_polys>
|
||||
</gml:featureMember>
|
||||
<gml:featureMember>
|
||||
<ogr:orthagonal_polys fid="polys_to_orth.3">
|
||||
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-0.699,0.892 -0.725687136357377,0.384140562836995 -0.009002223260981,0.34648000752227 0.01768491457045,0.854339441983783 -0.699,0.892</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>-0.619341074030194,0.777531730269581 -0.634560418042409,0.561059234380694 -0.531187228159207,0.553791463940342 -0.534852581753804,0.501657302696924 -0.405738104746291,0.492579752926693 -0.386853385820888,0.761186398054952 -0.619341074030194,0.777531730269581</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>-0.322407491943678,0.506161817822407 -0.185010186453913,0.735157326972015 -0.046855871016547,0.428568298193201 -0.322407491943678,0.506161817822407</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
|
||||
<ogr:intval>4</ogr:intval>
|
||||
</ogr:orthagonal_polys>
|
||||
</gml:featureMember>
|
||||
</ogr:FeatureCollection>
|
||||
@ -1638,6 +1638,9 @@ tests:
|
||||
OUTPUT:
|
||||
name: expected/split_lines_with_lines.gml
|
||||
type: vector
|
||||
compare:
|
||||
geometry:
|
||||
precision: 7
|
||||
|
||||
- algorithm: qgis:splitwithlines
|
||||
name: Split poly with lines
|
||||
@ -1652,6 +1655,9 @@ tests:
|
||||
OUTPUT:
|
||||
name: expected/split_polys_with_lines.gml
|
||||
type: vector
|
||||
compare:
|
||||
geometry:
|
||||
precision: 7
|
||||
|
||||
- algorithm: qgis:splitwithlines
|
||||
name: Split lines with same lines
|
||||
@ -1666,6 +1672,9 @@ tests:
|
||||
OUTPUT:
|
||||
name: expected/split_lines_with_lines_same.gml
|
||||
type: vector
|
||||
compare:
|
||||
geometry:
|
||||
precision: 7
|
||||
|
||||
- algorithm: qgis:dropgeometries
|
||||
name: Drop geometries
|
||||
@ -1866,3 +1875,35 @@ tests:
|
||||
OUTPUT:
|
||||
name: expected/oriented_bounds.gml
|
||||
type: vector
|
||||
compare:
|
||||
geometry:
|
||||
precision: 7
|
||||
|
||||
- algorithm: qgis:orthagonalize
|
||||
name: Orthagonalize polys
|
||||
params:
|
||||
INPUT_LAYER:
|
||||
name: custom/polys_to_orth.gml
|
||||
type: vector
|
||||
results:
|
||||
OUTPUT_LAYER:
|
||||
name: expected/orthagonal_polys.gml
|
||||
type: vector
|
||||
compare:
|
||||
geometry:
|
||||
precision: 7
|
||||
|
||||
|
||||
- algorithm: qgis:orthagonalize
|
||||
name: Orthagonalize lines
|
||||
params:
|
||||
INPUT_LAYER:
|
||||
name: custom/lines_to_orth.gml
|
||||
type: vector
|
||||
results:
|
||||
OUTPUT_LAYER:
|
||||
name: expected/orthagonal_lines.gml
|
||||
type: vector
|
||||
compare:
|
||||
geometry:
|
||||
precision: 7
|
||||
@ -921,6 +921,13 @@ QgsGeometry QgsGeometry::orientedMinimumBoundingBox( double& area, double &angle
|
||||
return minBounds;
|
||||
}
|
||||
|
||||
QgsGeometry QgsGeometry::orthagonalize( double tolerance, int maxIterations, double angleThreshold ) const
|
||||
{
|
||||
QgsInternalGeometryEngine engine( *this );
|
||||
|
||||
return engine.orthagonalize( tolerance, maxIterations, angleThreshold );
|
||||
}
|
||||
|
||||
bool QgsGeometry::intersects( const QgsRectangle& r ) const
|
||||
{
|
||||
QgsGeometry g = fromRect( r );
|
||||
|
||||
@ -473,6 +473,16 @@ class CORE_EXPORT QgsGeometry
|
||||
*/
|
||||
QgsGeometry orientedMinimumBoundingBox( double& area, double &angle, double& width, double& height ) const;
|
||||
|
||||
/**
|
||||
* Attempts to orthagonalize 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
|
||||
* either the vertices are within a specified tolerance of right angles or a set number of maximum
|
||||
* iterations is reached. The angle threshold parameter specifies how close to a right angle or
|
||||
* straight line an angle must be before it is attempted to be straightened.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
QgsGeometry orthagonalize( double tolerance = 1.0E-8, int maxIterations = 1000, double angleThreshold = 15.0 ) const;
|
||||
|
||||
//! Test for intersection with a rectangle (uses GEOS)
|
||||
bool intersects( const QgsRectangle& r ) const;
|
||||
|
||||
|
||||
@ -242,3 +242,236 @@ QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision ,
|
||||
|
||||
return QgsGeometry( new QgsPointV2( bestCell->x, bestCell->y ) );
|
||||
}
|
||||
|
||||
|
||||
// helpers for orthagonalize
|
||||
// adapted from original code in potlach/id osm editor
|
||||
|
||||
bool dotProductWithinAngleTolerance( double dotProduct, double lowerThreshold, double upperThreshold )
|
||||
{
|
||||
return lowerThreshold > qAbs( dotProduct ) || qAbs( dotProduct ) > upperThreshold;
|
||||
}
|
||||
|
||||
double normalizedDotProduct( const QgsPointV2& a, const QgsPointV2& b, const QgsPointV2& c )
|
||||
{
|
||||
QgsVector p = a - b;
|
||||
QgsVector q = c - b;
|
||||
|
||||
if ( p.length() > 0 )
|
||||
p = p.normalized();
|
||||
if ( q.length() > 0 )
|
||||
q = q.normalized();
|
||||
|
||||
return p * q;
|
||||
}
|
||||
|
||||
double squareness( QgsLineString* ring, double lowerThreshold, double upperThreshold )
|
||||
{
|
||||
double sum = 0.0;
|
||||
|
||||
bool isClosed = ring->isClosed();
|
||||
int numPoints = ring->numPoints();
|
||||
QgsPointV2 a;
|
||||
QgsPointV2 b;
|
||||
QgsPointV2 c;
|
||||
|
||||
for ( int i = 0; i < numPoints; ++i )
|
||||
{
|
||||
if ( !isClosed && i == numPoints - 1 )
|
||||
break;
|
||||
else if ( !isClosed && i == 0 )
|
||||
{
|
||||
b = ring->pointN( 0 );
|
||||
c = ring->pointN( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( i == 0 )
|
||||
{
|
||||
a = ring->pointN( numPoints - 1 );
|
||||
b = ring->pointN( 0 );
|
||||
}
|
||||
if ( i == numPoints - 1 )
|
||||
c = ring->pointN( 0 );
|
||||
else
|
||||
c = ring->pointN( i + 1 );
|
||||
|
||||
double dotProduct = normalizedDotProduct( a, b, c );
|
||||
if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) )
|
||||
continue;
|
||||
|
||||
sum += 2.0 * qMin( qAbs( dotProduct - 1.0 ), qMin( qAbs( dotProduct ), qAbs( dotProduct + 1 ) ) );
|
||||
}
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
QgsVector calcMotion( const QgsPointV2& a, const QgsPointV2& b, const QgsPointV2& c,
|
||||
double lowerThreshold, double upperThreshold )
|
||||
{
|
||||
QgsVector p = a - b;
|
||||
QgsVector q = c - b;
|
||||
|
||||
if ( qgsDoubleNear( p.length(), 0.0 ) || qgsDoubleNear( q.length(), 0.0 ) )
|
||||
return QgsVector( 0, 0 );
|
||||
|
||||
// 2.0 is a magic number from the original JOSM source code
|
||||
double scale = 2.0 * qMin( p.length(), q.length() );
|
||||
|
||||
p = p.normalized();
|
||||
q = q.normalized();
|
||||
double dotProduct = p * q;
|
||||
|
||||
if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) )
|
||||
{
|
||||
return QgsVector( 0, 0 );
|
||||
}
|
||||
|
||||
// wonderful nasty hack which has survived through JOSM -> id -> QGIS
|
||||
// to deal with almost-straight segments (angle is closer to 180 than to 90/270).
|
||||
if ( dotProduct < -0.707106781186547 )
|
||||
dotProduct += 1.0;
|
||||
|
||||
QgsVector new_v = p + q;
|
||||
// 0.1 magic number from JOSM implementation - think this is to limit each iterative step
|
||||
return new_v.normalized() * ( 0.1 * dotProduct * scale );
|
||||
}
|
||||
|
||||
QgsLineString* doOrthagonalize( QgsLineString* ring, int iterations, double tolerance, double lowerThreshold, double upperThreshold )
|
||||
{
|
||||
double minScore = DBL_MAX;
|
||||
|
||||
bool isClosed = ring->isClosed();
|
||||
int numPoints = ring->numPoints();
|
||||
|
||||
QScopedPointer< QgsLineString > best( ring->clone() );
|
||||
|
||||
for ( int it = 0; it < iterations; ++it )
|
||||
{
|
||||
QVector< QgsVector > /* yep */ motions;
|
||||
motions.reserve( numPoints );
|
||||
|
||||
// first loop through an calculate all motions
|
||||
QgsPointV2 a;
|
||||
QgsPointV2 b;
|
||||
QgsPointV2 c;
|
||||
for ( int i = 0; i < numPoints; ++i )
|
||||
{
|
||||
if ( isClosed && i == numPoints - 1 )
|
||||
motions << motions.at( 0 ); //closed ring, so last point follows first point motion
|
||||
else if ( !isClosed && ( i == 0 || i == numPoints - 1 ) )
|
||||
{
|
||||
b = ring->pointN( 0 );
|
||||
c = ring->pointN( 1 );
|
||||
motions << QgsVector( 0, 0 ); //non closed line, leave first/last vertex untouched
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( i == 0 )
|
||||
{
|
||||
a = ring->pointN( numPoints - 1 );
|
||||
b = ring->pointN( 0 );
|
||||
}
|
||||
if ( i == numPoints - 1 )
|
||||
c = ring->pointN( 0 );
|
||||
else
|
||||
c = ring->pointN( i + 1 );
|
||||
|
||||
motions << calcMotion( a, b, c, lowerThreshold, upperThreshold );
|
||||
}
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
|
||||
// then apply them
|
||||
for ( int i = 0; i < numPoints; ++i )
|
||||
{
|
||||
ring->setXAt( i, ring->xAt( i ) + motions.at( i ).x() );
|
||||
ring->setYAt( i, ring->yAt( i ) + motions.at( i ).y() );
|
||||
}
|
||||
|
||||
double newScore = squareness( ring, lowerThreshold, upperThreshold );
|
||||
if ( newScore < minScore )
|
||||
{
|
||||
best.reset( ring->clone() );
|
||||
minScore = newScore;
|
||||
}
|
||||
|
||||
if ( minScore < tolerance )
|
||||
break;
|
||||
}
|
||||
|
||||
delete ring;
|
||||
|
||||
return best.take();
|
||||
}
|
||||
|
||||
|
||||
QgsAbstractGeometry* orthagonalizeGeom( const QgsAbstractGeometry* geom, int maxIterations, double tolerance, double lowerThreshold, double upperThreshold )
|
||||
{
|
||||
QScopedPointer< QgsAbstractGeometry > segmentizedCopy;
|
||||
if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
|
||||
{
|
||||
segmentizedCopy.reset( geom->segmentize() );
|
||||
geom = segmentizedCopy.data();
|
||||
}
|
||||
|
||||
if ( QgsWkbTypes::geometryType( geom->wkbType() ) == QgsWkbTypes::LineGeometry )
|
||||
{
|
||||
return doOrthagonalize( static_cast< QgsLineString* >( geom->clone() ),
|
||||
maxIterations, tolerance, lowerThreshold, upperThreshold );
|
||||
}
|
||||
else
|
||||
{
|
||||
// polygon
|
||||
const QgsPolygonV2* polygon = static_cast< const QgsPolygonV2* >( geom );
|
||||
QgsPolygonV2* result = new QgsPolygonV2();
|
||||
|
||||
result->setExteriorRing( doOrthagonalize( static_cast< QgsLineString* >( polygon->exteriorRing()->clone() ),
|
||||
maxIterations, tolerance, lowerThreshold, upperThreshold ) );
|
||||
for ( int i = 0; i < polygon->numInteriorRings(); ++i )
|
||||
{
|
||||
result->addInteriorRing( doOrthagonalize( static_cast< QgsLineString* >( polygon->interiorRing( i )->clone() ),
|
||||
maxIterations, tolerance, lowerThreshold, upperThreshold ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
QgsGeometry QgsInternalGeometryEngine::orthagonalize( double tolerance, int maxIterations, double angleThreshold ) const
|
||||
{
|
||||
if ( !mGeometry || ( QgsWkbTypes::geometryType( mGeometry->wkbType() ) != QgsWkbTypes::LineGeometry
|
||||
&& QgsWkbTypes::geometryType( mGeometry->wkbType() ) != QgsWkbTypes::PolygonGeometry ) )
|
||||
{
|
||||
return QgsGeometry();
|
||||
}
|
||||
|
||||
double lowerThreshold = cos(( 90 - angleThreshold ) * M_PI / 180.00 );
|
||||
double upperThreshold = cos( angleThreshold * M_PI / 180.0 );
|
||||
|
||||
if ( const QgsGeometryCollection* gc = dynamic_cast< const QgsGeometryCollection*>( mGeometry ) )
|
||||
{
|
||||
int numGeom = gc->numGeometries();
|
||||
QList< QgsAbstractGeometry* > geometryList;
|
||||
geometryList.reserve( numGeom );
|
||||
for ( int i = 0; i < numGeom; ++i )
|
||||
{
|
||||
geometryList << orthagonalizeGeom( gc->geometryN( i ), maxIterations, tolerance, lowerThreshold, upperThreshold );
|
||||
}
|
||||
|
||||
QgsGeometry first = QgsGeometry( geometryList.takeAt( 0 ) );
|
||||
Q_FOREACH ( QgsAbstractGeometry* g, geometryList )
|
||||
{
|
||||
first.addPart( g );
|
||||
}
|
||||
return first;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QgsGeometry( orthagonalizeGeom( mGeometry, maxIterations, tolerance, lowerThreshold, upperThreshold ) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,6 +61,16 @@ class QgsInternalGeometryEngine
|
||||
*/
|
||||
QgsGeometry poleOfInaccessibility( double precision, double* distanceFromBoundary = nullptr ) const;
|
||||
|
||||
/**
|
||||
* Attempts to orthagonalize 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
|
||||
* either the vertices are within a specified tolerance of right angles or a set number of maximum
|
||||
* iterations is reached. The angle threshold parameter specifies how close to a right angle or
|
||||
* straight line an angle must be before it is attempted to be straightened.
|
||||
* @note added in QGIS 3.0
|
||||
*/
|
||||
QgsGeometry orthagonalize( double tolerance = 1.0E-8, int maxIterations = 1000, double angleThreshold = 15.0 ) const;
|
||||
|
||||
private:
|
||||
const QgsAbstractGeometry* mGeometry;
|
||||
};
|
||||
|
||||
@ -3668,6 +3668,50 @@ class TestQgsGeometry(unittest.TestCase):
|
||||
self.assertAlmostEqual(width, 4.4884, places=3)
|
||||
self.assertAlmostEqual(height, 6.4420, places=3)
|
||||
|
||||
def testOrthagonalize(self):
|
||||
empty = QgsGeometry()
|
||||
o = empty.orthagonalize()
|
||||
self.assertFalse(o)
|
||||
|
||||
# not a useful geometry
|
||||
point = QgsGeometry.fromWkt('Point(1 2)')
|
||||
o = point.orthagonalize()
|
||||
self.assertFalse(o)
|
||||
|
||||
# polygon
|
||||
polygon = QgsGeometry.fromWkt('Polygon((-0.699 0.892, -0.703 0.405, -0.022 0.361, 0.014 0.851, -0.699 0.892))')
|
||||
o = polygon.orthagonalize()
|
||||
exp = 'Polygon ((-0.69899999999999995 0.89200000000000002, -0.72568713635737736 0.38414056283699533, -0.00900222326098143 0.34648000752227009, 0.01768491457044956 0.85433944198378253, -0.69899999999999995 0.89200000000000002))'
|
||||
result = o.exportToWkt()
|
||||
self.assertTrue(compareWkt(result, exp, 0.00001),
|
||||
"orthagonalize: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
|
||||
|
||||
# polygon with ring
|
||||
polygon = QgsGeometry.fromWkt('Polygon ((-0.698 0.892, -0.702 0.405, -0.022 0.360, 0.014 0.850, -0.698 0.892),(-0.619 0.777, -0.619 0.574, -0.515 0.567, -0.517 0.516, -0.411 0.499, -0.379 0.767, -0.619 0.777),(-0.322 0.506, -0.185 0.735, -0.046 0.428, -0.322 0.506))')
|
||||
o = polygon.orthagonalize()
|
||||
exp = 'Polygon ((-0.69799999999999995 0.89200000000000002, -0.72515703079591087 0.38373993222914216, -0.00901577368860811 0.34547552423418099, 0.01814125858957143 0.85373558928902782, -0.69799999999999995 0.89200000000000002),(-0.61899999999999999 0.77700000000000002, -0.63403125159063511 0.56020458713735533, -0.53071476068518508 0.55304126003523246, -0.5343108192220235 0.5011754225601015, -0.40493624158682306 0.49220537936424585, -0.3863089084840608 0.76086661681561074, -0.61899999999999999 0.77700000000000002),(-0.32200000000000001 0.50600000000000001, -0.185 0.73499999999999999, -0.046 0.42799999999999999, -0.32200000000000001 0.50600000000000001))'
|
||||
result = o.exportToWkt()
|
||||
self.assertTrue(compareWkt(result, exp, 0.00001),
|
||||
"orthagonalize: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
|
||||
|
||||
# multipolygon
|
||||
|
||||
polygon = QgsGeometry.fromWkt('MultiPolygon(((-0.550 -1.553, -0.182 -0.954, -0.182 -0.954, 0.186 -1.538, -0.550 -1.553)),'
|
||||
'((0.506 -1.376, 0.433 -1.081, 0.765 -0.900, 0.923 -1.132, 0.923 -1.391, 0.506 -1.376)))')
|
||||
o = polygon.orthagonalize()
|
||||
exp = 'MultiPolygon (((-0.55000000000000004 -1.55299999999999994, -0.182 -0.95399999999999996, -0.182 -0.95399999999999996, 0.186 -1.53800000000000003, -0.55000000000000004 -1.55299999999999994)),((0.50600000000000001 -1.37599999999999989, 0.34888970623957499 -1.04704644438350125, 0.78332709454235683 -0.83955640656085295, 0.92300000000000004 -1.1319999999999999, 0.91737248858460974 -1.38514497083566535, 0.50600000000000001 -1.37599999999999989)))'
|
||||
result = o.exportToWkt()
|
||||
self.assertTrue(compareWkt(result, exp, 0.00001),
|
||||
"orthagonalize: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
|
||||
|
||||
#line
|
||||
line = QgsGeometry.fromWkt('LineString (-1.07445631048298162 -0.91619958829825165, 0.04022568180912156 -0.95572731852137571, 0.04741254184968957 -0.61794489661467789, 0.68704308546024517 -0.66106605685808595)')
|
||||
o = line.orthagonalize()
|
||||
exp = 'LineString (-1.07445631048298162 -0.91619958829825165, 0.04812855116470245 -0.96433184892270418, 0.06228000950284909 -0.63427853851139493, 0.68704308546024517 -0.66106605685808595)'
|
||||
result = o.exportToWkt()
|
||||
self.assertTrue(compareWkt(result, exp, 0.00001),
|
||||
"orthagonalize: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user