1 // SPDX-License-Identifier: LGPL-2.1-or-later 2 // 3 // SPDX-FileCopyrightText: 2008-2009 Patrick Spendrin <ps_ml@gmx.de> 4 // SPDX-FileCopyrightText: 2008 Inge Wallin <inge@lysator.liu.se> 5 // 6 7 8 #include "GeoDataPolygon.h" 9 #include "GeoDataPolygon_p.h" 10 #include "GeoDataCoordinates.h" 11 #include "GeoDataTypes.h" 12 #include "MarbleDebug.h" 13 14 #include <algorithm> 15 #include <QDataStream> 16 17 18 namespace Marble 19 { 20 GeoDataPolygon(TessellationFlags f)21GeoDataPolygon::GeoDataPolygon( TessellationFlags f ) 22 : GeoDataGeometry( new GeoDataPolygonPrivate( f ) ) 23 { 24 // nothing to do 25 } 26 GeoDataPolygon(const GeoDataGeometry & other)27GeoDataPolygon::GeoDataPolygon( const GeoDataGeometry & other ) 28 : GeoDataGeometry( other ) 29 { 30 // nothing to do 31 } 32 ~GeoDataPolygon()33GeoDataPolygon::~GeoDataPolygon() 34 { 35 #ifdef DEBUG_GEODATA 36 mDebug() << "delete polygon"; 37 #endif 38 } 39 nodeType() const40const char *GeoDataPolygon::nodeType() const 41 { 42 return GeoDataTypes::GeoDataPolygonType; 43 } 44 geometryId() const45EnumGeometryId GeoDataPolygon::geometryId() const 46 { 47 return GeoDataPolygonId; 48 } 49 copy() const50GeoDataGeometry *GeoDataPolygon::copy() const 51 { 52 return new GeoDataPolygon(*this); 53 } 54 operator ==(const GeoDataPolygon & other) const55bool GeoDataPolygon::operator==( const GeoDataPolygon &other ) const 56 { 57 Q_D(const GeoDataPolygon); 58 const GeoDataPolygonPrivate *other_d = other.d_func(); 59 60 if ( !GeoDataGeometry::equals(other) || 61 tessellate() != other.tessellate() || 62 isClosed() != other.isClosed() || 63 d->inner.size() != other_d->inner.size() || 64 d->outer != other_d->outer ) { 65 return false; 66 } 67 68 QVector<GeoDataLinearRing>::const_iterator itBound = d->inner.constBegin(); 69 QVector<GeoDataLinearRing>::const_iterator itEnd = d->inner.constEnd(); 70 QVector<GeoDataLinearRing>::const_iterator otherItBound = other_d->inner.constBegin(); 71 QVector<GeoDataLinearRing>::const_iterator otherItEnd= other_d->inner.constEnd(); 72 73 for ( ; itBound != itEnd && otherItBound != otherItEnd; ++itBound, ++otherItBound ) { 74 if ( *itBound != *otherItBound) { 75 return false; 76 } 77 } 78 79 Q_ASSERT ( itBound == itEnd && otherItBound == otherItEnd ); 80 return true; 81 } 82 operator !=(const GeoDataPolygon & other) const83bool GeoDataPolygon::operator!=( const GeoDataPolygon &other ) const 84 { 85 return !this->operator==(other); 86 } 87 isClosed() const88bool GeoDataPolygon::isClosed() const 89 { 90 return true; 91 } 92 tessellate() const93bool GeoDataPolygon::tessellate() const 94 { 95 Q_D(const GeoDataPolygon); 96 return d->m_tessellationFlags.testFlag(Tessellate); 97 } 98 setTessellate(bool tessellate)99void GeoDataPolygon::setTessellate( bool tessellate ) 100 { 101 // According to the KML reference the tesselation is done along great circles 102 // for polygons in Google Earth. Our "Tesselate" flag does this. 103 // Only for pure line strings and linear rings the 104 // latitude circles are followed for subsequent points that share the same latitude. 105 detach(); 106 107 Q_D(GeoDataPolygon); 108 if ( tessellate ) { 109 d->m_tessellationFlags |= Tessellate; 110 } else { 111 d->m_tessellationFlags ^= Tessellate; 112 } 113 } 114 tessellationFlags() const115TessellationFlags GeoDataPolygon::tessellationFlags() const 116 { 117 Q_D(const GeoDataPolygon); 118 return d->m_tessellationFlags; 119 } 120 setTessellationFlags(TessellationFlags f)121void GeoDataPolygon::setTessellationFlags( TessellationFlags f ) 122 { 123 detach(); 124 125 Q_D(GeoDataPolygon); 126 d->m_tessellationFlags = f; 127 } 128 latLonAltBox() const129const GeoDataLatLonAltBox& GeoDataPolygon::latLonAltBox() const 130 { 131 Q_D(const GeoDataPolygon); 132 return d->outer.latLonAltBox(); 133 } 134 outerBoundary()135GeoDataLinearRing &GeoDataPolygon::outerBoundary() 136 { 137 detach(); 138 139 Q_D(GeoDataPolygon); 140 return (d->outer); 141 } 142 outerBoundary() const143const GeoDataLinearRing &GeoDataPolygon::outerBoundary() const 144 { 145 Q_D(const GeoDataPolygon); 146 return d->outer; 147 } 148 setOuterBoundary(const GeoDataLinearRing & boundary)149void GeoDataPolygon::setOuterBoundary( const GeoDataLinearRing& boundary ) 150 { 151 detach(); 152 153 Q_D(GeoDataPolygon); 154 d->outer = boundary; 155 } 156 innerBoundaries()157QVector<GeoDataLinearRing>& GeoDataPolygon::innerBoundaries() 158 { 159 detach(); 160 161 Q_D(GeoDataPolygon); 162 return d->inner; 163 } 164 innerBoundaries() const165const QVector<GeoDataLinearRing>& GeoDataPolygon::innerBoundaries() const 166 { 167 Q_D(const GeoDataPolygon); 168 return d->inner; 169 } 170 appendInnerBoundary(const GeoDataLinearRing & boundary)171void GeoDataPolygon::appendInnerBoundary( const GeoDataLinearRing& boundary ) 172 { 173 detach(); 174 175 Q_D(GeoDataPolygon); 176 d->inner.append(boundary); 177 } 178 setRenderOrder(int renderOrder)179void GeoDataPolygon::setRenderOrder(int renderOrder) 180 { 181 detach(); 182 183 Q_D(GeoDataPolygon); 184 d->m_renderOrder = renderOrder; 185 } 186 renderOrder() const187int GeoDataPolygon::renderOrder() const 188 { 189 Q_D(const GeoDataPolygon); 190 return d->m_renderOrder; 191 } 192 pack(QDataStream & stream) const193void GeoDataPolygon::pack( QDataStream& stream ) const 194 { 195 Q_D(const GeoDataPolygon); 196 197 GeoDataObject::pack( stream ); 198 199 d->outer.pack( stream ); 200 201 stream << d->inner.size(); 202 stream << (qint32)(d->m_tessellationFlags); 203 204 for( QVector<GeoDataLinearRing>::const_iterator iterator 205 = d->inner.constBegin(); 206 iterator != d->inner.constEnd(); 207 ++iterator ) { 208 mDebug() << "innerRing: size" << d->inner.size(); 209 GeoDataLinearRing linearRing = ( *iterator ); 210 linearRing.pack( stream ); 211 } 212 } 213 unpack(QDataStream & stream)214void GeoDataPolygon::unpack( QDataStream& stream ) 215 { 216 detach(); 217 218 Q_D(GeoDataPolygon); 219 220 GeoDataObject::unpack( stream ); 221 222 d->outer.unpack( stream ); 223 224 qint32 size; 225 qint32 tessellationFlags; 226 227 stream >> size; 228 stream >> tessellationFlags; 229 230 d->m_tessellationFlags = (TessellationFlags)(tessellationFlags); 231 232 QVector<GeoDataLinearRing> &inner = d->inner; 233 inner.reserve(inner.size() + size); 234 for(qint32 i = 0; i < size; i++ ) { 235 GeoDataLinearRing linearRing; 236 linearRing.unpack( stream ); 237 inner.append(linearRing); 238 } 239 } 240 contains(const GeoDataCoordinates & coordinates) const241bool GeoDataPolygon::contains( const GeoDataCoordinates &coordinates ) const 242 { 243 if ( !outerBoundary().contains( coordinates ) ) { 244 // Not inside the polygon at all 245 return false; 246 } 247 248 for( const GeoDataLinearRing &ring: innerBoundaries() ) { 249 if ( ring.contains( coordinates ) ) { 250 // Inside the polygon, but in one of its holes 251 return false; 252 } 253 } 254 255 return true; 256 } 257 258 } 259