1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2008 Torsten Rahn <rahn@kde.org>
4 // SPDX-FileCopyrightText: 2009 Patrick Spendrin <ps_ml@gmx.de>
5 //
6
7
8 #include "GeoDataLineString.h"
9 #include "GeoDataLineString_p.h"
10
11 #include "GeoDataLinearRing.h"
12 #include "GeoDataTypes.h"
13 #include "Quaternion.h"
14 #include "MarbleDebug.h"
15
16 #include <QDataStream>
17
18
19 namespace Marble
20 {
GeoDataLineString(TessellationFlags f)21 GeoDataLineString::GeoDataLineString( TessellationFlags f )
22 : GeoDataGeometry( new GeoDataLineStringPrivate( f ) )
23 {
24 // mDebug() << "1) GeoDataLineString created:" << p();
25 }
26
GeoDataLineString(GeoDataLineStringPrivate * priv)27 GeoDataLineString::GeoDataLineString( GeoDataLineStringPrivate* priv )
28 : GeoDataGeometry( priv )
29 {
30 // mDebug() << "2) GeoDataLineString created:" << p();
31 }
32
GeoDataLineString(const GeoDataGeometry & other)33 GeoDataLineString::GeoDataLineString( const GeoDataGeometry & other )
34 : GeoDataGeometry( other )
35 {
36 // mDebug() << "3) GeoDataLineString created:" << p();
37 }
38
~GeoDataLineString()39 GeoDataLineString::~GeoDataLineString()
40 {
41 #ifdef DEBUG_GEODATA
42 mDebug() << "delete Linestring";
43 #endif
44 }
45
nodeType() const46 const char *GeoDataLineString::nodeType() const
47 {
48 return GeoDataTypes::GeoDataLineStringType;
49 }
50
geometryId() const51 EnumGeometryId GeoDataLineString::geometryId() const
52 {
53 return GeoDataLineStringId;
54 }
55
copy() const56 GeoDataGeometry *GeoDataLineString::copy() const
57 {
58 return new GeoDataLineString(*this);
59 }
60
interpolateDateLine(const GeoDataCoordinates & previousCoords,const GeoDataCoordinates & currentCoords,GeoDataCoordinates & previousAtDateLine,GeoDataCoordinates & currentAtDateLine,TessellationFlags f) const61 void GeoDataLineStringPrivate::interpolateDateLine( const GeoDataCoordinates & previousCoords,
62 const GeoDataCoordinates & currentCoords,
63 GeoDataCoordinates & previousAtDateLine,
64 GeoDataCoordinates & currentAtDateLine,
65 TessellationFlags f ) const
66 {
67 GeoDataCoordinates dateLineCoords;
68
69 // mDebug() << Q_FUNC_INFO;
70
71 if ( f.testFlag( RespectLatitudeCircle ) && previousCoords.latitude() == currentCoords.latitude() ) {
72 dateLineCoords = currentCoords;
73 }
74 else {
75 int recursionCounter = 0;
76 dateLineCoords = findDateLine( previousCoords, currentCoords, recursionCounter );
77 }
78
79 previousAtDateLine = dateLineCoords;
80 currentAtDateLine = dateLineCoords;
81
82 if ( previousCoords.longitude() < 0 ) {
83 previousAtDateLine.setLongitude( -M_PI );
84 currentAtDateLine.setLongitude( +M_PI );
85 }
86 else {
87 previousAtDateLine.setLongitude( +M_PI );
88 currentAtDateLine.setLongitude( -M_PI );
89 }
90 }
91
findDateLine(const GeoDataCoordinates & previousCoords,const GeoDataCoordinates & currentCoords,int recursionCounter) const92 GeoDataCoordinates GeoDataLineStringPrivate::findDateLine( const GeoDataCoordinates & previousCoords,
93 const GeoDataCoordinates & currentCoords,
94 int recursionCounter ) const
95 {
96 int currentSign = ( currentCoords.longitude() < 0.0 ) ? -1 : +1 ;
97 int previousSign = ( previousCoords.longitude() < 0.0 ) ? -1 : +1 ;
98
99 qreal longitudeDiff = fabs( previousSign * M_PI - previousCoords.longitude() )
100 + fabs( currentSign * M_PI - currentCoords.longitude() );
101
102 if ( longitudeDiff < 0.001 || recursionCounter == 100 ) {
103 // mDebug() << "stopped at recursion" << recursionCounter << " and longitude difference " << longitudeDiff;
104 return currentCoords;
105 }
106 ++recursionCounter;
107
108 const GeoDataCoordinates interpolatedCoords = previousCoords.nlerp(currentCoords, 0.5);
109
110 int interpolatedSign = ( interpolatedCoords.longitude() < 0.0 ) ? -1 : +1 ;
111
112 /*
113 mDebug() << "SRC" << previousCoords.toString();
114 mDebug() << "TAR" << currentCoords.toString();
115 mDebug() << "IPC" << interpolatedCoords.toString();
116 */
117
118 if ( interpolatedSign != currentSign ) {
119 return findDateLine( interpolatedCoords, currentCoords, recursionCounter );
120 }
121
122 return findDateLine( previousCoords, interpolatedCoords, recursionCounter );
123 }
124
levelForResolution(qreal resolution) const125 quint8 GeoDataLineStringPrivate::levelForResolution(qreal resolution) const {
126 if (m_previousResolution == resolution) return m_level;
127
128 m_previousResolution = resolution;
129
130 if (resolution < 0.0000005) m_level = 17;
131 else if (resolution < 0.0000010) m_level = 16;
132 else if (resolution < 0.0000020) m_level = 15;
133 else if (resolution < 0.0000040) m_level = 14;
134 else if (resolution < 0.0000080) m_level = 13;
135 else if (resolution < 0.0000160) m_level = 12;
136 else if (resolution < 0.0000320) m_level = 11;
137 else if (resolution < 0.0000640) m_level = 10;
138 else if (resolution < 0.0001280) m_level = 9;
139 else if (resolution < 0.0002560) m_level = 8;
140 else if (resolution < 0.0005120) m_level = 7;
141 else if (resolution < 0.0010240) m_level = 6;
142 else if (resolution < 0.0020480) m_level = 5;
143 else if (resolution < 0.0040960) m_level = 4;
144 else if (resolution < 0.0081920) m_level = 3;
145 else if (resolution < 0.0163840) m_level = 2;
146 else m_level = 1;
147
148 return m_level;
149 }
150
resolutionForLevel(int level)151 qreal GeoDataLineStringPrivate::resolutionForLevel(int level)
152 {
153 switch (level) {
154 case 0:
155 return 0.0655360;
156 case 1:
157 return 0.0327680;
158 case 2:
159 return 0.0163840;
160 case 3:
161 return 0.0081920;
162 case 4:
163 return 0.0040960;
164 case 5:
165 return 0.0020480;
166 case 6:
167 return 0.0010240;
168 case 7:
169 return 0.0005120;
170 case 8:
171 return 0.0002560;
172 case 9:
173 return 0.0001280;
174 case 10:
175 return 0.0000640;
176 case 11:
177 return 0.0000320;
178 case 12:
179 return 0.0000160;
180 case 13:
181 return 0.0000080;
182 case 14:
183 return 0.0000040;
184 case 15:
185 return 0.0000020;
186 case 16:
187 return 0.0000010;
188 default:
189 case 17:
190 return 0.0000005;
191 }
192 }
193
optimize(GeoDataLineString & lineString) const194 void GeoDataLineStringPrivate::optimize (GeoDataLineString& lineString) const
195 {
196
197 QVector<GeoDataCoordinates>::iterator itCoords = lineString.begin();
198 QVector<GeoDataCoordinates>::const_iterator itEnd = lineString.constEnd();
199
200 if (lineString.size() < 2) return;
201
202 // Calculate the least non-zero detail-level by checking the bounding box
203 quint8 startLevel = levelForResolution( ( lineString.latLonAltBox().width() + lineString.latLonAltBox().height() ) / 2 );
204
205 quint8 currentLevel = startLevel;
206 quint8 maxLevel = startLevel;
207 GeoDataCoordinates currentCoords;
208 lineString.first().setDetail(startLevel);
209
210 // Iterate through the linestring to assign different detail levels to the nodes.
211 // In general the first and last node should have the start level assigned as
212 // a detail level.
213 // Starting from the first node the algorithm picks those nodes which
214 // have a distance from each other that is just above the resolution that is
215 // associated with the start level (which we use as a "current level").
216 // Each of those nodes get the current level assigned as the detail level.
217 // After iterating through the linestring we increment the current level value
218 // and starting again with the first node we assign detail values in a similar way
219 // to the remaining nodes which have no final detail level assigned yet.
220 // We do as many iterations through the lineString as needed and bump up the
221 // current level until all nodes have a non-zero detail level assigned.
222
223 while ( currentLevel < 16 && currentLevel <= maxLevel + 1 ) {
224 itCoords = lineString.begin();
225
226 currentCoords = *itCoords;
227 ++itCoords;
228
229 for( ; itCoords != itEnd; ++itCoords) {
230 if (itCoords->detail() != 0 && itCoords->detail() < currentLevel) continue;
231
232 if ( currentLevel == startLevel && (itCoords->longitude() == -M_PI || itCoords->longitude() == M_PI
233 || itCoords->latitude() < -89 * DEG2RAD || itCoords->latitude() > 89 * DEG2RAD)) {
234 itCoords->setDetail(startLevel);
235 currentCoords = *itCoords;
236 maxLevel = currentLevel;
237 continue;
238 }
239 if (currentCoords.sphericalDistanceTo(*itCoords) < resolutionForLevel(currentLevel + 1)) {
240 itCoords->setDetail(currentLevel + 1);
241 }
242 else {
243 itCoords->setDetail(currentLevel);
244 currentCoords = *itCoords;
245 maxLevel = currentLevel;
246 }
247 }
248 ++currentLevel;
249 }
250 lineString.last().setDetail(startLevel);
251 }
252
isEmpty() const253 bool GeoDataLineString::isEmpty() const
254 {
255 Q_D(const GeoDataLineString);
256 return d->m_vector.isEmpty();
257 }
258
size() const259 int GeoDataLineString::size() const
260 {
261 Q_D(const GeoDataLineString);
262 return d->m_vector.size();
263 }
264
at(int pos)265 GeoDataCoordinates& GeoDataLineString::at( int pos )
266 {
267 detach();
268
269 Q_D(GeoDataLineString);
270 d->m_dirtyRange = true;
271 d->m_dirtyBox = true;
272 return d->m_vector[pos];
273 }
274
at(int pos) const275 const GeoDataCoordinates& GeoDataLineString::at( int pos ) const
276 {
277 Q_D(const GeoDataLineString);
278 return d->m_vector.at(pos);
279 }
280
operator [](int pos)281 GeoDataCoordinates& GeoDataLineString::operator[]( int pos )
282 {
283 detach();
284
285 Q_D(GeoDataLineString);
286 d->m_dirtyRange = true;
287 d->m_dirtyBox = true;
288 return d->m_vector[pos];
289 }
290
mid(int pos,int length) const291 GeoDataLineString GeoDataLineString::mid(int pos, int length) const
292 {
293 GeoDataLineString substring;
294 auto d = substring.d_func();
295 d->m_vector = d_func()->m_vector.mid(pos, length);
296 d->m_dirtyBox = true;
297 d->m_dirtyRange = true;
298 d->m_tessellationFlags = d_func()->m_tessellationFlags;
299 d->m_extrude = d_func()->m_extrude;
300 return substring;
301 }
302
operator [](int pos) const303 const GeoDataCoordinates& GeoDataLineString::operator[]( int pos ) const
304 {
305 Q_D(const GeoDataLineString);
306 return d->m_vector[pos];
307 }
308
last()309 GeoDataCoordinates& GeoDataLineString::last()
310 {
311 detach();
312
313 Q_D(GeoDataLineString);
314 d->m_dirtyRange = true;
315 d->m_dirtyBox = true;
316 return d->m_vector.last();
317 }
318
first()319 GeoDataCoordinates& GeoDataLineString::first()
320 {
321 detach();
322
323 Q_D(GeoDataLineString);
324 return d->m_vector.first();
325 }
326
last() const327 const GeoDataCoordinates& GeoDataLineString::last() const
328 {
329 Q_D(const GeoDataLineString);
330 return d->m_vector.last();
331 }
332
first() const333 const GeoDataCoordinates& GeoDataLineString::first() const
334 {
335 Q_D(const GeoDataLineString);
336 return d->m_vector.first();
337 }
338
begin()339 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::begin()
340 {
341 detach();
342
343 Q_D(GeoDataLineString);
344 return d->m_vector.begin();
345 }
346
begin() const347 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::begin() const
348 {
349 Q_D(const GeoDataLineString);
350 return d->m_vector.constBegin();
351 }
352
end()353 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::end()
354 {
355 detach();
356
357 Q_D(GeoDataLineString);
358 return d->m_vector.end();
359 }
360
end() const361 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::end() const
362 {
363 Q_D(const GeoDataLineString);
364 return d->m_vector.constEnd();
365 }
366
constBegin() const367 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::constBegin() const
368 {
369 Q_D(const GeoDataLineString);
370 return d->m_vector.constBegin();
371 }
372
constEnd() const373 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::constEnd() const
374 {
375 Q_D(const GeoDataLineString);
376 return d->m_vector.constEnd();
377 }
378
insert(int index,const GeoDataCoordinates & value)379 void GeoDataLineString::insert( int index, const GeoDataCoordinates& value )
380 {
381 detach();
382
383 Q_D(GeoDataLineString);
384 delete d->m_rangeCorrected;
385 d->m_rangeCorrected = nullptr;
386 d->m_dirtyRange = true;
387 d->m_dirtyBox = true;
388 d->m_vector.insert( index, value );
389 }
390
append(const GeoDataCoordinates & value)391 void GeoDataLineString::append ( const GeoDataCoordinates& value )
392 {
393 detach();
394
395 Q_D(GeoDataLineString);
396 delete d->m_rangeCorrected;
397 d->m_rangeCorrected = nullptr;
398 d->m_dirtyRange = true;
399 d->m_dirtyBox = true;
400 d->m_vector.append( value );
401 }
402
reserve(int size)403 void GeoDataLineString::reserve(int size)
404 {
405 Q_D(GeoDataLineString);
406 d->m_vector.reserve(size);
407 }
408
append(const QVector<GeoDataCoordinates> & values)409 void GeoDataLineString::append(const QVector<GeoDataCoordinates>& values)
410 {
411 detach();
412
413 Q_D(GeoDataLineString);
414 delete d->m_rangeCorrected;
415 d->m_rangeCorrected = nullptr;
416 d->m_dirtyRange = true;
417 d->m_dirtyBox = true;
418
419 d->m_vector.append(values);
420 }
421
operator <<(const GeoDataCoordinates & value)422 GeoDataLineString& GeoDataLineString::operator << ( const GeoDataCoordinates& value )
423 {
424 detach();
425
426 Q_D(GeoDataLineString);
427 delete d->m_rangeCorrected;
428 d->m_rangeCorrected = nullptr;
429 d->m_dirtyRange = true;
430 d->m_dirtyBox = true;
431 d->m_vector.append( value );
432 return *this;
433 }
434
operator <<(const GeoDataLineString & value)435 GeoDataLineString& GeoDataLineString::operator << ( const GeoDataLineString& value )
436 {
437 detach();
438
439 Q_D(GeoDataLineString);
440 delete d->m_rangeCorrected;
441 d->m_rangeCorrected = nullptr;
442 d->m_dirtyRange = true;
443 d->m_dirtyBox = true;
444
445 QVector<GeoDataCoordinates>::const_iterator itCoords = value.constBegin();
446 QVector<GeoDataCoordinates>::const_iterator itEnd = value.constEnd();
447
448 d->m_vector.reserve(d->m_vector.size() + value.size());
449 for( ; itCoords != itEnd; ++itCoords ) {
450 d->m_vector.append( *itCoords );
451 }
452
453 return *this;
454 }
455
operator ==(const GeoDataLineString & other) const456 bool GeoDataLineString::operator==( const GeoDataLineString &other ) const
457 {
458 if ( !GeoDataGeometry::equals(other) ||
459 size() != other.size() ||
460 tessellate() != other.tessellate() ) {
461 return false;
462 }
463
464 Q_D(const GeoDataLineString);
465 const GeoDataLineStringPrivate* other_d = other.d_func();
466
467 QVector<GeoDataCoordinates>::const_iterator itCoords = d->m_vector.constBegin();
468 QVector<GeoDataCoordinates>::const_iterator otherItCoords = other_d->m_vector.constBegin();
469 QVector<GeoDataCoordinates>::const_iterator itEnd = d->m_vector.constEnd();
470 QVector<GeoDataCoordinates>::const_iterator otherItEnd = other_d->m_vector.constEnd();
471
472 for ( ; itCoords != itEnd && otherItCoords != otherItEnd; ++itCoords, ++otherItCoords ) {
473 if ( *itCoords != *otherItCoords ) {
474 return false;
475 }
476 }
477
478 Q_ASSERT ( itCoords == itEnd && otherItCoords == otherItEnd );
479 return true;
480 }
481
operator !=(const GeoDataLineString & other) const482 bool GeoDataLineString::operator!=( const GeoDataLineString &other ) const
483 {
484 return !this->operator==(other);
485 }
486
clear()487 void GeoDataLineString::clear()
488 {
489 detach();
490
491 Q_D(GeoDataLineString);
492 delete d->m_rangeCorrected;
493 d->m_rangeCorrected = nullptr;
494 d->m_dirtyRange = true;
495 d->m_dirtyBox = true;
496
497 d->m_vector.clear();
498 }
499
isClosed() const500 bool GeoDataLineString::isClosed() const
501 {
502 return false;
503 }
504
tessellate() const505 bool GeoDataLineString::tessellate() const
506 {
507 Q_D(const GeoDataLineString);
508 return d->m_tessellationFlags.testFlag(Tessellate);
509 }
510
setTessellate(bool tessellate)511 void GeoDataLineString::setTessellate( bool tessellate )
512 {
513 detach();
514
515 Q_D(GeoDataLineString);
516 // According to the KML reference the tesselation of line strings in Google Earth
517 // is generally done along great circles. However for subsequent points that share
518 // the same latitude the latitude circles are followed. Our Tesselate and RespectLatitude
519 // Flags provide this behaviour. For true polygons the latitude circles don't get considered.
520
521 if (tessellate) {
522 d->m_tessellationFlags |= (Tessellate | RespectLatitudeCircle);
523 } else {
524 d->m_tessellationFlags &= ~(Tessellate | RespectLatitudeCircle);
525 }
526 }
527
tessellationFlags() const528 TessellationFlags GeoDataLineString::tessellationFlags() const
529 {
530 Q_D(const GeoDataLineString);
531 return d->m_tessellationFlags;
532 }
533
setTessellationFlags(TessellationFlags f)534 void GeoDataLineString::setTessellationFlags( TessellationFlags f )
535 {
536 detach();
537
538 Q_D(GeoDataLineString);
539 d->m_tessellationFlags = f;
540 }
541
reverse()542 void GeoDataLineString::reverse()
543 {
544 detach();
545
546 Q_D(GeoDataLineString);
547 delete d->m_rangeCorrected;
548 d->m_rangeCorrected = nullptr;
549 d->m_dirtyRange = true;
550 d->m_dirtyBox = true;
551 std::reverse(begin(), end());
552 }
553
toNormalized() const554 GeoDataLineString GeoDataLineString::toNormalized() const
555 {
556 Q_D(const GeoDataLineString);
557
558 GeoDataLineString normalizedLineString;
559
560 normalizedLineString.setTessellationFlags( tessellationFlags() );
561
562 qreal lon;
563 qreal lat;
564
565 // FIXME: Think about how we can avoid unnecessary copies
566 // if the linestring stays the same.
567 QVector<GeoDataCoordinates>::const_iterator end = d->m_vector.constEnd();
568 for( QVector<GeoDataCoordinates>::const_iterator itCoords
569 = d->m_vector.constBegin();
570 itCoords != end;
571 ++itCoords ) {
572
573 itCoords->geoCoordinates( lon, lat );
574 qreal alt = itCoords->altitude();
575 GeoDataCoordinates::normalizeLonLat( lon, lat );
576
577 GeoDataCoordinates normalizedCoords( *itCoords );
578 normalizedCoords.set( lon, lat, alt );
579 normalizedLineString << normalizedCoords;
580 }
581
582 return normalizedLineString;
583 }
584
toRangeCorrected() const585 GeoDataLineString GeoDataLineString::toRangeCorrected() const
586 {
587 Q_D(const GeoDataLineString);
588
589 if (d->m_dirtyRange) {
590
591 delete d->m_rangeCorrected;
592
593 if( isClosed() ) {
594 d->m_rangeCorrected = new GeoDataLinearRing(toPoleCorrected());
595 } else {
596 d->m_rangeCorrected = new GeoDataLineString(toPoleCorrected());
597 }
598 d->m_dirtyRange = false;
599 }
600
601 return *d->m_rangeCorrected;
602 }
603
toDateLineCorrected() const604 QVector<GeoDataLineString*> GeoDataLineString::toDateLineCorrected() const
605 {
606 Q_D(const GeoDataLineString);
607
608 QVector<GeoDataLineString*> lineStrings;
609
610 d->toDateLineCorrected(*this, lineStrings);
611
612 return lineStrings;
613 }
614
toPoleCorrected() const615 GeoDataLineString GeoDataLineString::toPoleCorrected() const
616 {
617 Q_D(const GeoDataLineString);
618
619 if( isClosed() ) {
620 GeoDataLinearRing poleCorrected;
621 d->toPoleCorrected(*this, poleCorrected);
622 return poleCorrected;
623 } else {
624 GeoDataLineString poleCorrected;
625 d->toPoleCorrected(*this, poleCorrected);
626 return poleCorrected;
627 }
628 }
629
toPoleCorrected(const GeoDataLineString & q,GeoDataLineString & poleCorrected) const630 void GeoDataLineStringPrivate::toPoleCorrected( const GeoDataLineString& q, GeoDataLineString& poleCorrected ) const
631 {
632 poleCorrected.setTessellationFlags( q.tessellationFlags() );
633
634 GeoDataCoordinates previousCoords;
635 GeoDataCoordinates currentCoords;
636
637 if ( q.isClosed() ) {
638 if ( !( m_vector.first().isPole() ) &&
639 ( m_vector.last().isPole() ) ) {
640 qreal firstLongitude = ( m_vector.first() ).longitude();
641 GeoDataCoordinates modifiedCoords( m_vector.last() );
642 modifiedCoords.setLongitude( firstLongitude );
643 poleCorrected << modifiedCoords;
644 }
645 }
646
647 QVector<GeoDataCoordinates>::const_iterator itCoords = m_vector.constBegin();
648 QVector<GeoDataCoordinates>::const_iterator itEnd = m_vector.constEnd();
649
650 for( ; itCoords != itEnd; ++itCoords ) {
651
652 currentCoords = *itCoords;
653
654 if ( itCoords == m_vector.constBegin() ) {
655 previousCoords = currentCoords;
656 }
657
658 if ( currentCoords.isPole() ) {
659 if ( previousCoords.isPole() ) {
660 continue;
661 }
662 else {
663 qreal previousLongitude = previousCoords.longitude();
664 GeoDataCoordinates currentModifiedCoords( currentCoords );
665 currentModifiedCoords.setLongitude( previousLongitude );
666 poleCorrected << currentModifiedCoords;
667 }
668 }
669 else {
670 if ( previousCoords.isPole() ) {
671 qreal currentLongitude = currentCoords.longitude();
672 GeoDataCoordinates previousModifiedCoords( previousCoords );
673 previousModifiedCoords.setLongitude( currentLongitude );
674 poleCorrected << previousModifiedCoords;
675 poleCorrected << currentCoords;
676 }
677 else {
678 // No poles at all. Nothing special to handle
679 poleCorrected << currentCoords;
680 }
681 }
682 previousCoords = currentCoords;
683 }
684
685 if ( q.isClosed() ) {
686 if ( ( m_vector.first().isPole() ) &&
687 !( m_vector.last().isPole() ) ) {
688 qreal lastLongitude = ( m_vector.last() ).longitude();
689 GeoDataCoordinates modifiedCoords( m_vector.first() );
690 modifiedCoords.setLongitude( lastLongitude );
691 poleCorrected << modifiedCoords;
692 }
693 }
694 }
695
toDateLineCorrected(const GeoDataLineString & q,QVector<GeoDataLineString * > & lineStrings) const696 void GeoDataLineStringPrivate::toDateLineCorrected(
697 const GeoDataLineString & q,
698 QVector<GeoDataLineString*> & lineStrings
699 ) const
700 {
701 const bool isClosed = q.isClosed();
702
703 const QVector<GeoDataCoordinates>::const_iterator itStartPoint = q.constBegin();
704 const QVector<GeoDataCoordinates>::const_iterator itEndPoint = q.constEnd();
705 QVector<GeoDataCoordinates>::const_iterator itPoint = itStartPoint;
706 QVector<GeoDataCoordinates>::const_iterator itPreviousPoint = itPoint;
707
708 TessellationFlags f = q.tessellationFlags();
709
710 GeoDataLineString * unfinishedLineString = nullptr;
711
712 GeoDataLineString * dateLineCorrected = isClosed ? new GeoDataLinearRing( f )
713 : new GeoDataLineString( f );
714
715 qreal previousLon = 0.0;
716 int previousSign = 1;
717
718 bool unfinished = false;
719
720 for (; itPoint != itEndPoint; ++itPoint ) {
721 const qreal currentLon = itPoint->longitude();
722
723 int currentSign = ( currentLon < 0.0 ) ? -1 : +1 ;
724
725 if( itPoint == q.constBegin() ) {
726 previousSign = currentSign;
727 previousLon = currentLon;
728 }
729
730 // If we are crossing the date line ...
731 if ( previousSign != currentSign && fabs(previousLon) + fabs(currentLon) > M_PI ) {
732
733 unfinished = !unfinished;
734
735 GeoDataCoordinates previousTemp;
736 GeoDataCoordinates currentTemp;
737
738 interpolateDateLine( *itPreviousPoint, *itPoint,
739 previousTemp, currentTemp, q.tessellationFlags() );
740
741 *dateLineCorrected << previousTemp;
742
743 if ( isClosed && unfinished ) {
744 // If it's a linear ring and if it crossed the IDL only once then
745 // store the current string inside the unfinishedLineString for later use ...
746 unfinishedLineString = dateLineCorrected;
747 // ... and start a new linear ring for now.
748 dateLineCorrected = new GeoDataLinearRing( f );
749 }
750 else {
751 // Now it can only be a (finished) line string or a finished linear ring.
752 // Store it in the vector if the size is not zero.
753 if ( dateLineCorrected->size() > 0 ) {
754 lineStrings << dateLineCorrected;
755 }
756 else {
757 // Or delete it.
758 delete dateLineCorrected;
759 }
760
761 // If it's a finished linear ring restore the "remembered" unfinished String
762 if ( isClosed && !unfinished && unfinishedLineString ) {
763 dateLineCorrected = unfinishedLineString;
764 }
765 else {
766 // if it's a line string just create a new line string.
767 dateLineCorrected = new GeoDataLineString( f );
768 }
769 }
770
771 *dateLineCorrected << currentTemp;
772 *dateLineCorrected << *itPoint;
773
774 }
775 else {
776 *dateLineCorrected << *itPoint;
777 }
778
779 previousSign = currentSign;
780 previousLon = currentLon;
781 itPreviousPoint = itPoint;
782 }
783
784 // If the line string doesn't cross the dateline an even number of times
785 // then need to take care of the data stored in the unfinishedLineString
786 if ( unfinished && unfinishedLineString && !unfinishedLineString->isEmpty() ) {
787 *dateLineCorrected << *unfinishedLineString;
788 delete unfinishedLineString;
789 }
790
791 lineStrings << dateLineCorrected;
792 }
793
latLonAltBox() const794 const GeoDataLatLonAltBox& GeoDataLineString::latLonAltBox() const
795 {
796 Q_D(const GeoDataLineString);
797
798 // GeoDataLatLonAltBox::fromLineString is very expensive
799 // that's why we recreate it only if the m_dirtyBox
800 // is TRUE.
801 // DO NOT REMOVE THIS CONSTRUCT OR MARBLE WILL BE SLOW.
802 if (d->m_dirtyBox) {
803 d->m_latLonAltBox = GeoDataLatLonAltBox::fromLineString(*this);
804 d->m_dirtyBox = false;
805 }
806
807 return d->m_latLonAltBox;
808 }
809
length(qreal planetRadius,int offset) const810 qreal GeoDataLineString::length( qreal planetRadius, int offset ) const
811 {
812 if( offset < 0 || offset >= size() ) {
813 return 0;
814 }
815
816 Q_D(const GeoDataLineString);
817 qreal length = 0.0;
818 QVector<GeoDataCoordinates> const & vector = d->m_vector;
819 int const start = qMax(offset+1, 1);
820 int const end = d->m_vector.size();
821 for( int i=start; i<end; ++i )
822 {
823 length += vector[i-1].sphericalDistanceTo(vector[i]);
824 }
825
826 return planetRadius * length;
827 }
828
erase(const QVector<GeoDataCoordinates>::Iterator & pos)829 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::erase ( const QVector<GeoDataCoordinates>::Iterator& pos )
830 {
831 detach();
832
833 Q_D(GeoDataLineString);
834 delete d->m_rangeCorrected;
835 d->m_rangeCorrected = nullptr;
836 d->m_dirtyRange = true;
837 d->m_dirtyBox = true;
838 return d->m_vector.erase( pos );
839 }
840
erase(const QVector<GeoDataCoordinates>::Iterator & begin,const QVector<GeoDataCoordinates>::Iterator & end)841 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::erase ( const QVector<GeoDataCoordinates>::Iterator& begin,
842 const QVector<GeoDataCoordinates>::Iterator& end )
843 {
844 detach();
845
846 Q_D(GeoDataLineString);
847 delete d->m_rangeCorrected;
848 d->m_rangeCorrected = nullptr;
849 d->m_dirtyRange = true;
850 d->m_dirtyBox = true;
851 return d->m_vector.erase( begin, end );
852 }
853
remove(int i)854 void GeoDataLineString::remove ( int i )
855 {
856 detach();
857
858 Q_D(GeoDataLineString);
859 d->m_dirtyRange = true;
860 d->m_dirtyBox = true;
861 d->m_vector.remove( i );
862 }
863
optimized() const864 GeoDataLineString GeoDataLineString::optimized () const
865 {
866 Q_D(const GeoDataLineString);
867
868 if( isClosed() ) {
869 GeoDataLinearRing linearRing(*this);
870 d->optimize(linearRing);
871 return linearRing;
872 } else {
873 GeoDataLineString lineString(*this);
874 d->optimize(lineString);
875 return lineString;
876 }
877 }
878
toVariantList() const879 QVariantList GeoDataLineString::toVariantList() const
880 {
881 Q_D(const GeoDataLineString);
882
883 QVariantList variantList;
884 for( const GeoDataCoordinates & itCoords : d->m_vector ) {
885 QVariantMap map;
886 map.insert("lon", itCoords.longitude(GeoDataCoordinates::Degree));
887 map.insert("lat", itCoords.latitude(GeoDataCoordinates::Degree));
888 map.insert("alt", itCoords.altitude());
889 variantList << map;
890 }
891
892 if (isClosed()) {
893 QVariantMap map;
894 map.insert("lon", d->m_vector.first().longitude(GeoDataCoordinates::Degree));
895 map.insert("lat", d->m_vector.first().latitude(GeoDataCoordinates::Degree));
896 map.insert("alt", d->m_vector.first().altitude());
897 variantList << map;
898 }
899
900 return variantList;
901 }
902
pack(QDataStream & stream) const903 void GeoDataLineString::pack( QDataStream& stream ) const
904 {
905 Q_D(const GeoDataLineString);
906
907 GeoDataGeometry::pack( stream );
908
909 stream << size();
910 stream << (qint32)(d->m_tessellationFlags);
911
912 for( QVector<GeoDataCoordinates>::const_iterator iterator
913 = d->m_vector.constBegin();
914 iterator != d->m_vector.constEnd();
915 ++iterator ) {
916 mDebug() << "innerRing: size" << d->m_vector.size();
917 GeoDataCoordinates coord = ( *iterator );
918 coord.pack( stream );
919 }
920
921 }
922
unpack(QDataStream & stream)923 void GeoDataLineString::unpack( QDataStream& stream )
924 {
925 detach();
926
927 Q_D(GeoDataLineString);
928
929 GeoDataGeometry::unpack( stream );
930 qint32 size;
931 qint32 tessellationFlags;
932
933 stream >> size;
934 stream >> tessellationFlags;
935
936 d->m_tessellationFlags = (TessellationFlags)(tessellationFlags);
937
938 d->m_vector.reserve(d->m_vector.size() + size);
939
940 for(qint32 i = 0; i < size; i++ ) {
941 GeoDataCoordinates coord;
942 coord.unpack( stream );
943 d->m_vector.append( coord );
944 }
945 }
946
947 }
948