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