Marble

GeoDataLineString.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2008 Torsten Rahn <[email protected]>
4 // SPDX-FileCopyrightText: 2009 Patrick Spendrin <[email protected]>
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 {
22  : GeoDataGeometry( new GeoDataLineStringPrivate( f ) )
23 {
24 // mDebug() << "1) GeoDataLineString created:" << p();
25 }
26 
27 GeoDataLineString::GeoDataLineString( GeoDataLineStringPrivate* priv )
28  : GeoDataGeometry( priv )
29 {
30 // mDebug() << "2) GeoDataLineString created:" << p();
31 }
32 
34  : GeoDataGeometry( other )
35 {
36 // mDebug() << "3) GeoDataLineString created:" << p();
37 }
38 
40 {
41 #ifdef DEBUG_GEODATA
42  mDebug() << "delete Linestring";
43 #endif
44 }
45 
46 const char *GeoDataLineString::nodeType() const
47 {
48  return GeoDataTypes::GeoDataLineStringType;
49 }
50 
51 EnumGeometryId GeoDataLineString::geometryId() const
52 {
53  return GeoDataLineStringId;
54 }
55 
56 GeoDataGeometry *GeoDataLineString::copy() const
57 {
58  return new GeoDataLineString(*this);
59 }
60 
61 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 
92 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 
125 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 
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 
194 void GeoDataLineStringPrivate::optimize (GeoDataLineString& lineString) const
195 {
196 
197  QVector<GeoDataCoordinates>::iterator itCoords = lineString.begin();
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 
254 {
255  Q_D(const GeoDataLineString);
256  return d->m_vector.isEmpty();
257 }
258 
260 {
261  Q_D(const GeoDataLineString);
262  return d->m_vector.size();
263 }
264 
266 {
267  detach();
268 
270  d->m_dirtyRange = true;
271  d->m_dirtyBox = true;
272  return d->m_vector[pos];
273 }
274 
276 {
277  Q_D(const GeoDataLineString);
278  return d->m_vector.at(pos);
279 }
280 
282 {
283  detach();
284 
286  d->m_dirtyRange = true;
287  d->m_dirtyBox = true;
288  return d->m_vector[pos];
289 }
290 
291 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 
304 {
305  Q_D(const GeoDataLineString);
306  return d->m_vector[pos];
307 }
308 
310 {
311  detach();
312 
314  d->m_dirtyRange = true;
315  d->m_dirtyBox = true;
316  return d->m_vector.last();
317 }
318 
320 {
321  detach();
322 
324  return d->m_vector.first();
325 }
326 
328 {
329  Q_D(const GeoDataLineString);
330  return d->m_vector.last();
331 }
332 
333 const GeoDataCoordinates& GeoDataLineString::first() const
334 {
335  Q_D(const GeoDataLineString);
336  return d->m_vector.first();
337 }
338 
340 {
341  detach();
342 
344  return d->m_vector.begin();
345 }
346 
348 {
349  Q_D(const GeoDataLineString);
350  return d->m_vector.constBegin();
351 }
352 
354 {
355  detach();
356 
358  return d->m_vector.end();
359 }
360 
362 {
363  Q_D(const GeoDataLineString);
364  return d->m_vector.constEnd();
365 }
366 
368 {
369  Q_D(const GeoDataLineString);
370  return d->m_vector.constBegin();
371 }
372 
374 {
375  Q_D(const GeoDataLineString);
376  return d->m_vector.constEnd();
377 }
378 
379 void GeoDataLineString::insert( int index, const GeoDataCoordinates& value )
380 {
381  detach();
382 
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 
392 {
393  detach();
394 
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 
404 {
406  d->m_vector.reserve(size);
407 }
408 
410 {
411  detach();
412 
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 
423 {
424  detach();
425 
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 
436 {
437  detach();
438 
440  delete d->m_rangeCorrected;
441  d->m_rangeCorrected = nullptr;
442  d->m_dirtyRange = true;
443  d->m_dirtyBox = true;
444 
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 
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 
468  QVector<GeoDataCoordinates>::const_iterator otherItCoords = other_d->m_vector.constBegin();
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 
482 bool GeoDataLineString::operator!=( const GeoDataLineString &other ) const
483 {
484  return !this->operator==(other);
485 }
486 
488 {
489  detach();
490 
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 
501 {
502  return false;
503 }
504 
506 {
507  Q_D(const GeoDataLineString);
508  return d->m_tessellationFlags.testFlag(Tessellate);
509 }
510 
511 void GeoDataLineString::setTessellate( bool tessellate )
512 {
513  detach();
514 
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 
529 {
530  Q_D(const GeoDataLineString);
531  return d->m_tessellationFlags;
532 }
533 
535 {
536  detach();
537 
539  d->m_tessellationFlags = f;
540 }
541 
543 {
544  detach();
545 
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 
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();
569  = d->m_vector.constBegin();
570  itCoords != end;
571  ++itCoords ) {
572 
573  itCoords->geoCoordinates( lon, lat );
574  qreal alt = itCoords->altitude();
576 
577  GeoDataCoordinates normalizedCoords( *itCoords );
578  normalizedCoords.set( lon, lat, alt );
579  normalizedLineString << normalizedCoords;
580  }
581 
582  return normalizedLineString;
583 }
584 
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 
605 {
606  Q_D(const GeoDataLineString);
607 
608  QVector<GeoDataLineString*> lineStrings;
609 
610  d->toDateLineCorrected(*this, lineStrings);
611 
612  return lineStrings;
613 }
614 
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 
630 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 
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 
696 void GeoDataLineStringPrivate::toDateLineCorrected(
697  const GeoDataLineString & q,
698  QVector<GeoDataLineString*> & lineStrings
699  ) const
700 {
701  const bool isClosed = q.isClosed();
702 
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 
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 
810 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 
830 {
831  detach();
832 
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 
843 {
844  detach();
845 
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 
855 {
856  detach();
857 
859  d->m_dirtyRange = true;
860  d->m_dirtyBox = true;
861  d->m_vector.remove( i );
862 }
863 
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 
880 {
881  Q_D(const GeoDataLineString);
882 
883  QVariantList variantList;
884  for( const GeoDataCoordinates & itCoords : qAsConst(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 
904 {
905  Q_D(const GeoDataLineString);
906 
907  GeoDataGeometry::pack( stream );
908 
909  stream << size();
910  stream << (qint32)(d->m_tessellationFlags);
911 
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 
924 {
925  detach();
926 
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 }
A 3d point representation.
virtual QVector< GeoDataLineString * > toDateLineCorrected() const
The line string corrected for date line crossing.
A class that defines a 3D bounding box for geographic data.
void append(const GeoDataCoordinates &value)
Appends a given geodesic position as a new node to the LineString.
T & last()
void clear()
Destroys all nodes in a LineString.
QVector::iterator begin()
virtual GeoDataLineString toRangeCorrected() const
Provides a more generic representation of the LineString.
void set(qreal lon, qreal lat, qreal alt=0, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
(re)set the coordinates in a GeoDataCoordinates object
GeoDataLineString(TessellationFlags f=NoTessellation)
Creates a new LineString.
QVector::const_iterator constEnd() const const
virtual GeoDataLineString toPoleCorrected() const
The line string with more generic pole values.
A base class for all geodata features.
const char * nodeType() const override
Provides type information for downcasting a GeoNode.
qreal longitude(GeoDataCoordinates::Unit unit) const
retrieves the longitude of the GeoDataCoordinates object use the unit parameter to switch between Rad...
QVariantList toVariantList() const
Returns a javascript-style list (that can be used e.g. with the QML GeoPolyline element).
QVector< GeoDataCoordinates >::ConstIterator constBegin() const
Returns a const iterator that points to the begin of the LineString.
void reserve(int size)
Attempts to allocate memory for at least size coordinates.
void reverse()
Reverses the LineString.
void unpack(QDataStream &stream) override
Unserialize the contents of the feature from stream.
bool tessellate() const
Returns whether the LineString follows the earth's surface.
void unpack(QDataStream &stream)
Unserialize the contents of the feature from stream.
GeoDataCoordinates & last()
Returns a reference to the last node in the LineString. This method detaches the returned coordinate ...
Q_SCRIPTABLE Q_NOREPLY void start()
GeoDataLineString & operator<<(const GeoDataCoordinates &value)
Appends a given geodesic position as a new node to the LineString.
A LineString that allows to store a contiguous set of line segments.
const GeoDataLatLonAltBox & latLonAltBox() const override
Returns the smallest latLonAltBox that contains the LineString.
bool isEmpty() const
Returns whether the LineString has no nodes at all.
QVector< GeoDataCoordinates >::ConstIterator constEnd() const
Returns a const iterator that points to the end of the LineString.
virtual qreal length(qreal planetRadius, int offset=0) const
Returns the length of LineString across a sphere starting from a coordinate in LineString This method...
Binds a QML item to a specific geodetic location in screen coordinates.
void pack(QDataStream &stream) const override
Serialize the LineString to a stream.
TessellationFlags tessellationFlags() const
Returns the tessellation flags for a LineString.
GeoDataCoordinates & first()
Returns a reference to the first node in the LineString. This method detaches the returned coordinate...
bool operator==(const GeoDataLineString &other) const
Returns true/false depending on whether this and other are/are not equal.
GeoDataCoordinates & at(int pos)
Returns a reference to the coordinates of a node at a given position. This method detaches the return...
GeoDataCoordinates & operator[](int pos)
Returns a reference to the coordinates of a node at a given position. This method detaches the return...
virtual GeoDataLineString toNormalized() const
The line string with nodes that have proper longitude/latitude ranges.
bool isPole(Pole=AnyPole) const
return whether our coordinates represent a pole This method can be used to check whether the coordina...
QVector< GeoDataCoordinates >::Iterator begin()
Returns an iterator that points to the begin of the LineString.
void unpack(QDataStream &stream) override
Unserialize the LineString from a stream.
QVector< GeoDataCoordinates >::Iterator end()
Returns an iterator that points to the end of the LineString.
QVector< GeoDataCoordinates >::Iterator erase(const QVector< GeoDataCoordinates >::Iterator &position)
Removes the node at the given position and returns it.
static GeoDataLatLonAltBox fromLineString(const GeoDataLineString &lineString)
Create the smallest bounding box from a line string.
virtual bool isClosed() const
Returns whether a LineString is a closed polygon.
~GeoDataLineString() override
Destroys a LineString.
QVector::const_iterator constBegin() const const
GeoDataLineString mid(int pos, int length=-1) const
Returns a sub-string which contains elements from this vector, starting at position pos.
void pack(QDataStream &stream) const override
Serialize the contents of the feature to stream.
static void normalizeLonLat(qreal &lon, qreal &lat, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize both longitude and latitude at the same time This method normalizes both latitude and longi...
void pack(QDataStream &stream) const
Serialize the contents of the feature to stream.
void setTessellationFlags(TessellationFlags f)
Sets the given tessellation flags for a LineString.
void insert(int index, const GeoDataCoordinates &value)
Inserts a new node at the given index.
void remove(int i)
Removes the node at the given position and destroys it.
void setTessellate(bool tessellate)
Sets the tessellation property for the LineString.
A LinearRing that allows to store a closed, contiguous set of line segments.
QVector< V > values(const QMultiHash< K, V > &c)
Q_D(Todo)
int size() const
Returns the number of nodes in a LineString.
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
GeoDataLineString optimized() const
Returns a linestring with detail values assigned to each node.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Oct 2 2023 03:52:08 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.