Marble

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

KDE's Doxygen guidelines are available online.