1 /***************************************************************************
2 qgspolygon.cpp
3 ----------------
4 begin : September 2014
5 copyright : (C) 2014 by Marco Hugentobler
6 email : marco at sourcepole dot ch
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgspolygon.h"
19 #include "qgsapplication.h"
20 #include "qgsgeometryutils.h"
21 #include "qgslinestring.h"
22 #include "qgsmultilinestring.h"
23 #include "qgswkbptr.h"
24
QgsPolygon()25 QgsPolygon::QgsPolygon()
26 {
27 mWkbType = QgsWkbTypes::Polygon;
28 }
29
30 ///@cond DOXYGEN_SHUTTUP
QgsPolygon(QgsLineString * exterior,const QList<QgsLineString * > & rings)31 QgsPolygon::QgsPolygon( QgsLineString *exterior, const QList<QgsLineString *> &rings )
32 {
33 setExteriorRing( exterior );
34 for ( QgsLineString *ring : rings )
35 {
36 addInteriorRing( ring );
37 }
38 clearCache();
39 }
40 ///@endcond
41
geometryType() const42 QString QgsPolygon::geometryType() const
43 {
44 return QStringLiteral( "Polygon" );
45 }
46
createEmptyWithSameType() const47 QgsPolygon *QgsPolygon::createEmptyWithSameType() const
48 {
49 auto result = std::make_unique< QgsPolygon >();
50 result->mWkbType = mWkbType;
51 return result.release();
52 }
53
clone() const54 QgsPolygon *QgsPolygon::clone() const
55 {
56 return new QgsPolygon( *this );
57 }
58
clear()59 void QgsPolygon::clear()
60 {
61 QgsCurvePolygon::clear();
62 mWkbType = QgsWkbTypes::Polygon;
63 }
64
fromWkb(QgsConstWkbPtr & wkbPtr)65 bool QgsPolygon::fromWkb( QgsConstWkbPtr &wkbPtr )
66 {
67 clear();
68 if ( !wkbPtr )
69 {
70 return false;
71 }
72
73 QgsWkbTypes::Type type = wkbPtr.readHeader();
74 if ( QgsWkbTypes::flatType( type ) != QgsWkbTypes::Polygon )
75 {
76 return false;
77 }
78 mWkbType = type;
79
80 QgsWkbTypes::Type ringType;
81 switch ( mWkbType )
82 {
83 case QgsWkbTypes::PolygonZ:
84 ringType = QgsWkbTypes::LineStringZ;
85 break;
86 case QgsWkbTypes::PolygonM:
87 ringType = QgsWkbTypes::LineStringM;
88 break;
89 case QgsWkbTypes::PolygonZM:
90 ringType = QgsWkbTypes::LineStringZM;
91 break;
92 case QgsWkbTypes::Polygon25D:
93 ringType = QgsWkbTypes::LineString25D;
94 break;
95 default:
96 ringType = QgsWkbTypes::LineString;
97 break;
98 }
99
100 int nRings;
101 wkbPtr >> nRings;
102 for ( int i = 0; i < nRings; ++i )
103 {
104 std::unique_ptr< QgsLineString > line( new QgsLineString() );
105 line->fromWkbPoints( ringType, wkbPtr );
106 /*if ( !line->isRing() )
107 {
108 delete line; continue;
109 }*/
110
111 if ( !mExteriorRing )
112 {
113 mExteriorRing = std::move( line );
114 }
115 else
116 {
117 mInteriorRings.append( line.release() );
118 }
119 }
120
121 return true;
122 }
123
wkbSize(QgsAbstractGeometry::WkbFlags) const124 int QgsPolygon::wkbSize( QgsAbstractGeometry::WkbFlags ) const
125 {
126 int binarySize = sizeof( char ) + sizeof( quint32 ) + sizeof( quint32 );
127
128 // Endianness and WkbType is not stored for LinearRings
129 if ( mExteriorRing )
130 {
131 binarySize += sizeof( quint32 ) + mExteriorRing->numPoints() * ( 2 + mExteriorRing->is3D() + mExteriorRing->isMeasure() ) * sizeof( double );
132 }
133 for ( const QgsCurve *curve : mInteriorRings )
134 {
135 binarySize += sizeof( quint32 ) + curve->numPoints() * ( 2 + curve->is3D() + curve->isMeasure() ) * sizeof( double );
136 }
137
138 return binarySize;
139 }
140
asWkb(QgsAbstractGeometry::WkbFlags flags) const141 QByteArray QgsPolygon::asWkb( QgsAbstractGeometry::WkbFlags flags ) const
142 {
143 QByteArray wkbArray;
144 wkbArray.resize( QgsPolygon::wkbSize() );
145 QgsWkbPtr wkb( wkbArray );
146 wkb << static_cast<char>( QgsApplication::endian() );
147
148 QgsWkbTypes::Type type = wkbType();
149 if ( flags & FlagExportTrianglesAsPolygons )
150 {
151 switch ( type )
152 {
153 case QgsWkbTypes::Triangle:
154 type = QgsWkbTypes::Polygon;
155 break;
156 case QgsWkbTypes::TriangleZ:
157 type = QgsWkbTypes::PolygonZ;
158 break;
159 case QgsWkbTypes::TriangleM:
160 type = QgsWkbTypes::PolygonM;
161 break;
162 case QgsWkbTypes::TriangleZM:
163 type = QgsWkbTypes::PolygonZM;
164 break;
165 default:
166 break;
167 }
168 }
169 wkb << static_cast<quint32>( type );
170
171 wkb << static_cast<quint32>( ( nullptr != mExteriorRing ) + mInteriorRings.size() );
172 if ( mExteriorRing )
173 {
174 QgsPointSequence pts;
175 mExteriorRing->points( pts );
176 QgsGeometryUtils::pointsToWKB( wkb, pts, mExteriorRing->is3D(), mExteriorRing->isMeasure() );
177 }
178 for ( const QgsCurve *curve : mInteriorRings )
179 {
180 QgsPointSequence pts;
181 curve->points( pts );
182 QgsGeometryUtils::pointsToWKB( wkb, pts, curve->is3D(), curve->isMeasure() );
183 }
184
185 return wkbArray;
186 }
187
addInteriorRing(QgsCurve * ring)188 void QgsPolygon::addInteriorRing( QgsCurve *ring )
189 {
190 if ( !ring )
191 return;
192
193 if ( ring->hasCurvedSegments() )
194 {
195 //can't add a curved ring to a QgsPolygonV2
196 QgsLineString *segmented = ring->curveToLine();
197 delete ring;
198 ring = segmented;
199 }
200
201 QgsLineString *lineString = qgsgeometry_cast< QgsLineString *>( ring );
202 if ( lineString && !lineString->isClosed() )
203 {
204 lineString->close();
205 }
206
207 if ( mWkbType == QgsWkbTypes::Polygon25D )
208 {
209 ring->convertTo( QgsWkbTypes::LineString25D );
210 mInteriorRings.append( ring );
211 }
212 else
213 {
214 QgsCurvePolygon::addInteriorRing( ring );
215 }
216 clearCache();
217 }
218
setExteriorRing(QgsCurve * ring)219 void QgsPolygon::setExteriorRing( QgsCurve *ring )
220 {
221 if ( !ring )
222 {
223 return;
224 }
225
226 if ( ring->hasCurvedSegments() )
227 {
228 //need to segmentize ring as polygon does not support curves
229 QgsCurve *line = ring->segmentize();
230 delete ring;
231 ring = line;
232 }
233
234 QgsLineString *lineString = qgsgeometry_cast< QgsLineString *>( ring );
235 if ( lineString && !lineString->isClosed() )
236 {
237 lineString->close();
238 }
239
240 mExteriorRing.reset( ring );
241
242 //set proper wkb type
243 setZMTypeFromSubGeometry( ring, QgsWkbTypes::Polygon );
244
245 //match dimensionality for rings
246 for ( QgsCurve *ring : std::as_const( mInteriorRings ) )
247 {
248 ring->convertTo( mExteriorRing->wkbType() );
249 }
250
251 clearCache();
252 }
253
boundary() const254 QgsAbstractGeometry *QgsPolygon::boundary() const
255 {
256 if ( !mExteriorRing )
257 return nullptr;
258
259 if ( mInteriorRings.isEmpty() )
260 {
261 return mExteriorRing->clone();
262 }
263 else
264 {
265 QgsMultiLineString *multiLine = new QgsMultiLineString();
266 int nInteriorRings = mInteriorRings.size();
267 multiLine->reserve( nInteriorRings + 1 );
268 multiLine->addGeometry( mExteriorRing->clone() );
269 for ( int i = 0; i < nInteriorRings; ++i )
270 {
271 multiLine->addGeometry( mInteriorRings.at( i )->clone() );
272 }
273 return multiLine;
274 }
275 }
276
pointDistanceToBoundary(double x,double y) const277 double QgsPolygon::pointDistanceToBoundary( double x, double y ) const
278 {
279 if ( !mExteriorRing )
280 return std::numeric_limits< double >::quiet_NaN();
281
282 bool inside = false;
283 double minimumDistance = std::numeric_limits<double>::max();
284 double minDistX = 0.0;
285 double minDistY = 0.0;
286
287 int numRings = mInteriorRings.size() + 1;
288 for ( int ringIndex = 0; ringIndex < numRings; ++ringIndex )
289 {
290 const QgsLineString *ring = static_cast< const QgsLineString * >( ringIndex == 0 ? mExteriorRing.get() : mInteriorRings.at( ringIndex - 1 ) );
291
292 int len = ring->numPoints() - 1; //assume closed
293 for ( int i = 0, j = len - 1; i < len; j = i++ )
294 {
295 double aX = ring->xAt( i );
296 double aY = ring->yAt( i );
297 double bX = ring->xAt( j );
298 double bY = ring->yAt( j );
299
300 if ( ( ( aY > y ) != ( bY > y ) ) &&
301 ( x < ( bX - aX ) * ( y - aY ) / ( bY - aY ) + aX ) )
302 inside = !inside;
303
304 minimumDistance = std::min( minimumDistance, QgsGeometryUtils::sqrDistToLine( x, y, aX, aY, bX, bY, minDistX, minDistY, 4 * std::numeric_limits<double>::epsilon() ) );
305 }
306 }
307
308 return ( inside ? 1 : -1 ) * std::sqrt( minimumDistance );
309 }
310
surfaceToPolygon() const311 QgsPolygon *QgsPolygon::surfaceToPolygon() const
312 {
313 return clone();
314 }
315
toCurveType() const316 QgsCurvePolygon *QgsPolygon::toCurveType() const
317 {
318 QgsCurvePolygon *curvePolygon = new QgsCurvePolygon();
319 curvePolygon->setExteriorRing( mExteriorRing->clone() );
320 int nInteriorRings = mInteriorRings.size();
321 for ( int i = 0; i < nInteriorRings; ++i )
322 {
323 curvePolygon->addInteriorRing( mInteriorRings.at( i )->clone() );
324 }
325 return curvePolygon;
326 }
327