Marble

GeoDataLatLonBox.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2007 Andrew Manson <g.real.ate@gmail.com>
4// SPDX-FileCopyrightText: 2008-2009 Torsten Rahn <rahn@kde.org>
5//
6
7
8#include "GeoDataLatLonBox.h"
9
10#include "MarbleDebug.h"
11#include "GeoDataLineString.h"
12
13#include "GeoDataTypes.h"
14
15#include <QDataStream>
16
17namespace Marble
18{
19
20const GeoDataLatLonBox GeoDataLatLonBox::empty = GeoDataLatLonBox();
21
22class GeoDataLatLonBoxPrivate
23{
24 public:
25 GeoDataLatLonBoxPrivate()
26 : m_north( 0.0 ),
27 m_south( 0.0 ),
28 m_east( 0.0 ),
29 m_west( 0.0 ),
30 m_rotation( 0.0 )
31 {
32 }
33
34 qreal m_north;
35 qreal m_south;
36 qreal m_east;
37 qreal m_west;
38 qreal m_rotation; // NOT implemented yet!
39};
40
41bool operator==( GeoDataLatLonBox const& lhs, GeoDataLatLonBox const& rhs )
42{
43 return lhs.d->m_west == rhs.d->m_west &&
44 lhs.d->m_east == rhs.d->m_east &&
45 lhs.d->m_north == rhs.d->m_north &&
46 lhs.d->m_south == rhs.d->m_south &&
47 lhs.d->m_rotation == rhs.d->m_rotation;
48}
49
50bool operator!=( GeoDataLatLonBox const& lhs, GeoDataLatLonBox const& rhs )
51{
52 return !( lhs == rhs );
53}
54
55GeoDataLatLonBox::GeoDataLatLonBox()
56 : GeoDataObject(),
57 d( new GeoDataLatLonBoxPrivate )
58{
59}
60
61GeoDataLatLonBox::GeoDataLatLonBox( qreal north, qreal south, qreal east, qreal west, GeoDataCoordinates::Unit unit )
62 : GeoDataObject(),
63 d( new GeoDataLatLonBoxPrivate )
64{
65 setBoundaries( north, south, east, west, unit );
66}
67
68GeoDataLatLonBox::GeoDataLatLonBox( const GeoDataLatLonBox & other )
69 : GeoDataObject( other ),
70 d( new GeoDataLatLonBoxPrivate( *other.d ) )
71{
72}
73
74GeoDataLatLonBox::~GeoDataLatLonBox()
75{
76 delete d;
77}
78
79const char* GeoDataLatLonBox::nodeType() const
80{
81 return GeoDataTypes::GeoDataLatLonBoxType;
82}
83
84qreal GeoDataLatLonBox::north( GeoDataCoordinates::Unit unit ) const
85{
86 if ( unit == GeoDataCoordinates::Degree ) {
87 return d->m_north * RAD2DEG;
88 }
89 return d->m_north;
90}
91
92void GeoDataLatLonBox::setNorth( const qreal north, GeoDataCoordinates::Unit unit )
93{
94 switch( unit ){
95 default:
96 case GeoDataCoordinates::Radian:
97 d->m_north = GeoDataCoordinates::normalizeLat( north );
98 break;
99 case GeoDataCoordinates::Degree:
100 d->m_north = GeoDataCoordinates::normalizeLat( north * DEG2RAD );
101 break;
102 }
103}
104
105qreal GeoDataLatLonBox::south( GeoDataCoordinates::Unit unit ) const
106{
107 if ( unit == GeoDataCoordinates::Degree ) {
108 return d->m_south * RAD2DEG;
109 }
110 return d->m_south;
111}
112
113void GeoDataLatLonBox::setSouth( const qreal south, GeoDataCoordinates::Unit unit )
114{
115 switch( unit ){
116 default:
117 case GeoDataCoordinates::Radian:
118 d->m_south = GeoDataCoordinates::normalizeLat( south );
119 break;
120 case GeoDataCoordinates::Degree:
121 d->m_south = GeoDataCoordinates::normalizeLat( south * DEG2RAD );
122 break;
123 }
124}
125
126qreal GeoDataLatLonBox::east( GeoDataCoordinates::Unit unit ) const
127{
128 if ( unit == GeoDataCoordinates::Degree ) {
129 return d->m_east * RAD2DEG;
130 }
131 return d->m_east;
132}
133
134void GeoDataLatLonBox::setEast( const qreal east, GeoDataCoordinates::Unit unit )
135{
136 switch( unit ){
137 default:
138 case GeoDataCoordinates::Radian:
139 d->m_east = GeoDataCoordinates::normalizeLon( east );
140 break;
141 case GeoDataCoordinates::Degree:
142 d->m_east = GeoDataCoordinates::normalizeLon( east * DEG2RAD );
143 break;
144 }
145}
146
147qreal GeoDataLatLonBox::west( GeoDataCoordinates::Unit unit ) const
148{
149 if ( unit == GeoDataCoordinates::Degree ) {
150 return d->m_west * RAD2DEG;
151 }
152 return d->m_west;
153}
154
155void GeoDataLatLonBox::setWest( const qreal west, GeoDataCoordinates::Unit unit )
156{
157 switch( unit ){
158 default:
159 case GeoDataCoordinates::Radian:
160 d->m_west = GeoDataCoordinates::normalizeLon( west );
161 break;
162 case GeoDataCoordinates::Degree:
163 d->m_west = GeoDataCoordinates::normalizeLon( west * DEG2RAD );
164 break;
165 }
166}
167
168void GeoDataLatLonBox::setRotation( const qreal rotation, GeoDataCoordinates::Unit unit )
169{
170 switch( unit ){
171 default:
172 case GeoDataCoordinates::Radian:
173 d->m_rotation = rotation;
174 break;
175 case GeoDataCoordinates::Degree:
176 d->m_rotation = rotation * DEG2RAD;
177 break;
178 }
179}
180
181qreal GeoDataLatLonBox::rotation( GeoDataCoordinates::Unit unit ) const
182{
183 if ( unit == GeoDataCoordinates::Degree ) {
184 return d->m_rotation * RAD2DEG;
185 }
186 return d->m_rotation;
187}
188
189void GeoDataLatLonBox::boundaries( qreal &north, qreal &south, qreal &east, qreal &west, GeoDataCoordinates::Unit unit ) const
190{
191 switch( unit ){
192 default:
193 case GeoDataCoordinates::Radian:
194 north = d->m_north;
195 south = d->m_south;
196 east = d->m_east;
197 west = d->m_west;
198 break;
199 case GeoDataCoordinates::Degree:
200 north = d->m_north * RAD2DEG;
201 south = d->m_south * RAD2DEG;
202 east = d->m_east * RAD2DEG;
203 west = d->m_west * RAD2DEG;
204 break;
205 }
206}
207
208void GeoDataLatLonBox::setBoundaries( qreal north, qreal south, qreal east, qreal west, GeoDataCoordinates::Unit unit )
209{
210 switch( unit ){
211 default:
212 case GeoDataCoordinates::Radian:
213 d->m_north = GeoDataCoordinates::normalizeLat( north );
214 d->m_south = GeoDataCoordinates::normalizeLat( south );
215 d->m_east = GeoDataCoordinates::normalizeLon( east );
216 d->m_west = GeoDataCoordinates::normalizeLon( west );
217 break;
218 case GeoDataCoordinates::Degree:
219 d->m_north = GeoDataCoordinates::normalizeLat( north * DEG2RAD );
220 d->m_south = GeoDataCoordinates::normalizeLat( south * DEG2RAD );
221 d->m_east = GeoDataCoordinates::normalizeLon( east * DEG2RAD );
222 d->m_west = GeoDataCoordinates::normalizeLon( west * DEG2RAD );
223 break;
224 }
225}
226
227void GeoDataLatLonBox::scale(qreal verticalFactor, qreal horizontalFactor) const
228{
229 GeoDataCoordinates const middle = center();
230 qreal const deltaY = 0.5 * height() * verticalFactor;
231 qreal const deltaX = 0.5 * width() * horizontalFactor;
232 d->m_north = qMin((middle.latitude() + deltaY), static_cast<qreal>(M_PI/2));
233 d->m_south = qMax((middle.latitude() - deltaY), static_cast<qreal>(-M_PI/2));
234 if (deltaX > 180 * DEG2RAD) {
235 d->m_east = M_PI;
236 d->m_west = -M_PI;
237 }
238 else {
239 d->m_east = GeoDataCoordinates::normalizeLon(middle.longitude() + deltaX);
240 d->m_west = GeoDataCoordinates::normalizeLon(middle.longitude() - deltaX);
241 }
242}
243
244GeoDataLatLonBox GeoDataLatLonBox::scaled(qreal verticalFactor, qreal horizontalFactor) const
245{
246 GeoDataLatLonBox result = *this;
247 result.scale(verticalFactor, horizontalFactor);
248 return result;
249}
250
251qreal GeoDataLatLonBox::width( GeoDataCoordinates::Unit unit ) const
252{
253 return GeoDataLatLonBox::width( d->m_east, d->m_west, unit );
254}
255
256qreal GeoDataLatLonBox::width( qreal east, qreal west, GeoDataCoordinates::Unit unit )
257{
258 qreal width = fabs( (qreal)( GeoDataLatLonBox::crossesDateLine(east, west)
259 ? 2 * M_PI - west + east
260 : east - west ) );
261
262 // This also covers the case where this bounding box covers the whole
263 // longitude range ( -180 <= lon <= + 180 ).
264 if ( width > 2 * M_PI ) {
265 width = 2 * M_PI;
266 }
267
268 if ( unit == GeoDataCoordinates::Degree ) {
269 return width * RAD2DEG;
270 }
271
272 return width;
273}
274
275qreal GeoDataLatLonBox::height( GeoDataCoordinates::Unit unit ) const
276{
277 return GeoDataLatLonBox::height(d->m_north, d->m_south, unit);
278}
279
280qreal GeoDataLatLonBox::height(qreal north, qreal south, GeoDataCoordinates::Unit unit)
281{
282 qreal height = fabs( (qreal)( south - north ) );
283
284 if ( unit == GeoDataCoordinates::Degree ) {
285 return height * RAD2DEG;
286 }
287
288 return height;
289}
290
291bool GeoDataLatLonBox::crossesDateLine() const
292{
293 return GeoDataLatLonBox::crossesDateLine(d->m_east, d->m_west);
294}
295
296bool GeoDataLatLonBox::crossesDateLine(qreal east, qreal west)
297{
298 return east < west || ( east == M_PI && west == -M_PI );
299}
300
301GeoDataCoordinates GeoDataLatLonBox::center() const
302{
303 if( isEmpty() )
304 return GeoDataCoordinates();
305
306 if( crossesDateLine() )
307 return GeoDataCoordinates( GeoDataCoordinates::normalizeLon( east() + 2 * M_PI - ( east() + 2 * M_PI - west() ) / 2 ) ,
308 north() - ( north() - south() ) / 2 );
309 else
310 return GeoDataCoordinates( east() - ( east() - west() ) / 2,
311 north() - ( north() - south() ) / 2 );
312}
313
314bool GeoDataLatLonBox::containsPole( Pole pole ) const
315{
316 switch ( pole ) {
317 case NorthPole:
318 return ( 2 * north() == +M_PI );
319 case SouthPole:
320 return ( 2 * south() == -M_PI );
321 default:
322 case AnyPole:
323 return ( 2 * north() == +M_PI
324 || 2 * south() == -M_PI );
325 }
326
327 mDebug() << "Invalid pole";
328 return false;
329}
330
331bool GeoDataLatLonBox::contains(qreal lon, qreal lat) const
332{
333 if ( lat < d->m_south || lat > d->m_north ) {
334 return false;
335 }
336
337 // We need to take care of the normal case ...
338 if ( ( ( lon < d->m_west || lon > d->m_east ) && ( d->m_west < d->m_east ) ) ||
339 // ... and the case where the bounding box crosses the date line:
340 ( ( lon < d->m_west && lon > d->m_east ) && ( d->m_west > d->m_east ) ) )
341 return false;
342
343 return true;
344}
345
346bool GeoDataLatLonBox::contains( const GeoDataCoordinates &point ) const
347{
348 qreal lon, lat;
349
350 point.geoCoordinates( lon, lat );
351
352 return contains(lon, lat);
353}
354
355bool GeoDataLatLonBox::contains( const GeoDataLatLonBox &other ) const
356{
357 // check the contain criterion for the latitude first as this is trivial:
358
359 if ( d->m_north >= other.north() && d->m_south <= other.south() ) {
360
361 if ( !crossesDateLine() ) {
362 if ( !other.crossesDateLine() ) {
363 // "Normal" case: both bounding boxes don't cross the date line
364 if ( d->m_west <= other.west() && d->m_east >= other.east() ) {
365 return true;
366 }
367 }
368 else {
369 // The other bounding box crosses the date line, "this" one does not:
370 // So the date line splits the other bounding box in two parts.
371 // Hence "this" bounding box could be fully contained by one of them.
372 // So for both cases we are able to ignore the "overhanging" portion
373 // and thereby basically reduce the problem to the "normal" case:
374
375 if ( ( other.west() <= d->m_west && d->m_east <= +M_PI )
376 || ( other.east() >= d->m_east && d->m_west >= -M_PI ) ) {
377 return true;
378 }
379 }
380 }
381 else {
382 if ( other.crossesDateLine() ) {
383 // Other "Simple" case: both bounding boxes cross the date line
384 if ( d->m_west <= other.west() && d->m_east >= other.east() ) {
385 return true;
386 }
387 }
388 else {
389 // "This" bounding box crosses the date line, the other one does not.
390 // So the date line splits "this" bounding box in two parts.
391 // Hence the other bounding box could be fully contained by one of them.
392 // So for both cases we are able to ignore the "overhanging" portion
393 // and thereby basically reduce the problem to the "normal" case:
394
395 if ( ( d->m_west <= other.west() && other.east() <= +M_PI )
396 || ( d->m_east >= other.east() && other.west() >= -M_PI ) ) {
397 return true;
398 }
399
400 // if this bounding box covers the whole longitude range ( -180 <= lon <= + 180 )
401 // then of course the "inner" bounding box is "inside"
402 if ( d->m_west == -M_PI && d->m_east == +M_PI ) {
403 return true;
404 }
405 }
406
407 }
408 }
409
410 return false;
411}
412
413bool GeoDataLatLonBox::intersects( const GeoDataLatLonBox &other ) const
414{
415 if ( isEmpty() || other.isEmpty() ) {
416 return false;
417 }
418
419 // check the intersection criterion for the latitude first:
420
421 // Case 1: northern boundary of other box intersects:
422 if ( (d->m_north >= other.d->m_north && d->m_south <= other.d->m_north)
423 // Case 2: northern boundary of this box intersects:
424 || (other.d->m_north >= d->m_north && other.d->m_south <= d->m_north)
425 // Case 3: southern boundary of other box intersects:
426 || (d->m_north >= other.d->m_south && d->m_south <= other.d->m_south)
427 // Case 4: southern boundary of this box intersects:
428 || (other.d->m_north >= d->m_south && other.d->m_south <= d->m_south)) {
429
430 if ( !crossesDateLine() ) {
431 if ( !other.crossesDateLine() ) {
432 // "Normal" case: both bounding boxes don't cross the date line
433 // Case 1: eastern boundary of other box intersects:
434 if ( (d->m_east >= other.d->m_east && d->m_west <= other.d->m_east)
435 // Case 2: eastern boundary of this box intersects:
436 || (other.d->m_east >= d->m_east && other.d->m_west <= d->m_east)
437 // Case 3: western boundary of other box intersects:
438 || (d->m_east >= other.d->m_west && d->m_west <= other.d->m_west)
439 // Case 4: western boundary of this box intersects:
440 || (other.d->m_east >= d->m_west && other.d->m_west <= d->m_west)) {
441 return true;
442 }
443 }
444 else {
445 // The other bounding box crosses the date line, "this" one does not:
446 // So the date line splits the other bounding box in two parts.
447
448 if ( d->m_west <= other.d->m_east || d->m_east >= other.d->m_west) {
449 return true;
450 }
451 }
452 }
453 else {
454 if ( other.crossesDateLine() ) {
455 // The trivial case: both bounding boxes cross the date line and intersect
456 return true;
457 }
458 else {
459 // "This" bounding box crosses the date line, the other one does not.
460 // So the date line splits "this" bounding box in two parts.
461 //
462 // This also covers the case where this bounding box covers the whole
463 // longitude range ( -180 <= lon <= + 180 ).
464 if ( other.d->m_west <= d->m_east || other.d->m_east >= d->m_west ) {
465 return true;
466 }
467 }
468 }
469 }
470
471 return false;
472}
473
474GeoDataLatLonBox GeoDataLatLonBox::united( const GeoDataLatLonBox& other ) const
475{
476 if ( isEmpty() ) {
477 return other;
478 }
479
480 if ( other.isEmpty() ) {
481 return *this;
482 }
483
484 GeoDataLatLonBox result;
485
486 // use the position of the centers of the boxes to determine the "smallest"
487 // box (i.e. should the total box go through IDL or not). this
488 // determination does not depend on one box or the other crossing IDL too
489 GeoDataCoordinates c1 = center();
490 GeoDataCoordinates c2 = other.center();
491
492 // do latitude first, quite simple
493 result.setNorth(qMax( d->m_north, other.north() ) );
494 result.setSouth( qMin( d->m_south, other.south() ) );
495
496 qreal w1 = d->m_west;
497 qreal w2 = other.west();
498 qreal e1 = d->m_east;
499 qreal e2 = other.east();
500
501 bool const idl1 = d->m_east < d->m_west;
502 bool const idl2 = other.d->m_east < other.d->m_west;
503
504 if ( idl1 ) {
505 w1 += 2* M_PI;
506 e1 += 2* M_PI;
507 }
508 if ( idl2 ) {
509 w2 += 2* M_PI;
510 e2 += 2* M_PI;
511 }
512
513 // in the usual case, we take the maximum of east bounds, and
514 // the minimum of west bounds. The exceptions are:
515 // - centers of boxes are more than 180 apart
516 // (so the smallest box should go around the IDL)
517 //
518 // - 1 but not 2 boxes are crossing IDL
519 if ( fabs( c2.longitude()-c1.longitude() ) > M_PI
520 || ( idl1 ^ idl2 ) ) {
521 // exceptions, we go the unusual way:
522 // min of east, max of west
523 result.setEast( qMin( e1, e2 ) );
524 result.setWest( qMax( w1, w2 ) );
525 }
526 else {
527 // normal case, max of east, min of west
528 result.setEast( qMax( e1, e2 ) );
529 result.setWest( qMin( w1, w2 ) );
530 }
531 return result;
532}
533
534GeoDataLatLonBox GeoDataLatLonBox::toCircumscribedRectangle() const
535{
536 QVector<GeoDataCoordinates> coordinates;
537 coordinates.reserve(4);
538
539 coordinates.append( GeoDataCoordinates( west(), north() ) );
540 coordinates.append( GeoDataCoordinates( west(), south() ) );
541 coordinates.append( GeoDataCoordinates( east() + ( crossesDateLine() ? 2 * M_PI : 0 ), north() ) );
542 coordinates.append( GeoDataCoordinates( east() + ( crossesDateLine() ? 2 * M_PI : 0 ), south() ) );
543
544 const qreal cosRotation = cos( rotation() );
545 const qreal sinRotation = sin( rotation() );
546
547 qreal centerLat = center().latitude();
548 qreal centerLon = center().longitude();
549 if ( GeoDataLatLonBox( 0, 0, center().longitude(), west() ).crossesDateLine() ) {
550 if ( !centerLon ) centerLon += M_PI;
551 else centerLon += 2 * M_PI;
552 }
553
555
556 bool northSet = false;
557 bool southSet = false;
558 bool eastSet = false;
559 bool westSet = false;
560
561 for ( const GeoDataCoordinates& coord: coordinates ) {
562
563 const qreal lon = coord.longitude();
564 const qreal lat = coord.latitude();
565
566 const qreal rotatedLon = ( lon - centerLon ) * cosRotation - ( lat - centerLat ) * sinRotation + centerLon;
567 const qreal rotatedLat = ( lon - centerLon ) * sinRotation + ( lat - centerLat ) * cosRotation + centerLat;
568
569 if ( !northSet || rotatedLat > box.north() ) {
570 northSet = true;
571 box.setNorth( rotatedLat );
572 }
573
574 if ( !southSet || rotatedLat < box.south() ) {
575 southSet = true;
576 box.setSouth( rotatedLat );
577 }
578
579 if ( !westSet || rotatedLon < box.west() ) {
580 westSet = true;
581 box.setWest( rotatedLon );
582 }
583
584 if ( !eastSet || rotatedLon > box.east() ) {
585 eastSet = true;
586 box.setEast( rotatedLon );
587 }
588 }
589
590 box.setBoundaries(box.north(), box.south(), box.east(), box.west());
591
592 return box;
593}
594
595GeoDataLatLonBox& GeoDataLatLonBox::operator=( const GeoDataLatLonBox &other )
596{
597 GeoDataObject::operator=( other );
598
599 *d = *other.d;
600 return *this;
601}
602
603GeoDataLatLonBox GeoDataLatLonBox::operator|( const GeoDataLatLonBox& other ) const
604{
605 return united( other );
606}
607
608GeoDataLatLonBox& GeoDataLatLonBox::operator|=( const GeoDataLatLonBox& other )
609{
610 *this = united( other );
611 return *this;
612}
613
614
615void GeoDataLatLonBox::pack( QDataStream& stream ) const
616{
617 GeoDataObject::pack( stream );
618
619 stream << d->m_north << d->m_south << d->m_east << d->m_west << d->m_rotation;
620}
621
622void GeoDataLatLonBox::unpack( QDataStream& stream )
623{
624 GeoDataObject::unpack( stream );
625
626 stream >> d->m_north >> d->m_south >> d->m_east >> d->m_west >> d->m_rotation;
627}
628
629GeoDataLatLonBox GeoDataLatLonBox::fromLineString( const GeoDataLineString& lineString )
630{
631 // If the line string is empty return an empty boundingbox
632 if ( lineString.isEmpty() ) {
633 return GeoDataLatLonBox();
634 }
635
636 qreal lon, lat;
637 lineString.first().geoCoordinates( lon, lat );
638 GeoDataCoordinates::normalizeLonLat( lon, lat );
639
640 qreal north = lat;
641 qreal south = lat;
642 qreal west = lon;
643 qreal east = lon;
644
645 // If there's only a single node stored then the boundingbox only contains that point
646 if ( lineString.size() == 1 )
647 return GeoDataLatLonBox( north, south, east, west );
648
649 // Specifies whether the polygon crosses the IDL
650 bool idlCrossed = false;
651
652 // "idlCrossState" specifies the state concerning IDL crossage.
653 // This is needed in order to create optimal bounding boxes in case of covering the IDL
654 // Every time the IDL gets crossed from east to west the idlCrossState value gets
655 // increased by one.
656 // Every time the IDL gets crossed from west to east the idlCrossState value gets
657 // decreased by one.
658
659 int idlCrossState = 0;
660 int idlMaxCrossState = 0;
661 int idlMinCrossState = 0;
662
663 // Holds values for east and west while idlCrossState != 0
664 qreal otherWest = lon;
665 qreal otherEast = lon;
666
667 qreal previousLon = lon;
668
669 int currentSign = ( lon < 0 ) ? -1 : +1;
670 int previousSign = currentSign;
671
674
675 bool processingLastNode = false;
676
677 while( it != itEnd ) {
678 // Get coordinates and normalize them to the desired range.
679 (it)->geoCoordinates( lon, lat );
680 GeoDataCoordinates::normalizeLonLat( lon, lat );
681
682 // Determining the maximum and minimum latitude
683 if ( lat > north ) {
684 north = lat;
685 } else if ( lat < south ) {
686 south = lat;
687 }
688
689 currentSign = ( lon < 0 ) ? -1 : +1;
690
691 // Once the polyline crosses the dateline the covered bounding box
692 // would cover the whole [-M_PI; M_PI] range.
693 // When looking separately at the longitude range that gets covered
694 // east and west from the IDL we get two bounding boxes (we prefix
695 // the resulting longitude range on the "other side" with "other").
696 // By picking the "inner" range values we get a more appropriate
697 // optimized single bounding box.
698
699 // IDL check
700 if ( previousSign != currentSign
701 && fabs( previousLon ) + fabs( lon ) > M_PI ) {
702
703 // Initialize values for otherWest and otherEast
704 if ( idlCrossed == false ) {
705 otherWest = lon;
706 otherEast = lon;
707 idlCrossed = true;
708 }
709
710 // Determine the new IDL Cross State
711 if ( previousLon < 0 ) {
712 idlCrossState++;
713 if ( idlCrossState > idlMaxCrossState ) {
714 idlMaxCrossState = idlCrossState;
715 }
716 }
717 else {
718 idlCrossState--;
719 if ( idlCrossState < idlMinCrossState ) {
720 idlMinCrossState = idlCrossState;
721 }
722 }
723 }
724
725 if ( idlCrossState == 0 ) {
726 if ( lon > east ) east = lon;
727 if ( lon < west ) west = lon;
728 }
729 else {
730 if ( lon > otherEast ) otherEast = lon;
731 if ( lon < otherWest ) otherWest = lon;
732 }
733
734 previousLon = lon;
735 previousSign = currentSign;
736
737 if ( processingLastNode ) {
738 break;
739 }
740 ++it;
741
742 if( lineString.isClosed() && it == itEnd ) {
743 it = lineString.constBegin();
744 processingLastNode = true;
745 }
746 }
747
748 if ( idlCrossed ) {
749 if ( idlMinCrossState < 0 ) {
750 east = otherEast;
751 }
752 if ( idlMaxCrossState > 0 ) {
753 west = otherWest;
754 }
755 if ( ( idlMinCrossState < 0 && idlMaxCrossState > 0 )
756 || idlMinCrossState < -1 || idlMaxCrossState > 1
757 || west <= east ) {
758 east = +M_PI;
759 west = -M_PI;
760 // if polygon fully in south hemisphere, contain south pole
761 if( north < 0 ) {
762 south = -M_PI/2;
763 } else {
764 north = M_PI/2;
765 }
766 }
767 }
768
769 return GeoDataLatLonBox( north, south, east, west );
770}
771
772bool GeoDataLatLonBox::isNull() const
773{
774 return d->m_north == d->m_south && d->m_east == d->m_west;
775}
776
777bool GeoDataLatLonBox::isEmpty() const
778{
779 return *this == empty;
780}
781
782bool GeoDataLatLonBox::fuzzyCompare(const GeoDataLatLonBox& lhs,
783 const GeoDataLatLonBox& rhs,
784 const qreal factor)
785{
786 bool equal = true;
787
788 // Check the latitude for approximate equality
789
790 double latDelta = lhs.height() * factor;
791
792 if (fabs(lhs.north() - rhs.north()) > latDelta) equal = false;
793 if (fabs(lhs.south() - rhs.south()) > latDelta) equal = false;
794
795
796 // Check the longitude for approximate equality
797
798 double lonDelta = lhs.width() * factor;
799
800 double lhsEast = lhs.east();
801 double rhsEast = rhs.east();
802
803 if (!GeoDataLatLonBox::crossesDateLine(lhsEast, rhsEast)) {
804 if (fabs(lhsEast - rhsEast) > lonDelta) equal = false;
805 }
806 else {
807 if (lhsEast < 0 && rhsEast > 0) {
808 lhsEast += 2 * M_PI;
809 if (fabs(lhsEast - rhsEast) > lonDelta) equal = false;
810 }
811 if (lhsEast > 0 && rhsEast < 0) {
812 rhsEast += 2 * M_PI;
813 if (fabs(lhsEast - rhsEast) > lonDelta) equal = false;
814 }
815 }
816
817 double lhsWest = lhs.west();
818 double rhsWest = rhs.west();
819
820 if (!GeoDataLatLonBox::crossesDateLine(lhsWest, rhsWest)) {
821 if (fabs(lhsWest - rhsWest) > lonDelta) equal = false;
822 }
823 else {
824 if (lhsWest < 0 && rhsWest > 0) {
825 lhsWest += 2 * M_PI;
826 if (fabs(lhsWest - rhsWest) > lonDelta) equal = false;
827 }
828 if (lhsWest > 0 && rhsWest < 0) {
829 rhsWest += 2 * M_PI;
830 if (fabs(lhsWest - rhsWest) > lonDelta) equal = false;
831 }
832 }
833
834 return equal;
835}
836
837
838void GeoDataLatLonBox::clear()
839{
840 *this = empty;
841}
842}
A 3d point representation.
qreal longitude(GeoDataCoordinates::Unit unit) const
retrieves the longitude of the GeoDataCoordinates object use the unit parameter to switch between Rad...
Unit
enum used constructor to specify the units used
qreal latitude(GeoDataCoordinates::Unit unit) const
retrieves the latitude of the GeoDataCoordinates object use the unit parameter to switch between Radi...
void geoCoordinates(qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit) const
use this function to get the longitude and latitude with one call - use the unit parameter to switch ...
A class that defines a 2D bounding box for geographic data.
void scale(qreal verticalFactor, qreal horizontalFactor) const
Changes the differences between the boundaries and the center by the given factor,...
qreal north(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the northern boundary of the bounding box.
qreal east(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the eastern boundary of the bounding box.
qreal height(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the height of the latitude interval.
virtual bool isEmpty() const
Indicates whether the bounding box is not initialised (and contains nothing).
qreal width(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the width of the longitude interval.
qreal west(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the western boundary of the bounding box.
qreal south(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the southern boundary of the bounding box.
virtual GeoDataCoordinates center() const
returns the center of this box
A LineString that allows to store a contiguous set of line segments.
virtual bool isClosed() const
Returns whether a LineString is a closed polygon.
bool isEmpty() const
Returns whether the LineString has no nodes at all.
QVector< GeoDataCoordinates >::ConstIterator constEnd() const
Returns a const iterator that points to the end of the LineString.
GeoDataCoordinates & first()
Returns a reference to the first node in the LineString. This method detaches the returned coordinate...
int size() const
Returns the number of nodes in a LineString.
QVector< GeoDataCoordinates >::ConstIterator constBegin() const
Returns a const iterator that points to the begin of the LineString.
Binds a QML item to a specific geodetic location in screen coordinates.
@ SouthPole
Only South Pole.
@ NorthPole
Only North Pole.
@ AnyPole
Any pole.
void append(QList< T > &&value)
void reserve(qsizetype size)
bool operator==(const QGraphicsApiFilter &reference, const QGraphicsApiFilter &sample)
bool operator!=(const QGraphicsApiFilter &reference, const QGraphicsApiFilter &sample)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.