File indexing completed on 2025-01-05 03:58:59
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2008-2009 Patrick Spendrin <ps_ml@gmx.de> 0004 // SPDX-FileCopyrightText: 2008 Inge Wallin <inge@lysator.liu.se> 0005 // 0006 0007 0008 #include "GeoDataPolygon.h" 0009 #include "GeoDataPolygon_p.h" 0010 #include "GeoDataCoordinates.h" 0011 #include "GeoDataTypes.h" 0012 #include "digikam_debug.h" 0013 0014 #include <algorithm> 0015 #include <QDataStream> 0016 0017 0018 namespace Marble 0019 { 0020 0021 GeoDataPolygon::GeoDataPolygon( TessellationFlags f ) 0022 : GeoDataGeometry( new GeoDataPolygonPrivate( f ) ) 0023 { 0024 // nothing to do 0025 } 0026 0027 GeoDataPolygon::GeoDataPolygon( const GeoDataGeometry & other ) 0028 : GeoDataGeometry( other ) 0029 { 0030 // nothing to do 0031 } 0032 0033 GeoDataPolygon::~GeoDataPolygon() 0034 { 0035 #ifdef DEBUG_GEODATA 0036 qCDebug(DIGIKAM_MARBLE_LOG) << "delete polygon"; 0037 #endif 0038 } 0039 0040 const char *GeoDataPolygon::nodeType() const 0041 { 0042 return GeoDataTypes::GeoDataPolygonType; 0043 } 0044 0045 EnumGeometryId GeoDataPolygon::geometryId() const 0046 { 0047 return GeoDataPolygonId; 0048 } 0049 0050 GeoDataGeometry *GeoDataPolygon::copy() const 0051 { 0052 return new GeoDataPolygon(*this); 0053 } 0054 0055 bool GeoDataPolygon::operator==( const GeoDataPolygon &other ) const 0056 { 0057 Q_D(const GeoDataPolygon); 0058 const GeoDataPolygonPrivate *other_d = other.d_func(); 0059 0060 if ( !GeoDataGeometry::equals(other) || 0061 tessellate() != other.tessellate() || 0062 isClosed() != other.isClosed() || 0063 d->inner.size() != other_d->inner.size() || 0064 d->outer != other_d->outer ) { 0065 return false; 0066 } 0067 0068 QVector<GeoDataLinearRing>::const_iterator itBound = d->inner.constBegin(); 0069 QVector<GeoDataLinearRing>::const_iterator itEnd = d->inner.constEnd(); 0070 QVector<GeoDataLinearRing>::const_iterator otherItBound = other_d->inner.constBegin(); 0071 QVector<GeoDataLinearRing>::const_iterator otherItEnd= other_d->inner.constEnd(); 0072 0073 for ( ; itBound != itEnd && otherItBound != otherItEnd; ++itBound, ++otherItBound ) { 0074 if ( *itBound != *otherItBound) { 0075 return false; 0076 } 0077 } 0078 0079 Q_ASSERT ( itBound == itEnd && otherItBound == otherItEnd ); 0080 return true; 0081 } 0082 0083 bool GeoDataPolygon::operator!=( const GeoDataPolygon &other ) const 0084 { 0085 return !this->operator==(other); 0086 } 0087 0088 bool GeoDataPolygon::isClosed() const 0089 { 0090 return true; 0091 } 0092 0093 bool GeoDataPolygon::tessellate() const 0094 { 0095 Q_D(const GeoDataPolygon); 0096 return d->m_tessellationFlags.testFlag(Tessellate); 0097 } 0098 0099 void GeoDataPolygon::setTessellate( bool tessellate ) 0100 { 0101 // According to the KML reference the tesselation is done along great circles 0102 // for polygons in Google Earth. Our "Tesselate" flag does this. 0103 // Only for pure line strings and linear rings the 0104 // latitude circles are followed for subsequent points that share the same latitude. 0105 detach(); 0106 0107 Q_D(GeoDataPolygon); 0108 if ( tessellate ) { 0109 d->m_tessellationFlags |= Tessellate; 0110 } else { 0111 d->m_tessellationFlags ^= Tessellate; 0112 } 0113 } 0114 0115 TessellationFlags GeoDataPolygon::tessellationFlags() const 0116 { 0117 Q_D(const GeoDataPolygon); 0118 return d->m_tessellationFlags; 0119 } 0120 0121 void GeoDataPolygon::setTessellationFlags( TessellationFlags f ) 0122 { 0123 detach(); 0124 0125 Q_D(GeoDataPolygon); 0126 d->m_tessellationFlags = f; 0127 } 0128 0129 const GeoDataLatLonAltBox& GeoDataPolygon::latLonAltBox() const 0130 { 0131 Q_D(const GeoDataPolygon); 0132 return d->outer.latLonAltBox(); 0133 } 0134 0135 GeoDataLinearRing &GeoDataPolygon::outerBoundary() 0136 { 0137 detach(); 0138 0139 Q_D(GeoDataPolygon); 0140 return (d->outer); 0141 } 0142 0143 const GeoDataLinearRing &GeoDataPolygon::outerBoundary() const 0144 { 0145 Q_D(const GeoDataPolygon); 0146 return d->outer; 0147 } 0148 0149 void GeoDataPolygon::setOuterBoundary( const GeoDataLinearRing& boundary ) 0150 { 0151 detach(); 0152 0153 Q_D(GeoDataPolygon); 0154 d->outer = boundary; 0155 } 0156 0157 QVector<GeoDataLinearRing>& GeoDataPolygon::innerBoundaries() 0158 { 0159 detach(); 0160 0161 Q_D(GeoDataPolygon); 0162 return d->inner; 0163 } 0164 0165 const QVector<GeoDataLinearRing>& GeoDataPolygon::innerBoundaries() const 0166 { 0167 Q_D(const GeoDataPolygon); 0168 return d->inner; 0169 } 0170 0171 void GeoDataPolygon::appendInnerBoundary( const GeoDataLinearRing& boundary ) 0172 { 0173 detach(); 0174 0175 Q_D(GeoDataPolygon); 0176 d->inner.append(boundary); 0177 } 0178 0179 void GeoDataPolygon::setRenderOrder(int renderOrder) 0180 { 0181 detach(); 0182 0183 Q_D(GeoDataPolygon); 0184 d->m_renderOrder = renderOrder; 0185 } 0186 0187 int GeoDataPolygon::renderOrder() const 0188 { 0189 Q_D(const GeoDataPolygon); 0190 return d->m_renderOrder; 0191 } 0192 0193 void GeoDataPolygon::pack( QDataStream& stream ) const 0194 { 0195 Q_D(const GeoDataPolygon); 0196 0197 GeoDataObject::pack( stream ); 0198 0199 d->outer.pack( stream ); 0200 0201 stream << d->inner.size(); 0202 stream << (qint32)(d->m_tessellationFlags); 0203 0204 for( QVector<GeoDataLinearRing>::const_iterator iterator 0205 = d->inner.constBegin(); 0206 iterator != d->inner.constEnd(); 0207 ++iterator ) { 0208 qCDebug(DIGIKAM_MARBLE_LOG) << "innerRing: size" << d->inner.size(); 0209 GeoDataLinearRing linearRing = ( *iterator ); 0210 linearRing.pack( stream ); 0211 } 0212 } 0213 0214 void GeoDataPolygon::unpack( QDataStream& stream ) 0215 { 0216 detach(); 0217 0218 Q_D(GeoDataPolygon); 0219 0220 GeoDataObject::unpack( stream ); 0221 0222 d->outer.unpack( stream ); 0223 0224 qint32 size; 0225 qint32 tessellationFlags; 0226 0227 stream >> size; 0228 stream >> tessellationFlags; 0229 0230 d->m_tessellationFlags = (TessellationFlags)(tessellationFlags); 0231 0232 QVector<GeoDataLinearRing> &inner = d->inner; 0233 inner.reserve(inner.size() + size); 0234 for(qint32 i = 0; i < size; i++ ) { 0235 GeoDataLinearRing linearRing; 0236 linearRing.unpack( stream ); 0237 inner.append(linearRing); 0238 } 0239 } 0240 0241 bool GeoDataPolygon::contains( const GeoDataCoordinates &coordinates ) const 0242 { 0243 if ( !outerBoundary().contains( coordinates ) ) { 0244 // Not inside the polygon at all 0245 return false; 0246 } 0247 0248 for( const GeoDataLinearRing &ring: innerBoundaries() ) { 0249 if ( ring.contains( coordinates ) ) { 0250 // Inside the polygon, but in one of its holes 0251 return false; 0252 } 0253 } 0254 0255 return true; 0256 } 0257 0258 }