1 /***************************************************************************
2                          qgspointv2.cpp
3                          --------------
4     begin                : September 2014
5     copyright            : (C) 2014 by Marco Hugentobler
6     email                : marco at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 
19 #include "qgspoint.h"
20 #include "qgsapplication.h"
21 #include "qgscoordinatetransform.h"
22 #include "qgsgeometryutils.h"
23 #include "qgsmaptopixel.h"
24 #include "qgswkbptr.h"
25 #include "qgsgeometrytransformer.h"
26 
27 #include <cmath>
28 #include <QPainter>
29 #include <QPainterPath>
30 #include <QRegularExpression>
31 #include <QJsonObject>
32 #include <QJsonArray>
33 #include <nlohmann/json.hpp>
34 
35 /***************************************************************************
36  * This class is considered CRITICAL and any change MUST be accompanied with
37  * full unit tests.
38  * See details in QEP #17
39  ****************************************************************************/
40 
QgsPoint(double x,double y,double z,double m,QgsWkbTypes::Type wkbType)41 QgsPoint::QgsPoint( double x, double y, double z, double m, QgsWkbTypes::Type wkbType )
42   : mX( x )
43   , mY( y )
44   , mZ( z )
45   , mM( m )
46 {
47   if ( wkbType != QgsWkbTypes::Unknown )
48   {
49     Q_ASSERT( QgsWkbTypes::flatType( wkbType ) == QgsWkbTypes::Point );
50     mWkbType = wkbType;
51   }
52   else if ( std::isnan( z ) )
53   {
54     if ( std::isnan( m ) )
55       mWkbType = QgsWkbTypes::Point;
56     else
57       mWkbType = QgsWkbTypes::PointM;
58   }
59   else if ( std::isnan( m ) )
60     mWkbType = QgsWkbTypes::PointZ;
61   else
62     mWkbType = QgsWkbTypes::PointZM;
63 }
64 
QgsPoint(const QgsPointXY & p)65 QgsPoint::QgsPoint( const QgsPointXY &p )
66   : mX( p.x() )
67   , mY( p.y() )
68   , mZ( std::numeric_limits<double>::quiet_NaN() )
69   , mM( std::numeric_limits<double>::quiet_NaN() )
70 {
71   mWkbType = QgsWkbTypes::Point;
72   if ( p.isEmpty() )
73   {
74     mX = std::numeric_limits<double>::quiet_NaN();
75     mY = std::numeric_limits<double>::quiet_NaN();
76   }
77 }
78 
QgsPoint(QPointF p)79 QgsPoint::QgsPoint( QPointF p )
80   : mX( p.x() )
81   , mY( p.y() )
82   , mZ( std::numeric_limits<double>::quiet_NaN() )
83   , mM( std::numeric_limits<double>::quiet_NaN() )
84 {
85   mWkbType = QgsWkbTypes::Point;
86 }
87 
QgsPoint(QgsWkbTypes::Type wkbType,double x,double y,double z,double m)88 QgsPoint::QgsPoint( QgsWkbTypes::Type wkbType, double x, double y, double z, double m )
89   : mX( x )
90   , mY( y )
91   , mZ( QgsWkbTypes::hasZ( wkbType ) ? z : std::numeric_limits<double>::quiet_NaN() )
92   , mM( QgsWkbTypes::hasM( wkbType ) ? m : std::numeric_limits<double>::quiet_NaN() )
93 {
94   Q_ASSERT( QgsWkbTypes::flatType( wkbType ) == QgsWkbTypes::Point );
95   mWkbType = wkbType;
96 }
97 
98 /***************************************************************************
99  * This class is considered CRITICAL and any change MUST be accompanied with
100  * full unit tests.
101  * See details in QEP #17
102  ****************************************************************************/
103 
clone() const104 QgsPoint *QgsPoint::clone() const
105 {
106   return new QgsPoint( *this );
107 }
108 
snappedToGrid(double hSpacing,double vSpacing,double dSpacing,double mSpacing) const109 QgsPoint *QgsPoint::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
110 {
111   // helper function
112   auto gridifyValue = []( double value, double spacing, bool extraCondition = true ) -> double
113   {
114     if ( spacing > 0 && extraCondition )
115       return  std::round( value / spacing ) * spacing;
116     else
117       return value;
118   };
119 
120   // Get the new values
121   const auto x = gridifyValue( mX, hSpacing );
122   const auto y = gridifyValue( mY, vSpacing );
123   const auto z = gridifyValue( mZ, dSpacing, QgsWkbTypes::hasZ( mWkbType ) );
124   const auto m = gridifyValue( mM, mSpacing, QgsWkbTypes::hasM( mWkbType ) );
125 
126   // return the new object
127   return new QgsPoint( mWkbType, x, y, z, m );
128 }
129 
removeDuplicateNodes(double,bool)130 bool QgsPoint::removeDuplicateNodes( double, bool )
131 {
132   return false;
133 }
134 
fromWkb(QgsConstWkbPtr & wkbPtr)135 bool QgsPoint::fromWkb( QgsConstWkbPtr &wkbPtr )
136 {
137   const QgsWkbTypes::Type type = wkbPtr.readHeader();
138   if ( QgsWkbTypes::flatType( type ) != QgsWkbTypes::Point )
139   {
140     clear();
141     return false;
142   }
143   mWkbType = type;
144 
145   wkbPtr >> mX;
146   wkbPtr >> mY;
147   if ( is3D() )
148     wkbPtr >> mZ;
149   if ( isMeasure() )
150     wkbPtr >> mM;
151 
152   clearCache();
153 
154   return true;
155 }
156 
157 /***************************************************************************
158  * This class is considered CRITICAL and any change MUST be accompanied with
159  * full unit tests.
160  * See details in QEP #17
161  ****************************************************************************/
162 
fromWkt(const QString & wkt)163 bool QgsPoint::fromWkt( const QString &wkt )
164 {
165   clear();
166 
167   QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
168 
169   if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::Point )
170     return false;
171   mWkbType = parts.first;
172 
173   QString secondWithoutParentheses = parts.second;
174   secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
175   parts.second = parts.second.remove( '(' ).remove( ')' );
176   if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
177        secondWithoutParentheses.isEmpty() )
178     return true;
179 
180   const thread_local QRegularExpression rx( QStringLiteral( "\\s" ) );
181 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
182   QStringList coordinates = parts.second.split( rx, QString::SkipEmptyParts );
183 #else
184   QStringList coordinates = parts.second.split( rx, Qt::SkipEmptyParts );
185 #endif
186 
187   // So far the parser hasn't looked at the coordinates. We'll avoid having anything but numbers and return NULL instead of 0 as a coordinate.
188   // Without this check, "POINT (a, b)" or "POINT (( 4, 3 ))" returned "POINT (0 ,0)"
189   // And some strange conversion...
190   // .. python:
191   // p = QgsPoint()
192   // p.fromWkt("POINT (-3.12, -4.2")
193   // False
194   // p.fromWkt( "POINT (-5.1234, -1.4321)" )
195   // True
196   // p.asWkt()
197   // 'Point (0 -1.43209999999999993)'
198   const thread_local QRegularExpression rxIsNumber( QStringLiteral( "^[+-]?(\\d\\.?\\d*[Ee][+\\-]?\\d+|(\\d+\\.\\d*|\\d*\\.\\d+)|\\d+)$" ) );
199   if ( coordinates.filter( rxIsNumber ).size() != coordinates.size() )
200     return false;
201 
202   if ( coordinates.size() < 2 )
203   {
204     clear();
205     return false;
206   }
207   else if ( coordinates.size() == 3 && !is3D() && !isMeasure() )
208   {
209     // 3 dimensional coordinates, but not specifically marked as such. We allow this
210     // anyway and upgrade geometry to have Z dimension
211     mWkbType = QgsWkbTypes::addZ( mWkbType );
212   }
213   else if ( coordinates.size() >= 4 && ( !is3D() || !isMeasure() ) )
214   {
215     // 4 (or more) dimensional coordinates, but not specifically marked as such. We allow this
216     // anyway and upgrade geometry to have Z&M dimensions
217     mWkbType = QgsWkbTypes::addZ( mWkbType );
218     mWkbType = QgsWkbTypes::addM( mWkbType );
219   }
220 
221   int idx = 0;
222   mX = coordinates[idx++].toDouble();
223   mY = coordinates[idx++].toDouble();
224   if ( is3D() && coordinates.length() > 2 )
225     mZ = coordinates[idx++].toDouble();
226   if ( isMeasure() && coordinates.length() > 2 + is3D() )
227     mM = coordinates[idx++].toDouble();
228 
229   return true;
230 }
231 
232 /***************************************************************************
233  * This class is considered CRITICAL and any change MUST be accompanied with
234  * full unit tests.
235  * See details in QEP #17
236  ****************************************************************************/
237 
wkbSize(WkbFlags) const238 int QgsPoint::wkbSize( WkbFlags ) const
239 {
240   int binarySize = sizeof( char ) + sizeof( quint32 );
241   binarySize += ( 2 + is3D() + isMeasure() ) * sizeof( double );
242   return binarySize;
243 }
244 
asWkb(WkbFlags flags) const245 QByteArray QgsPoint::asWkb( WkbFlags flags ) const
246 {
247   QByteArray wkbArray;
248   wkbArray.resize( QgsPoint::wkbSize( flags ) );
249   QgsWkbPtr wkb( wkbArray );
250   wkb << static_cast<char>( QgsApplication::endian() );
251   wkb << static_cast<quint32>( wkbType() );
252   wkb << mX << mY;
253   if ( is3D() )
254   {
255     wkb << mZ;
256   }
257   if ( isMeasure() )
258   {
259     wkb << mM;
260   }
261   return wkbArray;
262 }
263 
asWkt(int precision) const264 QString QgsPoint::asWkt( int precision ) const
265 {
266   QString wkt = wktTypeStr();
267 
268   if ( isEmpty() )
269     wkt += QLatin1String( " EMPTY" );
270   else
271   {
272     wkt += QLatin1String( " (" );
273     wkt += qgsDoubleToString( mX, precision ) + ' ' + qgsDoubleToString( mY, precision );
274     if ( is3D() )
275       wkt += ' ' + qgsDoubleToString( mZ, precision );
276     if ( isMeasure() )
277       wkt += ' ' + qgsDoubleToString( mM, precision );
278     wkt += ')';
279   }
280   return wkt;
281 }
282 
asGml2(QDomDocument & doc,int precision,const QString & ns,const QgsAbstractGeometry::AxisOrder axisOrder) const283 QDomElement QgsPoint::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
284 {
285   QDomElement elemPoint = doc.createElementNS( ns, QStringLiteral( "Point" ) );
286   QDomElement elemCoordinates = doc.createElementNS( ns, QStringLiteral( "coordinates" ) );
287 
288   // coordinate separator
289   const QString cs = QStringLiteral( "," );
290   // tuple separator
291   const QString ts = QStringLiteral( " " );
292 
293   elemCoordinates.setAttribute( QStringLiteral( "cs" ), cs );
294   elemCoordinates.setAttribute( QStringLiteral( "ts" ), ts );
295 
296   QString strCoordinates;
297   if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
298     strCoordinates = qgsDoubleToString( mX, precision ) + cs + qgsDoubleToString( mY, precision );
299   else
300     strCoordinates = qgsDoubleToString( mY, precision ) + cs + qgsDoubleToString( mX, precision );
301   elemCoordinates.appendChild( doc.createTextNode( strCoordinates ) );
302   elemPoint.appendChild( elemCoordinates );
303   return elemPoint;
304 }
305 
asGml3(QDomDocument & doc,int precision,const QString & ns,const QgsAbstractGeometry::AxisOrder axisOrder) const306 QDomElement QgsPoint::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
307 {
308   QDomElement elemPoint = doc.createElementNS( ns, QStringLiteral( "Point" ) );
309   QDomElement elemPosList = doc.createElementNS( ns, QStringLiteral( "pos" ) );
310   elemPosList.setAttribute( QStringLiteral( "srsDimension" ), is3D() ? 3 : 2 );
311   QString strCoordinates;
312   if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
313     strCoordinates = qgsDoubleToString( mX, precision ) + ' ' + qgsDoubleToString( mY, precision );
314   else
315     strCoordinates = qgsDoubleToString( mY, precision ) + ' ' + qgsDoubleToString( mX, precision );
316   if ( is3D() )
317     strCoordinates += ' ' + qgsDoubleToString( mZ, precision );
318 
319   elemPosList.appendChild( doc.createTextNode( strCoordinates ) );
320   elemPoint.appendChild( elemPosList );
321   return elemPoint;
322 }
323 
324 
asJsonObject(int precision) const325 json QgsPoint::asJsonObject( int precision ) const
326 {
327   json j
328   {
329     { "type", "Point" },
330     { "coordinates", json::array() },
331   };
332   if ( ! isEmpty() )
333   {
334     j["coordinates"].push_back( qgsRound( mX, precision ) );
335     j["coordinates"].push_back( qgsRound( mY, precision ) );
336     if ( is3D() )
337     {
338       j["coordinates"].push_back( qgsRound( mZ, precision ) );
339     }
340   }
341   return j;
342 }
343 
asKml(int precision) const344 QString QgsPoint::asKml( int precision ) const
345 {
346   return QStringLiteral( "<Point><coordinates>%1,%2</coordinates></Point>" ).arg( qgsDoubleToString( mX, precision ), qgsDoubleToString( mY, precision ) );
347 }
348 
draw(QPainter & p) const349 void QgsPoint::draw( QPainter &p ) const
350 {
351   p.drawRect( QRectF( mX - 2, mY - 2, 4, 4 ) );
352 }
353 
asQPainterPath() const354 QPainterPath QgsPoint::asQPainterPath() const
355 {
356   return QPainterPath();
357 }
358 
clear()359 void QgsPoint::clear()
360 {
361   mX = mY = std::numeric_limits<double>::quiet_NaN();
362   if ( is3D() )
363     mZ = 0.;
364   else
365     mZ = std::numeric_limits<double>::quiet_NaN();
366 
367   if ( isMeasure() )
368     mM = 0.;
369   else
370     mM = std::numeric_limits<double>::quiet_NaN();
371 
372   clearCache();
373 }
374 
375 
376 /***************************************************************************
377  * This class is considered CRITICAL and any change MUST be accompanied with
378  * full unit tests.
379  * See details in QEP #17
380  ****************************************************************************/
381 
transform(const QgsCoordinateTransform & ct,Qgis::TransformDirection d,bool transformZ)382 void QgsPoint::transform( const QgsCoordinateTransform &ct, Qgis::TransformDirection d, bool transformZ )
383 {
384   clearCache();
385   if ( transformZ )
386   {
387     ct.transformInPlace( mX, mY, mZ, d );
388   }
389   else
390   {
391     double z = 0.0;
392     ct.transformInPlace( mX, mY, z, d );
393   }
394 }
395 
coordinateSequence() const396 QgsCoordinateSequence QgsPoint::coordinateSequence() const
397 {
398   QgsCoordinateSequence cs;
399 
400   cs.append( QgsRingSequence() );
401   cs.back().append( QgsPointSequence() << QgsPoint( *this ) );
402 
403   return cs;
404 }
405 
nCoordinates() const406 int QgsPoint::nCoordinates() const
407 {
408   return 1;
409 }
410 
vertexNumberFromVertexId(QgsVertexId id) const411 int QgsPoint::vertexNumberFromVertexId( QgsVertexId id ) const
412 {
413   if ( id.vertex != 0 )
414     return -1;
415   else
416     return 0;
417 }
418 
boundary() const419 QgsAbstractGeometry *QgsPoint::boundary() const
420 {
421   return nullptr;
422 }
423 
isValid(QString &,Qgis::GeometryValidityFlags) const424 bool QgsPoint::isValid( QString &, Qgis::GeometryValidityFlags ) const
425 {
426   return true;
427 }
428 
insertVertex(QgsVertexId position,const QgsPoint & vertex)429 bool QgsPoint::insertVertex( QgsVertexId position, const QgsPoint &vertex )
430 {
431   Q_UNUSED( position )
432   Q_UNUSED( vertex )
433   return false;
434 }
435 
436 /***************************************************************************
437  * This class is considered CRITICAL and any change MUST be accompanied with
438  * full unit tests.
439  * See details in QEP #17
440  ****************************************************************************/
441 
moveVertex(QgsVertexId position,const QgsPoint & newPos)442 bool QgsPoint::moveVertex( QgsVertexId position, const QgsPoint &newPos )
443 {
444   Q_UNUSED( position )
445   clearCache();
446   mX = newPos.mX;
447   mY = newPos.mY;
448   if ( is3D() && newPos.is3D() )
449   {
450     mZ = newPos.mZ;
451   }
452   if ( isMeasure() && newPos.isMeasure() )
453   {
454     mM = newPos.mM;
455   }
456   return true;
457 }
458 
deleteVertex(QgsVertexId position)459 bool QgsPoint::deleteVertex( QgsVertexId position )
460 {
461   Q_UNUSED( position )
462   return false;
463 }
464 
closestSegment(const QgsPoint & pt,QgsPoint & segmentPt,QgsVertexId & vertexAfter,int * leftOf,double epsilon) const465 double QgsPoint::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt,  QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
466 {
467   Q_UNUSED( pt )
468   Q_UNUSED( segmentPt )
469   Q_UNUSED( vertexAfter )
470   if ( leftOf )
471     *leftOf = 0;
472   Q_UNUSED( epsilon )
473   return -1;  // no segments - return error
474 }
475 
nextVertex(QgsVertexId & id,QgsPoint & vertex) const476 bool QgsPoint::nextVertex( QgsVertexId &id, QgsPoint &vertex ) const
477 {
478   if ( id.vertex < 0 )
479   {
480     id.vertex = 0;
481     if ( id.part < 0 )
482     {
483       id.part = 0;
484     }
485     if ( id.ring < 0 )
486     {
487       id.ring = 0;
488     }
489     vertex = *this;
490     return true;
491   }
492   else
493   {
494     return false;
495   }
496 }
497 
adjacentVertices(QgsVertexId,QgsVertexId & previousVertex,QgsVertexId & nextVertex) const498 void QgsPoint::adjacentVertices( QgsVertexId, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
499 {
500   previousVertex = QgsVertexId();
501   nextVertex = QgsVertexId();
502 }
503 
vertexAngle(QgsVertexId vertex) const504 double QgsPoint::vertexAngle( QgsVertexId vertex ) const
505 {
506   Q_UNUSED( vertex )
507   return 0.0;
508 }
509 
vertexCount(int,int) const510 int QgsPoint::vertexCount( int, int ) const
511 {
512   return 1;
513 }
514 
ringCount(int) const515 int QgsPoint::ringCount( int ) const
516 {
517   return 1;
518 }
519 
partCount() const520 int QgsPoint::partCount() const
521 {
522   return 1;
523 }
524 
vertexAt(QgsVertexId) const525 QgsPoint QgsPoint::vertexAt( QgsVertexId ) const
526 {
527   return *this;
528 }
529 
toCurveType() const530 QgsPoint *QgsPoint::toCurveType() const
531 {
532   return clone();
533 }
534 
segmentLength(QgsVertexId) const535 double QgsPoint::segmentLength( QgsVertexId ) const
536 {
537   return 0.0;
538 }
539 
boundingBoxIntersects(const QgsRectangle & rectangle) const540 bool QgsPoint::boundingBoxIntersects( const QgsRectangle &rectangle ) const
541 {
542   return rectangle.contains( mX, mY );
543 }
544 
545 /***************************************************************************
546  * This class is considered CRITICAL and any change MUST be accompanied with
547  * full unit tests.
548  * See details in QEP #17
549  ****************************************************************************/
550 
addZValue(double zValue)551 bool QgsPoint::addZValue( double zValue )
552 {
553   if ( QgsWkbTypes::hasZ( mWkbType ) )
554     return false;
555 
556   mWkbType = QgsWkbTypes::addZ( mWkbType );
557   mZ = zValue;
558   clearCache();
559   return true;
560 }
561 
addMValue(double mValue)562 bool QgsPoint::addMValue( double mValue )
563 {
564   if ( QgsWkbTypes::hasM( mWkbType ) )
565     return false;
566 
567   mWkbType = QgsWkbTypes::addM( mWkbType );
568   mM = mValue;
569   clearCache();
570   return true;
571 }
572 
transform(const QTransform & t,double zTranslate,double zScale,double mTranslate,double mScale)573 void QgsPoint::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
574 {
575   clearCache();
576   qreal x, y;
577   t.map( mX, mY, &x, &y );
578   mX = x;
579   mY = y;
580 
581   if ( is3D() )
582   {
583     mZ = mZ * zScale + zTranslate;
584   }
585   if ( isMeasure() )
586   {
587     mM = mM * mScale + mTranslate;
588   }
589 }
590 
591 
dropZValue()592 bool QgsPoint::dropZValue()
593 {
594   if ( !is3D() )
595     return false;
596 
597   mWkbType = QgsWkbTypes::dropZ( mWkbType );
598   mZ = std::numeric_limits<double>::quiet_NaN();
599   clearCache();
600   return true;
601 }
602 
dropMValue()603 bool QgsPoint::dropMValue()
604 {
605   if ( !isMeasure() )
606     return false;
607 
608   mWkbType = QgsWkbTypes::dropM( mWkbType );
609   mM = std::numeric_limits<double>::quiet_NaN();
610   clearCache();
611   return true;
612 }
613 
swapXy()614 void QgsPoint::swapXy()
615 {
616   std::swap( mX, mY );
617   clearCache();
618 }
619 
convertTo(QgsWkbTypes::Type type)620 bool QgsPoint::convertTo( QgsWkbTypes::Type type )
621 {
622   if ( type == mWkbType )
623     return true;
624 
625   clearCache();
626 
627   switch ( type )
628   {
629     case QgsWkbTypes::Point:
630       mZ = std::numeric_limits<double>::quiet_NaN();
631       mM = std::numeric_limits<double>::quiet_NaN();
632       mWkbType = type;
633       return true;
634     case QgsWkbTypes::PointZ:
635     case QgsWkbTypes::Point25D:
636       mM = std::numeric_limits<double>::quiet_NaN();
637       mWkbType = type;
638       return true;
639     case QgsWkbTypes::PointM:
640       mZ = std::numeric_limits<double>::quiet_NaN();
641       mWkbType = type;
642       return true;
643     case QgsWkbTypes::PointZM:
644       mWkbType = type;
645       return true;
646     default:
647       break;
648   }
649 
650   return false;
651 }
652 
transform(QgsAbstractGeometryTransformer * transformer,QgsFeedback *)653 bool QgsPoint::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback * )
654 {
655   if ( !transformer )
656     return false;
657 
658   const bool res = transformer->transformPoint( mX, mY, mZ, mM );
659   clearCache();
660   return res;
661 }
662 
filterVertices(const std::function<bool (const QgsPoint &)> &)663 void QgsPoint::filterVertices( const std::function<bool ( const QgsPoint & )> & )
664 {
665   // no meaning for points
666 }
667 
transformVertices(const std::function<QgsPoint (const QgsPoint &)> & transform)668 void QgsPoint::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
669 {
670   const QgsPoint res = transform( *this );
671   mX = res.x();
672   mY = res.y();
673   if ( is3D() )
674     mZ = res.z();
675   if ( isMeasure() )
676     mM = res.m();
677   clearCache();
678 }
679 
distance3D(double x,double y,double z) const680 double QgsPoint::distance3D( double x, double y, double z ) const
681 {
682   double zDistSquared = 0.0;
683   if ( is3D() || !std::isnan( z ) )
684     zDistSquared = ( mZ - z ) * ( mZ - z );
685 
686   return std::sqrt( ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) + zDistSquared );
687 }
688 
distance3D(const QgsPoint & other) const689 double QgsPoint::distance3D( const QgsPoint &other ) const
690 {
691   double zDistSquared = 0.0;
692   if ( is3D() || other.is3D() )
693     zDistSquared = ( mZ - other.z() ) * ( mZ - other.z() );
694 
695   return std::sqrt( ( mX - other.x() ) * ( mX - other.x() ) + ( mY - other.y() ) * ( mY - other.y() ) + zDistSquared );
696 }
697 
distanceSquared3D(double x,double y,double z) const698 double QgsPoint::distanceSquared3D( double x, double y, double z ) const
699 {
700   double zDistSquared = 0.0;
701   if ( is3D() || !std::isnan( z ) )
702     zDistSquared = ( mZ - z ) * ( mZ - z );
703 
704   return ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) + zDistSquared;
705 }
706 
distanceSquared3D(const QgsPoint & other) const707 double QgsPoint::distanceSquared3D( const QgsPoint &other ) const
708 {
709   double zDistSquared = 0.0;
710   if ( is3D() || other.is3D() )
711     zDistSquared = ( mZ - other.z() ) * ( mZ - other.z() );
712 
713   return ( mX - other.x() ) * ( mX - other.x() ) + ( mY - other.y() ) * ( mY - other.y() ) + zDistSquared;
714 }
715 
azimuth(const QgsPoint & other) const716 double QgsPoint::azimuth( const QgsPoint &other ) const
717 {
718   const double dx = other.x() - mX;
719   const double dy = other.y() - mY;
720   return ( std::atan2( dx, dy ) * 180.0 / M_PI );
721 }
722 
inclination(const QgsPoint & other) const723 double QgsPoint::inclination( const QgsPoint &other ) const
724 {
725   const double distance = distance3D( other );
726   if ( qgsDoubleNear( distance, 0.0 ) )
727   {
728     return 90.0;
729   }
730   const double dz = other.z() - mZ;
731 
732   return ( std::acos( dz / distance ) * 180.0 / M_PI );
733 }
734 
project(double distance,double azimuth,double inclination) const735 QgsPoint QgsPoint::project( double distance, double azimuth, double inclination ) const
736 {
737   QgsWkbTypes::Type pType = mWkbType;
738   const double radsXy = azimuth * M_PI / 180.0;
739   double dx = 0.0, dy = 0.0, dz = 0.0;
740 
741   inclination = std::fmod( inclination, 360.0 );
742 
743   if ( !qgsDoubleNear( inclination, 90.0 ) )
744     pType = QgsWkbTypes::addZ( pType );
745 
746   if ( !is3D() && qgsDoubleNear( inclination, 90.0 ) )
747   {
748     dx = distance * std::sin( radsXy );
749     dy = distance * std::cos( radsXy );
750   }
751   else
752   {
753     const double radsZ = inclination * M_PI / 180.0;
754     dx = distance * std::sin( radsZ ) * std::sin( radsXy );
755     dy = distance * std::sin( radsZ ) * std::cos( radsXy );
756     dz = distance * std::cos( radsZ );
757   }
758 
759   return QgsPoint( mX + dx, mY + dy, mZ + dz, mM, pType );
760 }
761 
normalize()762 void QgsPoint::normalize()
763 {
764   // nothing to do
765 }
766 
isEmpty() const767 bool QgsPoint::isEmpty() const
768 {
769   return std::isnan( mX ) || std::isnan( mY );
770 }
771 
boundingBox() const772 QgsRectangle QgsPoint::boundingBox() const
773 {
774   return QgsRectangle( mX, mY, mX, mY );
775 }
776 
geometryType() const777 QString QgsPoint::geometryType() const
778 {
779   return QStringLiteral( "Point" );
780 }
781 
dimension() const782 int QgsPoint::dimension() const
783 {
784   return 0;
785 }
786 
childCount() const787 int QgsPoint::childCount() const
788 {
789   return 1;
790 }
791 
childPoint(int index) const792 QgsPoint QgsPoint::childPoint( int index ) const
793 {
794   Q_ASSERT( index == 0 );
795   return *this;
796 }
797 
createEmptyWithSameType() const798 QgsPoint *QgsPoint::createEmptyWithSameType() const
799 {
800   const double nan = std::numeric_limits<double>::quiet_NaN();
801   return new QgsPoint( nan, nan, nan, nan, mWkbType );
802 }
803 
compareToSameClass(const QgsAbstractGeometry * other) const804 int QgsPoint::compareToSameClass( const QgsAbstractGeometry *other ) const
805 {
806   const QgsPoint *otherPoint = qgsgeometry_cast< const QgsPoint * >( other );
807   if ( !otherPoint )
808     return -1;
809 
810   if ( mX < otherPoint->mX )
811   {
812     return -1;
813   }
814   else if ( mX > otherPoint->mX )
815   {
816     return 1;
817   }
818 
819   if ( mY < otherPoint->mY )
820   {
821     return -1;
822   }
823   else if ( mY > otherPoint->mY )
824   {
825     return 1;
826   }
827 
828   if ( is3D() && !otherPoint->is3D() )
829     return 1;
830   else if ( !is3D() && otherPoint->is3D() )
831     return -1;
832   else if ( is3D() && otherPoint->is3D() )
833   {
834     if ( mZ < otherPoint->mZ )
835     {
836       return -1;
837     }
838     else if ( mZ > otherPoint->mZ )
839     {
840       return 1;
841     }
842   }
843 
844   if ( isMeasure() && !otherPoint->isMeasure() )
845     return 1;
846   else if ( !isMeasure() && otherPoint->isMeasure() )
847     return -1;
848   else if ( isMeasure() && otherPoint->isMeasure() )
849   {
850     if ( mM < otherPoint->mM )
851     {
852       return -1;
853     }
854     else if ( mM > otherPoint->mM )
855     {
856       return 1;
857     }
858   }
859 
860   return 0;
861 }
862