Branch data Line data Source code
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 : :
41 : 54763 : QgsPoint::QgsPoint( double x, double y, double z, double m, QgsWkbTypes::Type wkbType )
42 : 54763 : : mX( x )
43 : 54763 : , mY( y )
44 : 54763 : , mZ( z )
45 : 54763 : , mM( m )
46 : 109526 : {
47 : 54763 : if ( wkbType != QgsWkbTypes::Unknown )
48 : : {
49 : : Q_ASSERT( QgsWkbTypes::flatType( wkbType ) == QgsWkbTypes::Point );
50 : 763 : mWkbType = wkbType;
51 : 763 : }
52 : 54000 : else if ( std::isnan( z ) )
53 : : {
54 : 53512 : if ( std::isnan( m ) )
55 : 53510 : mWkbType = QgsWkbTypes::Point;
56 : : else
57 : 2 : mWkbType = QgsWkbTypes::PointM;
58 : 53512 : }
59 : 488 : else if ( std::isnan( m ) )
60 : 266 : mWkbType = QgsWkbTypes::PointZ;
61 : : else
62 : 222 : mWkbType = QgsWkbTypes::PointZM;
63 : 54763 : }
64 : :
65 : 161 : QgsPoint::QgsPoint( const QgsPointXY &p )
66 : 161 : : mX( p.x() )
67 : 161 : , mY( p.y() )
68 : 161 : , mZ( std::numeric_limits<double>::quiet_NaN() )
69 : 161 : , mM( std::numeric_limits<double>::quiet_NaN() )
70 : 322 : {
71 : 161 : mWkbType = QgsWkbTypes::Point;
72 : 161 : if ( p.isEmpty() )
73 : : {
74 : 0 : mX = std::numeric_limits<double>::quiet_NaN();
75 : 0 : mY = std::numeric_limits<double>::quiet_NaN();
76 : 0 : }
77 : 161 : }
78 : :
79 : 1 : QgsPoint::QgsPoint( QPointF p )
80 : 1 : : mX( p.x() )
81 : 1 : , mY( p.y() )
82 : 1 : , mZ( std::numeric_limits<double>::quiet_NaN() )
83 : 1 : , mM( std::numeric_limits<double>::quiet_NaN() )
84 : 2 : {
85 : 1 : mWkbType = QgsWkbTypes::Point;
86 : 1 : }
87 : :
88 : 263600 : QgsPoint::QgsPoint( QgsWkbTypes::Type wkbType, double x, double y, double z, double m )
89 : 263600 : : mX( x )
90 : 263600 : , mY( y )
91 : 263600 : , mZ( QgsWkbTypes::hasZ( wkbType ) ? z : std::numeric_limits<double>::quiet_NaN() )
92 : 263600 : , mM( QgsWkbTypes::hasM( wkbType ) ? m : std::numeric_limits<double>::quiet_NaN() )
93 : 527200 : {
94 : : Q_ASSERT( QgsWkbTypes::flatType( wkbType ) == QgsWkbTypes::Point );
95 : 263600 : mWkbType = wkbType;
96 : 263600 : }
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 : :
104 : 140 : QgsPoint *QgsPoint::clone() const
105 : : {
106 : 140 : return new QgsPoint( *this );
107 : 0 : }
108 : :
109 : 10 : QgsPoint *QgsPoint::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
110 : : {
111 : : // helper function
112 : 40 : auto gridifyValue = []( double value, double spacing, bool extraCondition = true ) -> double
113 : : {
114 : 40 : if ( spacing > 0 && extraCondition )
115 : 19 : return std::round( value / spacing ) * spacing;
116 : : else
117 : 21 : return value;
118 : 40 : };
119 : :
120 : : // Get the new values
121 : 10 : auto x = gridifyValue( mX, hSpacing );
122 : 10 : auto y = gridifyValue( mY, vSpacing );
123 : 10 : auto z = gridifyValue( mZ, dSpacing, QgsWkbTypes::hasZ( mWkbType ) );
124 : 10 : auto m = gridifyValue( mM, mSpacing, QgsWkbTypes::hasM( mWkbType ) );
125 : :
126 : : // return the new object
127 : 10 : return new QgsPoint( mWkbType, x, y, z, m );
128 : 0 : }
129 : :
130 : 3 : bool QgsPoint::removeDuplicateNodes( double, bool )
131 : : {
132 : 3 : return false;
133 : : }
134 : :
135 : 13 : bool QgsPoint::fromWkb( QgsConstWkbPtr &wkbPtr )
136 : : {
137 : 13 : QgsWkbTypes::Type type = wkbPtr.readHeader();
138 : 13 : if ( QgsWkbTypes::flatType( type ) != QgsWkbTypes::Point )
139 : : {
140 : 2 : clear();
141 : 2 : return false;
142 : : }
143 : 11 : mWkbType = type;
144 : :
145 : 11 : wkbPtr >> mX;
146 : 11 : wkbPtr >> mY;
147 : 11 : if ( is3D() )
148 : 5 : wkbPtr >> mZ;
149 : 11 : if ( isMeasure() )
150 : 5 : wkbPtr >> mM;
151 : :
152 : 11 : clearCache();
153 : :
154 : 11 : return true;
155 : 13 : }
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 : :
163 : 509 : bool QgsPoint::fromWkt( const QString &wkt )
164 : : {
165 : 509 : clear();
166 : :
167 : 509 : QPair<QgsWkbTypes::Type, QString> parts = QgsGeometryUtils::wktReadBlock( wkt );
168 : :
169 : 509 : if ( QgsWkbTypes::flatType( parts.first ) != QgsWkbTypes::Point )
170 : 1 : return false;
171 : 508 : mWkbType = parts.first;
172 : :
173 : 508 : QString secondWithoutParentheses = parts.second;
174 : 508 : secondWithoutParentheses = secondWithoutParentheses.remove( '(' ).remove( ')' ).simplified().remove( ' ' );
175 : 508 : parts.second = parts.second.remove( '(' ).remove( ')' );
176 : 615 : if ( ( parts.second.compare( QLatin1String( "EMPTY" ), Qt::CaseInsensitive ) == 0 ) ||
177 : 107 : secondWithoutParentheses.isEmpty() )
178 : 402 : return true;
179 : :
180 : 212 : 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 : 106 : 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 : 212 : QRegularExpression rxIsNumber( QStringLiteral( "^[+-]?(\\d\\.?\\d*[Ee][+\\-]?\\d+|(\\d+\\.\\d*|\\d*\\.\\d+)|\\d+)$" ) );
199 : 106 : if ( coordinates.filter( rxIsNumber ).size() != coordinates.size() )
200 : 11 : return false;
201 : :
202 : 95 : if ( coordinates.size() < 2 )
203 : : {
204 : 1 : clear();
205 : 1 : return false;
206 : : }
207 : 94 : 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 : 0 : mWkbType = QgsWkbTypes::addZ( mWkbType );
212 : 0 : }
213 : 94 : 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 : 0 : mWkbType = QgsWkbTypes::addZ( mWkbType );
218 : 0 : mWkbType = QgsWkbTypes::addM( mWkbType );
219 : 0 : }
220 : :
221 : 94 : int idx = 0;
222 : 94 : mX = coordinates[idx++].toDouble();
223 : 94 : mY = coordinates[idx++].toDouble();
224 : 94 : if ( is3D() && coordinates.length() > 2 )
225 : 5 : mZ = coordinates[idx++].toDouble();
226 : 94 : if ( isMeasure() && coordinates.length() > 2 + is3D() )
227 : 6 : mM = coordinates[idx++].toDouble();
228 : :
229 : 94 : return true;
230 : 509 : }
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 : :
238 : 31 : int QgsPoint::wkbSize( WkbFlags ) const
239 : : {
240 : 31 : int binarySize = sizeof( char ) + sizeof( quint32 );
241 : 31 : binarySize += ( 2 + is3D() + isMeasure() ) * sizeof( double );
242 : 31 : return binarySize;
243 : : }
244 : :
245 : 22 : QByteArray QgsPoint::asWkb( WkbFlags flags ) const
246 : : {
247 : 22 : QByteArray wkbArray;
248 : 22 : wkbArray.resize( QgsPoint::wkbSize( flags ) );
249 : 22 : QgsWkbPtr wkb( wkbArray );
250 : 22 : wkb << static_cast<char>( QgsApplication::endian() );
251 : 22 : wkb << static_cast<quint32>( wkbType() );
252 : 22 : wkb << mX << mY;
253 : 22 : if ( is3D() )
254 : : {
255 : 5 : wkb << mZ;
256 : 5 : }
257 : 22 : if ( isMeasure() )
258 : : {
259 : 5 : wkb << mM;
260 : 5 : }
261 : 22 : return wkbArray;
262 : 22 : }
263 : :
264 : 511 : QString QgsPoint::asWkt( int precision ) const
265 : : {
266 : 511 : QString wkt = wktTypeStr();
267 : :
268 : 511 : if ( isEmpty() )
269 : 404 : wkt += QLatin1String( " EMPTY" );
270 : : else
271 : : {
272 : 107 : wkt += QLatin1String( " (" );
273 : 107 : wkt += qgsDoubleToString( mX, precision ) + ' ' + qgsDoubleToString( mY, precision );
274 : 107 : if ( is3D() )
275 : 37 : wkt += ' ' + qgsDoubleToString( mZ, precision );
276 : 107 : if ( isMeasure() )
277 : 25 : wkt += ' ' + qgsDoubleToString( mM, precision );
278 : 107 : wkt += ')';
279 : : }
280 : 511 : return wkt;
281 : 511 : }
282 : :
283 : 8 : QDomElement QgsPoint::asGml2( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
284 : : {
285 : 16 : QDomElement elemPoint = doc.createElementNS( ns, QStringLiteral( "Point" ) );
286 : 16 : QDomElement elemCoordinates = doc.createElementNS( ns, QStringLiteral( "coordinates" ) );
287 : :
288 : : // coordinate separator
289 : 16 : QString cs = QStringLiteral( "," );
290 : : // tuple separator
291 : 16 : QString ts = QStringLiteral( " " );
292 : :
293 : 16 : elemCoordinates.setAttribute( QStringLiteral( "cs" ), cs );
294 : 16 : elemCoordinates.setAttribute( QStringLiteral( "ts" ), ts );
295 : :
296 : 8 : QString strCoordinates;
297 : 8 : if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
298 : 6 : strCoordinates = qgsDoubleToString( mX, precision ) + cs + qgsDoubleToString( mY, precision );
299 : : else
300 : 2 : strCoordinates = qgsDoubleToString( mY, precision ) + cs + qgsDoubleToString( mX, precision );
301 : 8 : elemCoordinates.appendChild( doc.createTextNode( strCoordinates ) );
302 : 8 : elemPoint.appendChild( elemCoordinates );
303 : 8 : return elemPoint;
304 : 8 : }
305 : :
306 : 10 : QDomElement QgsPoint::asGml3( QDomDocument &doc, int precision, const QString &ns, const QgsAbstractGeometry::AxisOrder axisOrder ) const
307 : : {
308 : 20 : QDomElement elemPoint = doc.createElementNS( ns, QStringLiteral( "Point" ) );
309 : 20 : QDomElement elemPosList = doc.createElementNS( ns, QStringLiteral( "pos" ) );
310 : 20 : elemPosList.setAttribute( QStringLiteral( "srsDimension" ), is3D() ? 3 : 2 );
311 : 10 : QString strCoordinates;
312 : 10 : if ( axisOrder == QgsAbstractGeometry::AxisOrder::XY )
313 : 7 : strCoordinates = qgsDoubleToString( mX, precision ) + ' ' + qgsDoubleToString( mY, precision );
314 : : else
315 : 3 : strCoordinates = qgsDoubleToString( mY, precision ) + ' ' + qgsDoubleToString( mX, precision );
316 : 10 : if ( is3D() )
317 : 2 : strCoordinates += ' ' + qgsDoubleToString( mZ, precision );
318 : :
319 : 10 : elemPosList.appendChild( doc.createTextNode( strCoordinates ) );
320 : 10 : elemPoint.appendChild( elemPosList );
321 : 10 : return elemPoint;
322 : 10 : }
323 : :
324 : :
325 : 4 : json QgsPoint::asJsonObject( int precision ) const
326 : : {
327 : 20 : json j
328 : 12 : {
329 : 4 : { "type", "Point" },
330 : 4 : { "coordinates", json::array() },
331 : : };
332 : 4 : if ( ! isEmpty() )
333 : : {
334 : 3 : j["coordinates"].push_back( qgsRound( mX, precision ) );
335 : 3 : j["coordinates"].push_back( qgsRound( mY, precision ) );
336 : 3 : if ( is3D() )
337 : : {
338 : 0 : j["coordinates"].push_back( qgsRound( mZ, precision ) );
339 : 0 : }
340 : 3 : }
341 : 4 : return j;
342 : 4 : }
343 : :
344 : 6 : QString QgsPoint::asKml( int precision ) const
345 : : {
346 : 12 : return QStringLiteral( "<Point><coordinates>%1,%2</coordinates></Point>" ).arg( qgsDoubleToString( mX, precision ), qgsDoubleToString( mY, precision ) );
347 : 0 : }
348 : :
349 : 0 : void QgsPoint::draw( QPainter &p ) const
350 : : {
351 : 0 : p.drawRect( QRectF( mX - 2, mY - 2, 4, 4 ) );
352 : 0 : }
353 : :
354 : 0 : QPainterPath QgsPoint::asQPainterPath() const
355 : : {
356 : 0 : return QPainterPath();
357 : : }
358 : :
359 : 22181 : void QgsPoint::clear()
360 : : {
361 : 22181 : mX = mY = std::numeric_limits<double>::quiet_NaN();
362 : 22181 : if ( is3D() )
363 : 882 : mZ = 0.;
364 : : else
365 : 21299 : mZ = std::numeric_limits<double>::quiet_NaN();
366 : :
367 : 22181 : if ( isMeasure() )
368 : 876 : mM = 0.;
369 : : else
370 : 21305 : mM = std::numeric_limits<double>::quiet_NaN();
371 : :
372 : 22181 : clearCache();
373 : 22181 : }
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 : :
382 : 2 : void QgsPoint::transform( const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d, bool transformZ )
383 : : {
384 : 2 : clearCache();
385 : 2 : if ( transformZ )
386 : : {
387 : 0 : ct.transformInPlace( mX, mY, mZ, d );
388 : 0 : }
389 : : else
390 : : {
391 : 2 : double z = 0.0;
392 : 2 : ct.transformInPlace( mX, mY, z, d );
393 : : }
394 : 2 : }
395 : :
396 : 3 : QgsCoordinateSequence QgsPoint::coordinateSequence() const
397 : : {
398 : 3 : QgsCoordinateSequence cs;
399 : :
400 : 3 : cs.append( QgsRingSequence() );
401 : 3 : cs.back().append( QgsPointSequence() << QgsPoint( *this ) );
402 : :
403 : 3 : return cs;
404 : 3 : }
405 : :
406 : 0 : int QgsPoint::nCoordinates() const
407 : : {
408 : 0 : return 1;
409 : : }
410 : :
411 : 3 : int QgsPoint::vertexNumberFromVertexId( QgsVertexId id ) const
412 : : {
413 : 3 : if ( id.vertex != 0 )
414 : 2 : return -1;
415 : : else
416 : 1 : return 0;
417 : 3 : }
418 : :
419 : 1 : QgsAbstractGeometry *QgsPoint::boundary() const
420 : : {
421 : 1 : return nullptr;
422 : : }
423 : :
424 : 1 : bool QgsPoint::isValid( QString &, int ) const
425 : : {
426 : 1 : return true;
427 : : }
428 : :
429 : 1 : bool QgsPoint::insertVertex( QgsVertexId position, const QgsPoint &vertex )
430 : : {
431 : : Q_UNUSED( position )
432 : 1 : Q_UNUSED( vertex )
433 : 1 : 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 : :
442 : 15 : bool QgsPoint::moveVertex( QgsVertexId position, const QgsPoint &newPos )
443 : : {
444 : : Q_UNUSED( position )
445 : 15 : clearCache();
446 : 15 : mX = newPos.mX;
447 : 15 : mY = newPos.mY;
448 : 15 : if ( is3D() && newPos.is3D() )
449 : : {
450 : 3 : mZ = newPos.mZ;
451 : 3 : }
452 : 15 : if ( isMeasure() && newPos.isMeasure() )
453 : : {
454 : 3 : mM = newPos.mM;
455 : 3 : }
456 : 15 : return true;
457 : : }
458 : :
459 : 1 : bool QgsPoint::deleteVertex( QgsVertexId position )
460 : : {
461 : : Q_UNUSED( position )
462 : 1 : return false;
463 : : }
464 : :
465 : 3 : double QgsPoint::closestSegment( const QgsPoint &pt, QgsPoint &segmentPt, QgsVertexId &vertexAfter, int *leftOf, double epsilon ) const
466 : : {
467 : 3 : Q_UNUSED( pt )
468 : 3 : Q_UNUSED( segmentPt )
469 : 3 : Q_UNUSED( vertexAfter )
470 : 3 : if ( leftOf )
471 : 0 : *leftOf = 0;
472 : : Q_UNUSED( epsilon )
473 : 3 : return -1; // no segments - return error
474 : : }
475 : :
476 : 9 : bool QgsPoint::nextVertex( QgsVertexId &id, QgsPoint &vertex ) const
477 : : {
478 : 9 : if ( id.vertex < 0 )
479 : : {
480 : 6 : id.vertex = 0;
481 : 6 : if ( id.part < 0 )
482 : : {
483 : 3 : id.part = 0;
484 : 3 : }
485 : 6 : if ( id.ring < 0 )
486 : : {
487 : 3 : id.ring = 0;
488 : 3 : }
489 : 6 : vertex = *this;
490 : 6 : return true;
491 : : }
492 : : else
493 : : {
494 : 3 : return false;
495 : : }
496 : 9 : }
497 : :
498 : 3 : void QgsPoint::adjacentVertices( QgsVertexId, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
499 : : {
500 : 3 : previousVertex = QgsVertexId();
501 : 3 : nextVertex = QgsVertexId();
502 : 3 : }
503 : :
504 : 1 : double QgsPoint::vertexAngle( QgsVertexId vertex ) const
505 : : {
506 : : Q_UNUSED( vertex )
507 : 1 : return 0.0;
508 : : }
509 : :
510 : 57 : int QgsPoint::vertexCount( int, int ) const
511 : : {
512 : 57 : return 1;
513 : : }
514 : :
515 : 40 : int QgsPoint::ringCount( int ) const
516 : : {
517 : 40 : return 1;
518 : : }
519 : :
520 : 58 : int QgsPoint::partCount() const
521 : : {
522 : 58 : return 1;
523 : : }
524 : :
525 : 110 : QgsPoint QgsPoint::vertexAt( QgsVertexId ) const
526 : : {
527 : 110 : return *this;
528 : : }
529 : :
530 : 1 : QgsPoint *QgsPoint::toCurveType() const
531 : : {
532 : 1 : return clone();
533 : : }
534 : :
535 : 6 : double QgsPoint::segmentLength( QgsVertexId ) const
536 : : {
537 : 6 : return 0.0;
538 : : }
539 : :
540 : 3 : bool QgsPoint::boundingBoxIntersects( const QgsRectangle &rectangle ) const
541 : : {
542 : 3 : 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 : :
551 : 11 : bool QgsPoint::addZValue( double zValue )
552 : : {
553 : 11 : if ( QgsWkbTypes::hasZ( mWkbType ) )
554 : 1 : return false;
555 : :
556 : 10 : mWkbType = QgsWkbTypes::addZ( mWkbType );
557 : 10 : mZ = zValue;
558 : 10 : clearCache();
559 : 10 : return true;
560 : 11 : }
561 : :
562 : 9 : bool QgsPoint::addMValue( double mValue )
563 : : {
564 : 9 : if ( QgsWkbTypes::hasM( mWkbType ) )
565 : 1 : return false;
566 : :
567 : 8 : mWkbType = QgsWkbTypes::addM( mWkbType );
568 : 8 : mM = mValue;
569 : 8 : clearCache();
570 : 8 : return true;
571 : 9 : }
572 : :
573 : 8 : void QgsPoint::transform( const QTransform &t, double zTranslate, double zScale, double mTranslate, double mScale )
574 : : {
575 : 8 : clearCache();
576 : : qreal x, y;
577 : 8 : t.map( mX, mY, &x, &y );
578 : 8 : mX = x;
579 : 8 : mY = y;
580 : :
581 : 8 : if ( is3D() )
582 : : {
583 : 2 : mZ = mZ * zScale + zTranslate;
584 : 2 : }
585 : 8 : if ( isMeasure() )
586 : : {
587 : 2 : mM = mM * mScale + mTranslate;
588 : 2 : }
589 : 8 : }
590 : :
591 : :
592 : 17 : bool QgsPoint::dropZValue()
593 : : {
594 : 17 : if ( !is3D() )
595 : 11 : return false;
596 : :
597 : 6 : mWkbType = QgsWkbTypes::dropZ( mWkbType );
598 : 6 : mZ = std::numeric_limits<double>::quiet_NaN();
599 : 6 : clearCache();
600 : 6 : return true;
601 : 17 : }
602 : :
603 : 6 : bool QgsPoint::dropMValue()
604 : : {
605 : 6 : if ( !isMeasure() )
606 : 2 : return false;
607 : :
608 : 4 : mWkbType = QgsWkbTypes::dropM( mWkbType );
609 : 4 : mM = std::numeric_limits<double>::quiet_NaN();
610 : 4 : clearCache();
611 : 4 : return true;
612 : 6 : }
613 : :
614 : 1 : void QgsPoint::swapXy()
615 : : {
616 : 1 : std::swap( mX, mY );
617 : 1 : clearCache();
618 : 1 : }
619 : :
620 : 13 : bool QgsPoint::convertTo( QgsWkbTypes::Type type )
621 : : {
622 : 13 : if ( type == mWkbType )
623 : 1 : return true;
624 : :
625 : 12 : clearCache();
626 : :
627 : 12 : switch ( type )
628 : : {
629 : : case QgsWkbTypes::Point:
630 : 4 : mZ = std::numeric_limits<double>::quiet_NaN();
631 : 4 : mM = std::numeric_limits<double>::quiet_NaN();
632 : 4 : mWkbType = type;
633 : 4 : return true;
634 : : case QgsWkbTypes::PointZ:
635 : : case QgsWkbTypes::Point25D:
636 : 5 : mM = std::numeric_limits<double>::quiet_NaN();
637 : 5 : mWkbType = type;
638 : 5 : return true;
639 : : case QgsWkbTypes::PointM:
640 : 1 : mZ = std::numeric_limits<double>::quiet_NaN();
641 : 1 : mWkbType = type;
642 : 1 : return true;
643 : : case QgsWkbTypes::PointZM:
644 : 1 : mWkbType = type;
645 : 1 : return true;
646 : : default:
647 : 1 : break;
648 : : }
649 : :
650 : 1 : return false;
651 : 13 : }
652 : :
653 : 2 : bool QgsPoint::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback * )
654 : : {
655 : 2 : if ( !transformer )
656 : 0 : return false;
657 : :
658 : 2 : const bool res = transformer->transformPoint( mX, mY, mZ, mM );
659 : 2 : clearCache();
660 : 2 : return res;
661 : 2 : }
662 : :
663 : 1 : void QgsPoint::filterVertices( const std::function<bool ( const QgsPoint & )> & )
664 : : {
665 : : // no meaning for points
666 : 1 : }
667 : :
668 : 3 : void QgsPoint::transformVertices( const std::function<QgsPoint( const QgsPoint & )> &transform )
669 : : {
670 : 3 : QgsPoint res = transform( *this );
671 : 3 : mX = res.x();
672 : 3 : mY = res.y();
673 : 3 : if ( is3D() )
674 : 2 : mZ = res.z();
675 : 3 : if ( isMeasure() )
676 : 2 : mM = res.m();
677 : 3 : clearCache();
678 : 3 : }
679 : :
680 : 3 : double QgsPoint::distance3D( double x, double y, double z ) const
681 : : {
682 : 3 : double zDistSquared = 0.0;
683 : 3 : if ( is3D() || !std::isnan( z ) )
684 : 3 : zDistSquared = ( mZ - z ) * ( mZ - z );
685 : :
686 : 3 : return std::sqrt( ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) + zDistSquared );
687 : : }
688 : :
689 : 28 : double QgsPoint::distance3D( const QgsPoint &other ) const
690 : : {
691 : 28 : double zDistSquared = 0.0;
692 : 28 : if ( is3D() || other.is3D() )
693 : 26 : zDistSquared = ( mZ - other.z() ) * ( mZ - other.z() );
694 : :
695 : 28 : return std::sqrt( ( mX - other.x() ) * ( mX - other.x() ) + ( mY - other.y() ) * ( mY - other.y() ) + zDistSquared );
696 : : }
697 : :
698 : 5 : double QgsPoint::distanceSquared3D( double x, double y, double z ) const
699 : : {
700 : 5 : double zDistSquared = 0.0;
701 : 5 : if ( is3D() || !std::isnan( z ) )
702 : 5 : zDistSquared = ( mZ - z ) * ( mZ - z );
703 : :
704 : 5 : return ( mX - x ) * ( mX - x ) + ( mY - y ) * ( mY - y ) + zDistSquared;
705 : : }
706 : :
707 : 5 : double QgsPoint::distanceSquared3D( const QgsPoint &other ) const
708 : : {
709 : 5 : double zDistSquared = 0.0;
710 : 5 : if ( is3D() || other.is3D() )
711 : 4 : zDistSquared = ( mZ - other.z() ) * ( mZ - other.z() );
712 : :
713 : 5 : return ( mX - other.x() ) * ( mX - other.x() ) + ( mY - other.y() ) * ( mY - other.y() ) + zDistSquared;
714 : : }
715 : :
716 : 91 : double QgsPoint::azimuth( const QgsPoint &other ) const
717 : : {
718 : 91 : double dx = other.x() - mX;
719 : 91 : double dy = other.y() - mY;
720 : 91 : return ( std::atan2( dx, dy ) * 180.0 / M_PI );
721 : : }
722 : :
723 : 17 : double QgsPoint::inclination( const QgsPoint &other ) const
724 : : {
725 : 17 : double distance = distance3D( other );
726 : 17 : if ( qgsDoubleNear( distance, 0.0 ) )
727 : : {
728 : 2 : return 90.0;
729 : : }
730 : 15 : double dz = other.z() - mZ;
731 : :
732 : 15 : return ( std::acos( dz / distance ) * 180.0 / M_PI );
733 : 17 : }
734 : :
735 : 588 : QgsPoint QgsPoint::project( double distance, double azimuth, double inclination ) const
736 : : {
737 : 588 : QgsWkbTypes::Type pType = mWkbType;
738 : 588 : double radsXy = azimuth * M_PI / 180.0;
739 : 588 : double dx = 0.0, dy = 0.0, dz = 0.0;
740 : :
741 : 588 : inclination = std::fmod( inclination, 360.0 );
742 : :
743 : 588 : if ( !qgsDoubleNear( inclination, 90.0 ) )
744 : 26 : pType = QgsWkbTypes::addZ( pType );
745 : :
746 : 588 : if ( !is3D() && qgsDoubleNear( inclination, 90.0 ) )
747 : : {
748 : 521 : dx = distance * std::sin( radsXy );
749 : 521 : dy = distance * std::cos( radsXy );
750 : 521 : }
751 : : else
752 : : {
753 : 67 : double radsZ = inclination * M_PI / 180.0;
754 : 67 : dx = distance * std::sin( radsZ ) * std::sin( radsXy );
755 : 67 : dy = distance * std::sin( radsZ ) * std::cos( radsXy );
756 : 67 : dz = distance * std::cos( radsZ );
757 : : }
758 : :
759 : 588 : return QgsPoint( mX + dx, mY + dy, mZ + dz, mM, pType );
760 : : }
761 : :
762 : 923 : bool QgsPoint::isEmpty() const
763 : : {
764 : 923 : return std::isnan( mX ) || std::isnan( mY );
765 : : }
766 : :
767 : 224 : QgsRectangle QgsPoint::boundingBox() const
768 : : {
769 : 224 : return QgsRectangle( mX, mY, mX, mY );
770 : : }
771 : :
772 : 516 : QString QgsPoint::geometryType() const
773 : : {
774 : 1032 : return QStringLiteral( "Point" );
775 : : }
776 : :
777 : 22 : int QgsPoint::dimension() const
778 : : {
779 : 22 : return 0;
780 : : }
781 : :
782 : 10 : int QgsPoint::childCount() const
783 : : {
784 : 10 : return 1;
785 : : }
786 : :
787 : 6 : QgsPoint QgsPoint::childPoint( int index ) const
788 : : {
789 : : Q_ASSERT( index == 0 );
790 : 6 : return *this;
791 : : }
792 : :
793 : 1 : QgsPoint *QgsPoint::createEmptyWithSameType() const
794 : : {
795 : 1 : double nan = std::numeric_limits<double>::quiet_NaN();
796 : 1 : return new QgsPoint( nan, nan, nan, nan, mWkbType );
797 : 0 : }
|