File indexing completed on 2024-05-12 03:50:17

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 "MarbleDebug.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     mDebug() << "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         mDebug() << "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 }