1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2007 Murad Tagirov <tmurad@gmail.com>
4 // SPDX-FileCopyrightText: 2009 Patrick Spendrin <ps_ml@gmx.de>
5 //
6 
7 
8 #include "GeoDataFeature.h"
9 #include "GeoDataFeature_p.h"
10 
11 #include <QDataStream>
12 #include <QSize>
13 
14 #include "MarbleDirs.h"
15 #include "MarbleDebug.h"
16 
17 #include "GeoDataStyle.h"
18 #include "GeoDataStyleMap.h"
19 
20 #include "GeoDataContainer.h"
21 #include "GeoDataDocument.h"
22 #include "GeoDataFolder.h"
23 #include "GeoDataGroundOverlay.h"
24 #include "GeoDataNetworkLink.h"
25 #include "GeoDataNetworkLinkControl.h"
26 #include "GeoDataPhotoOverlay.h"
27 #include "GeoDataPlacemark.h"
28 #include "GeoDataScreenOverlay.h"
29 #include "GeoDataTour.h"
30 #include "GeoDataRegion.h"
31 #include "GeoDataCamera.h"
32 
33 namespace Marble
34 {
35 
36 const QSharedPointer<const GeoDataStyle> GeoDataFeaturePrivate::s_defaultStyle(new GeoDataStyle);
37 
GeoDataFeature()38 GeoDataFeature::GeoDataFeature()
39     : d_ptr(new GeoDataFeaturePrivate())
40 {
41 }
42 
GeoDataFeature(const GeoDataFeature & other)43 GeoDataFeature::GeoDataFeature( const GeoDataFeature& other )
44     : GeoDataObject(),
45       d_ptr(new GeoDataFeaturePrivate(*other.d_ptr))
46 {
47 }
48 
GeoDataFeature(const QString & name)49 GeoDataFeature::GeoDataFeature( const QString& name )
50     : d_ptr(new GeoDataFeaturePrivate())
51 {
52     d_ptr->m_name = name;
53 }
54 
GeoDataFeature(GeoDataFeaturePrivate * dd)55 GeoDataFeature::GeoDataFeature(GeoDataFeaturePrivate *dd)
56     : GeoDataObject(),
57       d_ptr(dd)
58 {
59 }
60 
GeoDataFeature(const GeoDataFeature & other,GeoDataFeaturePrivate * dd)61 GeoDataFeature::GeoDataFeature(const GeoDataFeature& other, GeoDataFeaturePrivate *dd)
62     : GeoDataObject(),
63       d_ptr(dd)
64 {
65     Q_UNUSED(other);
66     // TODO: some classes pass "other" on and thus get duplicated id, also in operator=. Align behaviour
67 }
68 
~GeoDataFeature()69 GeoDataFeature::~GeoDataFeature()
70 {
71     delete d_ptr;
72 }
73 
operator =(const GeoDataFeature & other)74 GeoDataFeature& GeoDataFeature::operator=( const GeoDataFeature& other )
75 {
76     if (this != &other) {
77         *d_ptr = *other.d_ptr;
78     }
79 
80     return *this;
81 }
82 
operator ==(const GeoDataFeature & other) const83 bool GeoDataFeature::operator==(const GeoDataFeature &other) const
84 {
85     if (nodeType() != other.nodeType()) {
86         return false;
87     }
88 
89     if (nodeType() == GeoDataTypes::GeoDataDocumentType) {
90         const GeoDataDocument &thisDoc = static_cast<const GeoDataDocument &>(*this);
91         const GeoDataDocument &otherDoc = static_cast<const GeoDataDocument &>(other);
92 
93         return thisDoc == otherDoc;
94     } else if (nodeType() == GeoDataTypes::GeoDataFolderType) {
95         const GeoDataFolder &thisFolder = static_cast<const GeoDataFolder &>(*this);
96         const GeoDataFolder &otherFolder = static_cast<const GeoDataFolder &>(other);
97 
98         return thisFolder == otherFolder;
99     } else if (nodeType() == GeoDataTypes::GeoDataGroundOverlayType) {
100         const GeoDataGroundOverlay &thisGO = static_cast<const GeoDataGroundOverlay &>(*this);
101         const GeoDataGroundOverlay &otherGO = static_cast<const GeoDataGroundOverlay &>(other);
102 
103         return thisGO == otherGO;
104     } else if (nodeType() == GeoDataTypes::GeoDataNetworkLinkType) {
105         const GeoDataNetworkLink &thisNetLink = static_cast<const GeoDataNetworkLink &>(*this);
106         const GeoDataNetworkLink &otherNetLink = static_cast<const GeoDataNetworkLink &>(other);
107 
108         return thisNetLink == otherNetLink;
109     } else if (nodeType() == GeoDataTypes::GeoDataNetworkLinkControlType) {
110         const GeoDataNetworkLinkControl &thisNLC = static_cast<const GeoDataNetworkLinkControl &>(*this);
111         const GeoDataNetworkLinkControl &otherNLC = static_cast<const GeoDataNetworkLinkControl &>(other);
112 
113         return thisNLC == otherNLC;
114     } else if (nodeType() == GeoDataTypes::GeoDataPhotoOverlayType) {
115         const GeoDataPhotoOverlay &thisPO = static_cast<const GeoDataPhotoOverlay &>(*this);
116         const GeoDataPhotoOverlay &otherPO = static_cast<const GeoDataPhotoOverlay &>(other);
117 
118         return thisPO == otherPO;
119     } else if (nodeType() == GeoDataTypes::GeoDataPlacemarkType) {
120         const GeoDataPlacemark &thisPM = static_cast<const GeoDataPlacemark &>(*this);
121         const GeoDataPlacemark &otherPM = static_cast<const GeoDataPlacemark &>(other);
122 
123         return thisPM == otherPM;
124     } else if (nodeType() == GeoDataTypes::GeoDataScreenOverlayType) {
125         const GeoDataScreenOverlay &thisSO = static_cast<const GeoDataScreenOverlay &>(*this);
126         const GeoDataScreenOverlay &otherSO = static_cast<const GeoDataScreenOverlay &>(other);
127 
128         return thisSO == otherSO;
129     } else if (nodeType() == GeoDataTypes::GeoDataTourType) {
130         const GeoDataTour &thisTour = static_cast<const GeoDataTour &>(*this);
131         const GeoDataTour &otherTour = static_cast<const GeoDataTour &>(other);
132 
133         return thisTour == otherTour;
134     }
135 
136     return false;
137 }
138 
equals(const GeoDataFeature & other) const139 bool GeoDataFeature::equals( const GeoDataFeature &other ) const
140 {
141     Q_D(const GeoDataFeature);
142     const GeoDataFeaturePrivate* const other_d = other.d_func();
143 
144     if (!GeoDataObject::equals(other) ||
145         d->m_name != other_d->m_name ||
146         d->m_styleUrl != other_d->m_styleUrl ||
147         d->m_popularity != other_d->m_popularity ||
148         d->m_zoomLevel != other_d->m_zoomLevel ||
149         d->m_visible != other_d->m_visible ||
150         d->m_role != other_d->m_role ||
151         d->m_extendedData != other_d->m_extendedData ||
152         *style() != *other.style()) {
153         return false;
154     }
155 
156     if ((!d->m_styleMap && other_d->m_styleMap) ||
157         (d->m_styleMap && !other_d->m_styleMap)) {
158         return false;
159     }
160 
161     if ((d->m_styleMap && other_d->m_styleMap) &&
162         (*d->m_styleMap != *other_d->m_styleMap)) {
163         return false;
164     }
165 
166     if ((!d->m_featureExtendedData && other_d->m_featureExtendedData && other_d->m_featureExtendedData->m_abstractView) ||
167         (d->m_featureExtendedData && d->m_featureExtendedData->m_abstractView && !other_d->m_featureExtendedData)) {
168         return false;
169     }
170 
171     if ((d->m_featureExtendedData && other_d->m_featureExtendedData) &&
172         (*d->m_featureExtendedData != *other_d->m_featureExtendedData)) {
173         return false;
174     }
175 
176     return true;
177 }
178 
featureId() const179 EnumFeatureId GeoDataFeature::featureId() const
180 {
181     Q_D(const GeoDataFeature);
182     return d->featureId();
183 }
184 
name() const185 QString GeoDataFeature::name() const
186 {
187     Q_D(const GeoDataFeature);
188     return d->m_name;
189 }
190 
setName(const QString & value)191 void GeoDataFeature::setName( const QString &value )
192 {
193     Q_D(GeoDataFeature);
194     d->m_name = value;
195 }
196 
snippet() const197 GeoDataSnippet GeoDataFeature::snippet() const
198 {
199     Q_D(const GeoDataFeature);
200     return d->featureExtendedData().m_snippet;
201 }
202 
setSnippet(const GeoDataSnippet & snippet)203 void GeoDataFeature::setSnippet( const GeoDataSnippet &snippet )
204 {
205     Q_D(GeoDataFeature);
206     d->featureExtendedData().m_snippet = snippet;
207 }
208 
address() const209 QString GeoDataFeature::address() const
210 {
211     Q_D(const GeoDataFeature);
212     if (!d->m_featureExtendedData) {
213         return QString();
214     }
215 
216     return d->featureExtendedData().m_address;
217 }
218 
setAddress(const QString & value)219 void GeoDataFeature::setAddress( const QString &value)
220 {
221     Q_D(GeoDataFeature);
222     if (value.isEmpty() && !d->m_featureExtendedData) {
223         return; // nothing to change
224     }
225 
226     d->featureExtendedData().m_address = value;
227 }
228 
phoneNumber() const229 QString GeoDataFeature::phoneNumber() const
230 {
231     Q_D(const GeoDataFeature);
232     if (!d->m_featureExtendedData) {
233         return QString();
234     }
235 
236     return d->featureExtendedData().m_phoneNumber;
237 }
238 
setPhoneNumber(const QString & value)239 void GeoDataFeature::setPhoneNumber( const QString &value)
240 {
241     Q_D(GeoDataFeature);
242     if (value.isEmpty() && !d->m_featureExtendedData) {
243         return; // nothing to change
244     }
245 
246     d->featureExtendedData().m_phoneNumber = value;
247 }
248 
description() const249 QString GeoDataFeature::description() const
250 {
251     Q_D(const GeoDataFeature);
252     if (!d->m_featureExtendedData) {
253         return QString();
254     }
255 
256     return d->featureExtendedData().m_description;
257 }
258 
setDescription(const QString & value)259 void GeoDataFeature::setDescription( const QString &value)
260 {
261     Q_D(GeoDataFeature);
262     if (value.isEmpty() && !d->m_featureExtendedData) {
263         return; // nothing to change
264     }
265 
266     d->featureExtendedData().m_description = value;
267 }
268 
descriptionIsCDATA() const269 bool GeoDataFeature::descriptionIsCDATA() const
270 {
271     Q_D(const GeoDataFeature);
272     if (!d->m_featureExtendedData) {
273         return false;
274     }
275 
276     return d->featureExtendedData().m_descriptionCDATA;
277 }
278 
setDescriptionCDATA(bool cdata)279 void GeoDataFeature::setDescriptionCDATA( bool cdata )
280 {
281     Q_D(GeoDataFeature);
282     d->featureExtendedData().m_descriptionCDATA = cdata;
283 }
284 
abstractView() const285 const GeoDataAbstractView* GeoDataFeature::abstractView() const
286 {
287     Q_D(const GeoDataFeature);
288     if (!d->m_featureExtendedData) {
289         return nullptr;
290     }
291 
292     return d->featureExtendedData().m_abstractView;
293 }
294 
abstractView()295 GeoDataAbstractView *GeoDataFeature::abstractView()
296 {
297     // FIXME: Calling detach() doesn't help at all because the m_abstractView
298     // object isn't actually copied in the Private class as well.
299     // detach();
300 
301     Q_D(GeoDataFeature);
302     return d->featureExtendedData().m_abstractView;
303 }
304 
setAbstractView(GeoDataAbstractView * abstractView)305 void GeoDataFeature::setAbstractView( GeoDataAbstractView *abstractView )
306 {
307     Q_D(GeoDataFeature);
308     if (abstractView == nullptr && !d->m_featureExtendedData) {
309         return; // nothing to change
310     }
311 
312     d->featureExtendedData().m_abstractView = abstractView;
313 }
314 
styleUrl() const315 QString GeoDataFeature::styleUrl() const
316 {
317     Q_D(const GeoDataFeature);
318     return d->m_styleUrl;
319 }
320 
setStyleUrl(const QString & value)321 void GeoDataFeature::setStyleUrl( const QString &value )
322 {
323     Q_D(GeoDataFeature);
324     d->m_styleUrl = value;
325 
326     if ( value.isEmpty() ) {
327         d->m_style = GeoDataStyle::Ptr();
328         return;
329     }
330 
331     QString styleUrl = value;
332     styleUrl.remove(QLatin1Char('#'));
333 
334     for (auto object = parent(); object != nullptr; object = object->parent()) {
335         if (GeoDataDocument *doc = geodata_cast<GeoDataDocument>(object)) {
336             GeoDataStyleMap &styleMap = doc->styleMap( styleUrl );
337             const QString normalStyleUrl = styleMap.value(QStringLiteral("normal"));
338             if (!normalStyleUrl.isEmpty()) {
339                 styleUrl = normalStyleUrl;
340                 styleUrl.remove(QLatin1Char('#'));
341             }
342             // Not calling setStyle here because we don't want
343             // re-parenting of the style
344             d->m_style = doc->style( styleUrl );
345             break;
346         }
347     }
348 }
349 
isVisible() const350 bool GeoDataFeature::isVisible() const
351 {
352     Q_D(const GeoDataFeature);
353     return d->m_visible;
354 }
355 
setVisible(bool value)356 void GeoDataFeature::setVisible( bool value )
357 {
358     Q_D(GeoDataFeature);
359     d->m_visible = value;
360 }
361 
isGloballyVisible() const362 bool GeoDataFeature::isGloballyVisible() const
363 {
364     Q_D(const GeoDataFeature);
365     if ( parent() == nullptr ) {
366         return d->m_visible;
367     }
368     const GeoDataContainer *container = static_cast<const GeoDataContainer*>(parent());
369     return d->m_visible && container->isGloballyVisible();
370 }
371 
372 
timeSpan() const373 const GeoDataTimeSpan &GeoDataFeature::timeSpan() const
374 {
375     Q_D(const GeoDataFeature);
376     return d->featureExtendedData().m_timeSpan;
377 }
378 
timeSpan()379 GeoDataTimeSpan &GeoDataFeature::timeSpan()
380 {
381     Q_D(GeoDataFeature);
382     return d->featureExtendedData().m_timeSpan;
383 }
384 
setTimeSpan(const GeoDataTimeSpan & timeSpan)385 void GeoDataFeature::setTimeSpan( const GeoDataTimeSpan &timeSpan )
386 {
387     Q_D(GeoDataFeature);
388     d->featureExtendedData().m_timeSpan = timeSpan;
389 }
390 
timeStamp() const391 const GeoDataTimeStamp &GeoDataFeature::timeStamp() const
392 {
393     Q_D(const GeoDataFeature);
394     return d->featureExtendedData().m_timeStamp;
395 }
396 
timeStamp()397 GeoDataTimeStamp &GeoDataFeature::timeStamp()
398 {
399     Q_D(GeoDataFeature);
400     return d->featureExtendedData().m_timeStamp;
401 }
402 
setTimeStamp(const GeoDataTimeStamp & timeStamp)403 void GeoDataFeature::setTimeStamp( const GeoDataTimeStamp &timeStamp )
404 {
405     Q_D(GeoDataFeature);
406     d->featureExtendedData().m_timeStamp = timeStamp;
407 }
408 
extendedData() const409 const GeoDataExtendedData &GeoDataFeature::extendedData() const
410 {
411     Q_D(const GeoDataFeature);
412     return d->m_extendedData;
413 }
414 
style() const415 GeoDataStyle::ConstPtr GeoDataFeature::style() const
416 {
417     Q_D(const GeoDataFeature);
418     if (d->m_style) {
419         return d->m_style;
420     }
421 
422     return GeoDataFeaturePrivate::s_defaultStyle;
423 }
424 
customStyle() const425 GeoDataStyle::ConstPtr GeoDataFeature::customStyle() const
426 {
427     Q_D(const GeoDataFeature);
428     return d->m_style;
429 }
430 
setStyle(const GeoDataStyle::Ptr & style)431 void GeoDataFeature::setStyle( const GeoDataStyle::Ptr &style )
432 {
433     Q_D(GeoDataFeature);
434     if (style)
435         style->setParent( this );
436     d->m_style = style;
437 }
438 
extendedData()439 GeoDataExtendedData& GeoDataFeature::extendedData()
440 {
441     Q_D(GeoDataFeature);
442     return d->m_extendedData;
443 }
444 
setExtendedData(const GeoDataExtendedData & extendedData)445 void GeoDataFeature::setExtendedData( const GeoDataExtendedData& extendedData )
446 {
447     Q_D(GeoDataFeature);
448     d->m_extendedData = extendedData;
449 }
450 
region() const451 const GeoDataRegion& GeoDataFeature::region() const
452 {
453     Q_D(const GeoDataFeature);
454     return d->featureExtendedData().m_region;
455 }
456 
region()457 GeoDataRegion& GeoDataFeature::region()
458 {
459     Q_D(GeoDataFeature);
460     return d->featureExtendedData().m_region;
461 }
462 
setRegion(const GeoDataRegion & region)463 void GeoDataFeature::setRegion( const GeoDataRegion& region )
464 {
465     Q_D(GeoDataFeature);
466     d->featureExtendedData().m_region = region;
467 }
468 
role() const469 const QString GeoDataFeature::role() const
470 {
471     Q_D(const GeoDataFeature);
472     return d->m_role;
473 }
474 
setRole(const QString & role)475 void GeoDataFeature::setRole( const QString &role )
476 {
477     Q_D(GeoDataFeature);
478     d->m_role = role;
479 }
480 
styleMap() const481 const GeoDataStyleMap* GeoDataFeature::styleMap() const
482 {
483     Q_D(const GeoDataFeature);
484     return d->m_styleMap;
485 }
486 
setStyleMap(const GeoDataStyleMap * styleMap)487 void GeoDataFeature::setStyleMap( const GeoDataStyleMap* styleMap )
488 {
489     Q_D(GeoDataFeature);
490     d->m_styleMap = styleMap;
491 }
492 
zoomLevel() const493 int GeoDataFeature::zoomLevel() const
494 {
495     Q_D(const GeoDataFeature);
496     return d->m_zoomLevel;
497 }
498 
setZoomLevel(int zoomLevel)499 void GeoDataFeature::setZoomLevel( int zoomLevel )
500 {
501     Q_D(GeoDataFeature);
502     d->m_zoomLevel = zoomLevel;
503 }
504 
popularity() const505 qint64 GeoDataFeature::popularity() const
506 {
507     Q_D(const GeoDataFeature);
508     return d->m_popularity;
509 }
510 
setPopularity(qint64 popularity)511 void GeoDataFeature::setPopularity( qint64 popularity )
512 {
513     Q_D(GeoDataFeature);
514     d->m_popularity = popularity;
515 }
516 
pack(QDataStream & stream) const517 void GeoDataFeature::pack( QDataStream& stream ) const
518 {
519     Q_D(const GeoDataFeature);
520 
521     GeoDataObject::pack( stream );
522 
523     stream << d->m_name;
524     stream << d->featureExtendedData().m_address;
525     stream << d->featureExtendedData().m_phoneNumber;
526     stream << d->featureExtendedData().m_description;
527     stream << d->m_visible;
528 //    stream << d->m_visualCategory;
529     stream << d->m_role;
530     stream << d->m_popularity;
531     stream << d->m_zoomLevel;
532 }
533 
unpack(QDataStream & stream)534 void GeoDataFeature::unpack( QDataStream& stream )
535 {
536     Q_D(GeoDataFeature);
537     GeoDataObject::unpack( stream );
538 
539     stream >> d->m_name;
540     stream >> d->featureExtendedData().m_address;
541     stream >> d->featureExtendedData().m_phoneNumber;
542     stream >> d->featureExtendedData().m_description;
543     stream >> d->m_visible;
544 //    stream >> (int)d->m_visualCategory;
545     stream >> d->m_role;
546     stream >> d->m_popularity;
547     stream >> d->m_zoomLevel;
548 }
549 
550 }
551