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