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