8#include "GeoDataLineString.h"
9#include "GeoDataLineString_p.h"
11#include "GeoDataLinearRing.h"
12#include "GeoDataTypes.h"
13#include "Quaternion.h"
14#include "MarbleDebug.h"
42 mDebug() <<
"delete Linestring";
48 return GeoDataTypes::GeoDataLineStringType;
51EnumGeometryId GeoDataLineString::geometryId()
const
53 return GeoDataLineStringId;
56GeoDataGeometry *GeoDataLineString::copy()
const
61void GeoDataLineStringPrivate::interpolateDateLine(
const GeoDataCoordinates & previousCoords,
62 const GeoDataCoordinates & currentCoords,
63 GeoDataCoordinates & previousAtDateLine,
64 GeoDataCoordinates & currentAtDateLine,
65 TessellationFlags f )
const
67 GeoDataCoordinates dateLineCoords;
71 if ( f.testFlag( RespectLatitudeCircle ) && previousCoords.latitude() == currentCoords.latitude() ) {
72 dateLineCoords = currentCoords;
75 int recursionCounter = 0;
76 dateLineCoords = findDateLine( previousCoords, currentCoords, recursionCounter );
79 previousAtDateLine = dateLineCoords;
80 currentAtDateLine = dateLineCoords;
82 if ( previousCoords.longitude() < 0 ) {
83 previousAtDateLine.setLongitude( -M_PI );
84 currentAtDateLine.setLongitude( +M_PI );
87 previousAtDateLine.setLongitude( +M_PI );
88 currentAtDateLine.setLongitude( -M_PI );
92GeoDataCoordinates GeoDataLineStringPrivate::findDateLine(
const GeoDataCoordinates & previousCoords,
93 const GeoDataCoordinates & currentCoords,
94 int recursionCounter )
const
96 int currentSign = ( currentCoords.longitude() < 0.0 ) ? -1 : +1 ;
97 int previousSign = ( previousCoords.longitude() < 0.0 ) ? -1 : +1 ;
99 qreal longitudeDiff = fabs( previousSign * M_PI - previousCoords.longitude() )
100 + fabs( currentSign * M_PI - currentCoords.longitude() );
102 if ( longitudeDiff < 0.001 || recursionCounter == 100 ) {
104 return currentCoords;
108 const GeoDataCoordinates interpolatedCoords = previousCoords.nlerp(currentCoords, 0.5);
110 int interpolatedSign = ( interpolatedCoords.longitude() < 0.0 ) ? -1 : +1 ;
118 if ( interpolatedSign != currentSign ) {
119 return findDateLine( interpolatedCoords, currentCoords, recursionCounter );
122 return findDateLine( previousCoords, interpolatedCoords, recursionCounter );
125quint8 GeoDataLineStringPrivate::levelForResolution(qreal resolution)
const {
126 if (m_previousResolution == resolution)
return m_level;
128 m_previousResolution = resolution;
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;
151qreal GeoDataLineStringPrivate::resolutionForLevel(
int level)
194void GeoDataLineStringPrivate::optimize (GeoDataLineString& lineString)
const
200 if (lineString.size() < 2)
return;
203 quint8 startLevel = levelForResolution( ( lineString.latLonAltBox().width() + lineString.latLonAltBox().height() ) / 2 );
205 quint8 currentLevel = startLevel;
206 quint8 maxLevel = startLevel;
207 GeoDataCoordinates currentCoords;
208 lineString.first().setDetail(startLevel);
223 while ( currentLevel < 16 && currentLevel <= maxLevel + 1 ) {
224 itCoords = lineString.begin();
226 currentCoords = *itCoords;
229 for( ; itCoords != itEnd; ++itCoords) {
230 if (itCoords->detail() != 0 && itCoords->detail() < currentLevel)
continue;
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;
239 if (currentCoords.sphericalDistanceTo(*itCoords) < resolutionForLevel(currentLevel + 1)) {
240 itCoords->setDetail(currentLevel + 1);
243 itCoords->setDetail(currentLevel);
244 currentCoords = *itCoords;
245 maxLevel = currentLevel;
250 lineString.last().setDetail(startLevel);
256 return d->m_vector.isEmpty();
262 return d->m_vector.size();
270 d->m_dirtyRange =
true;
271 d->m_dirtyBox =
true;
272 return d->m_vector[pos];
278 return d->m_vector.at(pos);
286 d->m_dirtyRange =
true;
287 d->m_dirtyBox =
true;
288 return d->m_vector[pos];
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;
306 return d->m_vector[pos];
314 d->m_dirtyRange =
true;
315 d->m_dirtyBox =
true;
316 return d->m_vector.last();
324 return d->m_vector.first();
330 return d->m_vector.last();
336 return d->m_vector.first();
344 return d->m_vector.begin();
350 return d->m_vector.constBegin();
358 return d->m_vector.end();
364 return d->m_vector.constEnd();
370 return d->m_vector.constBegin();
376 return d->m_vector.constEnd();
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 );
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 );
406 d->m_vector.reserve(
size);
414 delete d->m_rangeCorrected;
415 d->m_rangeCorrected =
nullptr;
416 d->m_dirtyRange =
true;
417 d->m_dirtyBox =
true;
419 d->m_vector.append(values);
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 );
440 delete d->m_rangeCorrected;
441 d->m_rangeCorrected =
nullptr;
442 d->m_dirtyRange =
true;
443 d->m_dirtyBox =
true;
448 d->m_vector.reserve(d->m_vector.size() + value.
size());
449 for( ; itCoords != itEnd; ++itCoords ) {
450 d->m_vector.append( *itCoords );
458 if ( !GeoDataGeometry::equals(other) ||
465 const GeoDataLineStringPrivate* other_d = other.d_func();
472 for ( ; itCoords != itEnd && otherItCoords != otherItEnd; ++itCoords, ++otherItCoords ) {
473 if ( *itCoords != *otherItCoords ) {
478 Q_ASSERT ( itCoords == itEnd && otherItCoords == otherItEnd );
492 delete d->m_rangeCorrected;
493 d->m_rangeCorrected =
nullptr;
494 d->m_dirtyRange =
true;
495 d->m_dirtyBox =
true;
508 return d->m_tessellationFlags.testFlag(Tessellate);
522 d->m_tessellationFlags |= (Tessellate | RespectLatitudeCircle);
524 d->m_tessellationFlags &= ~(Tessellate | RespectLatitudeCircle);
531 return d->m_tessellationFlags;
539 d->m_tessellationFlags = f;
547 delete d->m_rangeCorrected;
548 d->m_rangeCorrected =
nullptr;
549 d->m_dirtyRange =
true;
550 d->m_dirtyBox =
true;
569 = d->m_vector.constBegin();
573 itCoords->geoCoordinates( lon, lat );
574 qreal alt = itCoords->altitude();
578 normalizedCoords.
set( lon, lat, alt );
579 normalizedLineString << normalizedCoords;
582 return normalizedLineString;
589 if (d->m_dirtyRange) {
591 delete d->m_rangeCorrected;
598 d->m_dirtyRange =
false;
601 return *d->m_rangeCorrected;
610 d->toDateLineCorrected(*
this, lineStrings);
622 return poleCorrected;
626 return poleCorrected;
638 if ( !( m_vector.first().isPole() ) &&
639 ( m_vector.last().isPole() ) ) {
640 qreal firstLongitude = ( m_vector.first() ).longitude();
642 modifiedCoords.setLongitude( firstLongitude );
643 poleCorrected << modifiedCoords;
650 for( ; itCoords != itEnd; ++itCoords ) {
652 currentCoords = *itCoords;
654 if ( itCoords == m_vector.constBegin() ) {
655 previousCoords = currentCoords;
658 if ( currentCoords.
isPole() ) {
659 if ( previousCoords.
isPole() ) {
663 qreal previousLongitude = previousCoords.
longitude();
664 GeoDataCoordinates currentModifiedCoords( currentCoords );
665 currentModifiedCoords.setLongitude( previousLongitude );
666 poleCorrected << currentModifiedCoords;
670 if ( previousCoords.
isPole() ) {
671 qreal currentLongitude = currentCoords.
longitude();
672 GeoDataCoordinates previousModifiedCoords( previousCoords );
673 previousModifiedCoords.setLongitude( currentLongitude );
674 poleCorrected << previousModifiedCoords;
675 poleCorrected << currentCoords;
679 poleCorrected << currentCoords;
682 previousCoords = currentCoords;
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;
696void GeoDataLineStringPrivate::toDateLineCorrected(
697 const GeoDataLineString & q,
701 const bool isClosed = q.isClosed();
708 TessellationFlags f = q.tessellationFlags();
710 GeoDataLineString * unfinishedLineString =
nullptr;
712 GeoDataLineString * dateLineCorrected = isClosed ?
new GeoDataLinearRing( f )
713 : new GeoDataLineString( f );
715 qreal previousLon = 0.0;
716 int previousSign = 1;
718 bool unfinished =
false;
720 for (; itPoint != itEndPoint; ++itPoint ) {
721 const qreal currentLon = itPoint->longitude();
723 int currentSign = ( currentLon < 0.0 ) ? -1 : +1 ;
725 if( itPoint == q.constBegin() ) {
726 previousSign = currentSign;
727 previousLon = currentLon;
731 if ( previousSign != currentSign && fabs(previousLon) + fabs(currentLon) > M_PI ) {
733 unfinished = !unfinished;
735 GeoDataCoordinates previousTemp;
736 GeoDataCoordinates currentTemp;
738 interpolateDateLine( *itPreviousPoint, *itPoint,
739 previousTemp, currentTemp, q.tessellationFlags() );
741 *dateLineCorrected << previousTemp;
743 if ( isClosed && unfinished ) {
746 unfinishedLineString = dateLineCorrected;
748 dateLineCorrected =
new GeoDataLinearRing( f );
753 if ( dateLineCorrected->size() > 0 ) {
754 lineStrings << dateLineCorrected;
758 delete dateLineCorrected;
762 if ( isClosed && !unfinished && unfinishedLineString ) {
763 dateLineCorrected = unfinishedLineString;
767 dateLineCorrected =
new GeoDataLineString( f );
771 *dateLineCorrected << currentTemp;
772 *dateLineCorrected << *itPoint;
776 *dateLineCorrected << *itPoint;
779 previousSign = currentSign;
780 previousLon = currentLon;
781 itPreviousPoint = itPoint;
786 if ( unfinished && unfinishedLineString && !unfinishedLineString->isEmpty() ) {
787 *dateLineCorrected << *unfinishedLineString;
788 delete unfinishedLineString;
791 lineStrings << dateLineCorrected;
804 d->m_dirtyBox =
false;
807 return d->m_latLonAltBox;
812 if( offset < 0 || offset >=
size() ) {
819 int const start = qMax(offset+1, 1);
820 int const end = d->m_vector.size();
823 length += vector[i-1].sphericalDistanceTo(vector[i]);
826 return planetRadius *
length;
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 );
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 );
859 d->m_dirtyRange =
true;
860 d->m_dirtyBox =
true;
861 d->m_vector.remove( i );
870 d->optimize(linearRing);
874 d->optimize(lineString);
883 QVariantList variantList;
886 map.insert(
"lon", itCoords.longitude(GeoDataCoordinates::Degree));
887 map.insert(
"lat", itCoords.latitude(GeoDataCoordinates::Degree));
888 map.insert(
"alt", itCoords.altitude());
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());
910 stream << (qint32)(d->m_tessellationFlags);
913 = d->m_vector.constBegin();
914 iterator != d->m_vector.constEnd();
916 mDebug() <<
"innerRing: size" << d->m_vector.size();
918 coord.
pack( stream );
938 d->m_vector.reserve(d->m_vector.size() +
size);
940 for(qint32 i = 0; i <
size; i++ ) {
943 d->m_vector.append( coord );
A 3d point representation.
bool isPole(Pole=AnyPole) const
return whether our coordinates represent a pole This method can be used to check whether the coordina...
void unpack(QDataStream &stream)
Unserialize the contents of the feature from stream.
void set(qreal lon, qreal lat, qreal alt=0, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
(re)set the coordinates in a GeoDataCoordinates object
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.
qreal longitude(GeoDataCoordinates::Unit unit) const
retrieves the longitude of the GeoDataCoordinates object use the unit parameter to switch between Rad...
A base class for all geodata features.
void pack(QDataStream &stream) const override
Serialize the contents of the feature to stream.
void unpack(QDataStream &stream) override
Unserialize the contents of the feature from stream.
A class that defines a 3D bounding box for geographic data.
static GeoDataLatLonAltBox fromLineString(const GeoDataLineString &lineString)
Create the smallest bounding box from a line string.
A LineString that allows to store a contiguous set of line segments.
QVector< GeoDataCoordinates >::Iterator erase(const QVector< GeoDataCoordinates >::Iterator &position)
Removes the node at the given position and returns it.
virtual bool isClosed() const
Returns whether a LineString is a closed polygon.
bool isEmpty() const
Returns whether the LineString has no nodes at all.
GeoDataLineString(TessellationFlags f=NoTessellation)
Creates a new LineString.
QVector< GeoDataCoordinates >::Iterator end()
Returns an iterator that points to the end of the LineString.
QVector< GeoDataCoordinates >::ConstIterator constEnd() const
Returns a const iterator that points to the end of the LineString.
void insert(int index, const GeoDataCoordinates &value)
Inserts a new node at the given index.
void reverse()
Reverses the LineString.
void remove(int i)
Removes the node at the given position and destroys it.
void unpack(QDataStream &stream) override
Unserialize the LineString from a stream.
void setTessellationFlags(TessellationFlags f)
Sets the given tessellation flags for a LineString.
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...
GeoDataCoordinates & at(int pos)
Returns a reference to the coordinates of a node at a given position. This method detaches the return...
int size() const
Returns the number of nodes in a LineString.
QVariantList toVariantList() const
Returns a javascript-style list (that can be used e.g. with the QML GeoPolyline element).
GeoDataLineString optimized() const
Returns a linestring with detail values assigned to each node.
GeoDataLineString mid(int pos, int length=-1) const
Returns a sub-string which contains elements from this vector, starting at position pos.
void clear()
Destroys all nodes in a LineString.
const GeoDataLatLonAltBox & latLonAltBox() const override
Returns the smallest latLonAltBox that contains the LineString.
virtual GeoDataLineString toRangeCorrected() const
Provides a more generic representation of the LineString.
~GeoDataLineString() override
Destroys a LineString.
GeoDataLineString & operator<<(const GeoDataCoordinates &value)
Appends a given geodesic position as a new node to the LineString.
const char * nodeType() const override
Provides type information for downcasting a GeoNode.
bool operator==(const GeoDataLineString &other) const
Returns true/false depending on whether this and other are/are not equal.
virtual GeoDataLineString toNormalized() const
The line string with nodes that have proper longitude/latitude ranges.
void pack(QDataStream &stream) const override
Serialize the LineString to a stream.
GeoDataCoordinates & operator[](int pos)
Returns a reference to the coordinates of a node at a given position. This method detaches the return...
void setTessellate(bool tessellate)
Sets the tessellation property for the LineString.
virtual QVector< GeoDataLineString * > toDateLineCorrected() const
The line string corrected for date line crossing.
void append(const GeoDataCoordinates &value)
Appends a given geodesic position as a new node to the LineString.
virtual GeoDataLineString toPoleCorrected() const
The line string with more generic pole values.
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...
GeoDataCoordinates & last()
Returns a reference to the last node in the LineString. This method detaches the returned coordinate ...
QVector< GeoDataCoordinates >::ConstIterator constBegin() const
Returns a const iterator that points to the begin of the LineString.
QVector< GeoDataCoordinates >::Iterator begin()
Returns an iterator that points to the begin of the LineString.
void reserve(int size)
Attempts to allocate memory for at least size coordinates.
bool tessellate() const
Returns whether the LineString follows the earth's surface.
A LinearRing that allows to store a closed, contiguous set of line segments.
Q_SCRIPTABLE Q_NOREPLY void start()
Binds a QML item to a specific geodetic location in screen coordinates.