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)21 GeoDataPolygon::GeoDataPolygon( TessellationFlags f )
22     : GeoDataGeometry( new GeoDataPolygonPrivate( f ) )
23 {
24     // nothing to do
25 }
26 
GeoDataPolygon(const GeoDataGeometry & other)27 GeoDataPolygon::GeoDataPolygon( const GeoDataGeometry & other )
28     : GeoDataGeometry( other )
29 {
30     // nothing to do
31 }
32 
~GeoDataPolygon()33 GeoDataPolygon::~GeoDataPolygon()
34 {
35 #ifdef DEBUG_GEODATA
36     mDebug() << "delete polygon";
37 #endif
38 }
39 
nodeType() const40 const char *GeoDataPolygon::nodeType() const
41 {
42     return GeoDataTypes::GeoDataPolygonType;
43 }
44 
geometryId() const45 EnumGeometryId GeoDataPolygon::geometryId() const
46 {
47     return GeoDataPolygonId;
48 }
49 
copy() const50 GeoDataGeometry *GeoDataPolygon::copy() const
51 {
52     return new GeoDataPolygon(*this);
53 }
54 
operator ==(const GeoDataPolygon & other) const55 bool 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) const83 bool GeoDataPolygon::operator!=( const GeoDataPolygon &other ) const
84 {
85     return !this->operator==(other);
86 }
87 
isClosed() const88 bool GeoDataPolygon::isClosed() const
89 {
90     return true;
91 }
92 
tessellate() const93 bool GeoDataPolygon::tessellate() const
94 {
95     Q_D(const GeoDataPolygon);
96     return d->m_tessellationFlags.testFlag(Tessellate);
97 }
98 
setTessellate(bool tessellate)99 void 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() const115 TessellationFlags GeoDataPolygon::tessellationFlags() const
116 {
117     Q_D(const GeoDataPolygon);
118     return d->m_tessellationFlags;
119 }
120 
setTessellationFlags(TessellationFlags f)121 void GeoDataPolygon::setTessellationFlags( TessellationFlags f )
122 {
123     detach();
124 
125     Q_D(GeoDataPolygon);
126     d->m_tessellationFlags = f;
127 }
128 
latLonAltBox() const129 const GeoDataLatLonAltBox& GeoDataPolygon::latLonAltBox() const
130 {
131     Q_D(const GeoDataPolygon);
132     return d->outer.latLonAltBox();
133 }
134 
outerBoundary()135 GeoDataLinearRing &GeoDataPolygon::outerBoundary()
136 {
137     detach();
138 
139     Q_D(GeoDataPolygon);
140     return (d->outer);
141 }
142 
outerBoundary() const143 const GeoDataLinearRing &GeoDataPolygon::outerBoundary() const
144 {
145     Q_D(const GeoDataPolygon);
146     return d->outer;
147 }
148 
setOuterBoundary(const GeoDataLinearRing & boundary)149 void GeoDataPolygon::setOuterBoundary( const GeoDataLinearRing& boundary )
150 {
151     detach();
152 
153     Q_D(GeoDataPolygon);
154     d->outer = boundary;
155 }
156 
innerBoundaries()157 QVector<GeoDataLinearRing>& GeoDataPolygon::innerBoundaries()
158 {
159     detach();
160 
161     Q_D(GeoDataPolygon);
162     return d->inner;
163 }
164 
innerBoundaries() const165 const QVector<GeoDataLinearRing>& GeoDataPolygon::innerBoundaries() const
166 {
167     Q_D(const GeoDataPolygon);
168     return d->inner;
169 }
170 
appendInnerBoundary(const GeoDataLinearRing & boundary)171 void GeoDataPolygon::appendInnerBoundary( const GeoDataLinearRing& boundary )
172 {
173     detach();
174 
175     Q_D(GeoDataPolygon);
176     d->inner.append(boundary);
177 }
178 
setRenderOrder(int renderOrder)179 void GeoDataPolygon::setRenderOrder(int renderOrder)
180 {
181     detach();
182 
183     Q_D(GeoDataPolygon);
184     d->m_renderOrder = renderOrder;
185 }
186 
renderOrder() const187 int GeoDataPolygon::renderOrder() const
188 {
189     Q_D(const GeoDataPolygon);
190     return d->m_renderOrder;
191 }
192 
pack(QDataStream & stream) const193 void 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)214 void 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) const241 bool 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