7#include "GeoDataLatLonBox.h"
9#include "GeoDataLineString.h"
10#include "MarbleDebug.h"
12#include "GeoDataTypes.h"
19const GeoDataLatLonBox GeoDataLatLonBox::empty = GeoDataLatLonBox();
21class GeoDataLatLonBoxPrivate
24 GeoDataLatLonBoxPrivate()
40bool operator==(GeoDataLatLonBox
const &lhs, GeoDataLatLonBox
const &rhs)
42 return lhs.d->m_west == rhs.d->m_west && lhs.d->m_east == rhs.d->m_east && lhs.d->m_north == rhs.d->m_north && lhs.d->m_south == rhs.d->m_south
43 && lhs.d->m_rotation == rhs.d->m_rotation;
46bool operator!=(GeoDataLatLonBox
const &lhs, GeoDataLatLonBox
const &rhs)
51GeoDataLatLonBox::GeoDataLatLonBox()
53 , d(new GeoDataLatLonBoxPrivate)
57GeoDataLatLonBox::GeoDataLatLonBox(qreal north, qreal south, qreal east, qreal west, GeoDataCoordinates::Unit unit)
59 , d(new GeoDataLatLonBoxPrivate)
61 setBoundaries(north, south, east, west, unit);
64GeoDataLatLonBox::GeoDataLatLonBox(
const GeoDataLatLonBox &other)
65 : GeoDataObject(other)
66 , d(new GeoDataLatLonBoxPrivate(*other.d))
70GeoDataLatLonBox::~GeoDataLatLonBox()
75const char *GeoDataLatLonBox::nodeType()
const
77 return GeoDataTypes::GeoDataLatLonBoxType;
82 if (unit == GeoDataCoordinates::Degree) {
83 return d->m_north * RAD2DEG;
92 case GeoDataCoordinates::Radian:
93 d->m_north = GeoDataCoordinates::normalizeLat(north);
95 case GeoDataCoordinates::Degree:
96 d->m_north = GeoDataCoordinates::normalizeLat(north * DEG2RAD);
103 if (unit == GeoDataCoordinates::Degree) {
104 return d->m_south * RAD2DEG;
113 case GeoDataCoordinates::Radian:
114 d->m_south = GeoDataCoordinates::normalizeLat(south);
116 case GeoDataCoordinates::Degree:
117 d->m_south = GeoDataCoordinates::normalizeLat(south * DEG2RAD);
124 if (unit == GeoDataCoordinates::Degree) {
125 return d->m_east * RAD2DEG;
134 case GeoDataCoordinates::Radian:
135 d->m_east = GeoDataCoordinates::normalizeLon(east);
137 case GeoDataCoordinates::Degree:
138 d->m_east = GeoDataCoordinates::normalizeLon(east * DEG2RAD);
145 if (unit == GeoDataCoordinates::Degree) {
146 return d->m_west * RAD2DEG;
155 case GeoDataCoordinates::Radian:
156 d->m_west = GeoDataCoordinates::normalizeLon(west);
158 case GeoDataCoordinates::Degree:
159 d->m_west = GeoDataCoordinates::normalizeLon(west * DEG2RAD);
164void GeoDataLatLonBox::setRotation(
const qreal rotation, GeoDataCoordinates::Unit unit)
168 case GeoDataCoordinates::Radian:
169 d->m_rotation = rotation;
171 case GeoDataCoordinates::Degree:
172 d->m_rotation = rotation * DEG2RAD;
179 if (unit == GeoDataCoordinates::Degree) {
180 return d->m_rotation * RAD2DEG;
182 return d->m_rotation;
185void GeoDataLatLonBox::boundaries(qreal &north, qreal &south, qreal &east, qreal &west,
GeoDataCoordinates::Unit unit)
const
189 case GeoDataCoordinates::Radian:
195 case GeoDataCoordinates::Degree:
196 north = d->m_north * RAD2DEG;
197 south = d->m_south * RAD2DEG;
198 east = d->m_east * RAD2DEG;
199 west = d->m_west * RAD2DEG;
204void GeoDataLatLonBox::setBoundaries(qreal north, qreal south, qreal east, qreal west, GeoDataCoordinates::Unit unit)
208 case GeoDataCoordinates::Radian:
209 d->m_north = GeoDataCoordinates::normalizeLat(north);
210 d->m_south = GeoDataCoordinates::normalizeLat(south);
211 d->m_east = GeoDataCoordinates::normalizeLon(east);
212 d->m_west = GeoDataCoordinates::normalizeLon(west);
214 case GeoDataCoordinates::Degree:
215 d->m_north = GeoDataCoordinates::normalizeLat(north * DEG2RAD);
216 d->m_south = GeoDataCoordinates::normalizeLat(south * DEG2RAD);
217 d->m_east = GeoDataCoordinates::normalizeLon(east * DEG2RAD);
218 d->m_west = GeoDataCoordinates::normalizeLon(west * DEG2RAD);
223void GeoDataLatLonBox::scale(qreal verticalFactor, qreal horizontalFactor)
const
226 qreal
const deltaY = 0.5 * height() * verticalFactor;
227 qreal
const deltaX = 0.5 * width() * horizontalFactor;
228 d->m_north = qMin((middle.latitude() + deltaY),
static_cast<qreal
>(M_PI / 2));
229 d->m_south = qMax((middle.latitude() - deltaY),
static_cast<qreal
>(-M_PI / 2));
230 if (deltaX > 180 * DEG2RAD) {
234 d->m_east = GeoDataCoordinates::normalizeLon(middle.longitude() + deltaX);
235 d->m_west = GeoDataCoordinates::normalizeLon(middle.longitude() - deltaX);
239GeoDataLatLonBox GeoDataLatLonBox::scaled(qreal verticalFactor, qreal horizontalFactor)
const
242 result.
scale(verticalFactor, horizontalFactor);
248 return GeoDataLatLonBox::width(d->m_east, d->m_west, unit);
253 qreal width = fabs((qreal)(GeoDataLatLonBox::crossesDateLine(east, west) ? 2 * M_PI - west + east : east - west));
257 if (width > 2 * M_PI) {
261 if (unit == GeoDataCoordinates::Degree) {
262 return width * RAD2DEG;
270 return GeoDataLatLonBox::height(d->m_north, d->m_south, unit);
275 qreal height = fabs((qreal)(south - north));
277 if (unit == GeoDataCoordinates::Degree) {
278 return height * RAD2DEG;
284bool GeoDataLatLonBox::crossesDateLine()
const
286 return GeoDataLatLonBox::crossesDateLine(d->m_east, d->m_west);
289bool GeoDataLatLonBox::crossesDateLine(qreal east, qreal west)
291 return east < west || (east == M_PI && west == -M_PI);
299 if (crossesDateLine())
300 return {GeoDataCoordinates::normalizeLon(east() + 2 * M_PI - (east() + 2 * M_PI - west()) / 2), north() - (north() - south()) / 2};
302 return {east() - (east() - west()) / 2, north() - (north() - south()) / 2};
305bool GeoDataLatLonBox::containsPole(
Pole pole)
const
309 return (2 * north() == +M_PI);
311 return (2 * south() == -M_PI);
314 return (2 * north() == +M_PI || 2 * south() == -M_PI);
317 mDebug() <<
"Invalid pole";
321bool GeoDataLatLonBox::contains(qreal lon, qreal lat)
const
323 if (lat < d->m_south || lat > d->m_north) {
328 if (((lon < d->m_west || lon > d->m_east) && (d->m_west < d->m_east)) ||
330 ((lon < d->m_west && lon > d->m_east) && (d->m_west > d->m_east)))
342 return contains(lon, lat);
345bool GeoDataLatLonBox::contains(
const GeoDataLatLonBox &other)
const
349 if (d->m_north >= other.north() && d->m_south <= other.south()) {
350 if (!crossesDateLine()) {
351 if (!other.crossesDateLine()) {
353 if (d->m_west <= other.west() && d->m_east >= other.east()) {
363 if ((other.west() <= d->m_west && d->m_east <= +M_PI) || (other.east() >= d->m_east && d->m_west >= -M_PI)) {
368 if (other.crossesDateLine()) {
370 if (d->m_west <= other.west() && d->m_east >= other.east()) {
380 if ((d->m_west <= other.west() && other.east() <= +M_PI) || (d->m_east >= other.east() && other.west() >= -M_PI)) {
386 if (d->m_west == -M_PI && d->m_east == +M_PI) {
396bool GeoDataLatLonBox::intersects(
const GeoDataLatLonBox &other)
const
398 if (isEmpty() || other.isEmpty()) {
405 if ((d->m_north >= other.d->m_north && d->m_south <= other.d->m_north)
407 || (other.d->m_north >= d->m_north && other.d->m_south <= d->m_north)
409 || (d->m_north >= other.d->m_south && d->m_south <= other.d->m_south)
411 || (other.d->m_north >= d->m_south && other.d->m_south <= d->m_south)) {
412 if (!crossesDateLine()) {
413 if (!other.crossesDateLine()) {
416 if ((d->m_east >= other.d->m_east && d->m_west <= other.d->m_east)
418 || (other.d->m_east >= d->m_east && other.d->m_west <= d->m_east)
420 || (d->m_east >= other.d->m_west && d->m_west <= other.d->m_west)
422 || (other.d->m_east >= d->m_west && other.d->m_west <= d->m_west)) {
429 if (d->m_west <= other.d->m_east || d->m_east >= other.d->m_west) {
434 if (other.crossesDateLine()) {
443 if (other.d->m_west <= d->m_east || other.d->m_east >= d->m_west) {
472 result.setNorth(qMax(d->m_north, other.
north()));
473 result.setSouth(qMin(d->m_south, other.
south()));
475 qreal w1 = d->m_west;
476 qreal w2 = other.
west();
477 qreal e1 = d->m_east;
478 qreal e2 = other.
east();
480 bool const idl1 = d->m_east < d->m_west;
481 bool const idl2 = other.d->m_east < other.d->m_west;
498 if (fabs(c2.longitude() - c1.longitude()) > M_PI || (idl1 ^ idl2)) {
501 result.setEast(qMin(e1, e2));
502 result.setWest(qMax(w1, w2));
505 result.setEast(qMax(e1, e2));
506 result.setWest(qMin(w1, w2));
521 const qreal cosRotation = cos(rotation());
522 const qreal sinRotation = sin(rotation());
524 qreal centerLat = center().latitude();
525 qreal centerLon = center().longitude();
526 if (
GeoDataLatLonBox(0, 0, center().longitude(), west()).crossesDateLine()) {
530 centerLon += 2 * M_PI;
535 bool northSet =
false;
536 bool southSet =
false;
537 bool eastSet =
false;
538 bool westSet =
false;
541 const qreal lon = coord.longitude();
542 const qreal lat = coord.latitude();
544 const qreal rotatedLon = (lon - centerLon) * cosRotation - (lat - centerLat) * sinRotation + centerLon;
545 const qreal rotatedLat = (lon - centerLon) * sinRotation + (lat - centerLat) * cosRotation + centerLat;
547 if (!northSet || rotatedLat > box.
north()) {
549 box.setNorth(rotatedLat);
552 if (!southSet || rotatedLat < box.
south()) {
554 box.setSouth(rotatedLat);
557 if (!westSet || rotatedLon < box.
west()) {
559 box.setWest(rotatedLon);
562 if (!eastSet || rotatedLon > box.
east()) {
564 box.setEast(rotatedLon);
575 GeoDataObject::operator=(other);
581GeoDataLatLonBox GeoDataLatLonBox::operator|(
const GeoDataLatLonBox &other)
const
583 return united(other);
588 *
this = united(other);
594 GeoDataObject::pack(stream);
596 stream << d->m_north << d->m_south << d->m_east << d->m_west << d->m_rotation;
601 GeoDataObject::unpack(stream);
603 stream >> d->m_north >> d->m_south >> d->m_east >> d->m_west >> d->m_rotation;
615 GeoDataCoordinates::normalizeLonLat(lon, lat);
623 if (lineString.
size() == 1)
624 return {north, south, east, west};
627 bool idlCrossed =
false;
636 int idlCrossState = 0;
637 int idlMaxCrossState = 0;
638 int idlMinCrossState = 0;
641 qreal otherWest = lon;
642 qreal otherEast = lon;
644 qreal previousLon = lon;
646 int currentSign = (lon < 0) ? -1 : +1;
647 int previousSign = currentSign;
652 bool processingLastNode =
false;
654 while (it != itEnd) {
656 (it)->geoCoordinates(lon, lat);
657 GeoDataCoordinates::normalizeLonLat(lon, lat);
662 }
else if (lat < south) {
666 currentSign = (lon < 0) ? -1 : +1;
677 if (previousSign != currentSign && fabs(previousLon) + fabs(lon) > M_PI) {
679 if (idlCrossed ==
false) {
686 if (previousLon < 0) {
688 if (idlCrossState > idlMaxCrossState) {
689 idlMaxCrossState = idlCrossState;
693 if (idlCrossState < idlMinCrossState) {
694 idlMinCrossState = idlCrossState;
699 if (idlCrossState == 0) {
712 previousSign = currentSign;
714 if (processingLastNode) {
719 if (lineString.
isClosed() && it == itEnd) {
721 processingLastNode =
true;
726 if (idlMinCrossState < 0) {
729 if (idlMaxCrossState > 0) {
732 if ((idlMinCrossState < 0 && idlMaxCrossState > 0) || idlMinCrossState < -1 || idlMaxCrossState > 1 || west <= east) {
744 return {north, south, east, west};
747bool GeoDataLatLonBox::isNull()
const
749 return d->m_north == d->m_south && d->m_east == d->m_west;
752bool GeoDataLatLonBox::isEmpty()
const
754 return *
this == empty;
763 double latDelta = lhs.
height() * factor;
765 if (fabs(lhs.
north() - rhs.
north()) > latDelta)
767 if (fabs(lhs.
south() - rhs.
south()) > latDelta)
772 double lonDelta = lhs.
width() * factor;
774 double lhsEast = lhs.
east();
775 double rhsEast = rhs.
east();
777 if (!GeoDataLatLonBox::crossesDateLine(lhsEast, rhsEast)) {
778 if (fabs(lhsEast - rhsEast) > lonDelta)
781 if (lhsEast < 0 && rhsEast > 0) {
783 if (fabs(lhsEast - rhsEast) > lonDelta)
786 if (lhsEast > 0 && rhsEast < 0) {
788 if (fabs(lhsEast - rhsEast) > lonDelta)
793 double lhsWest = lhs.
west();
794 double rhsWest = rhs.
west();
796 if (!GeoDataLatLonBox::crossesDateLine(lhsWest, rhsWest)) {
797 if (fabs(lhsWest - rhsWest) > lonDelta)
800 if (lhsWest < 0 && rhsWest > 0) {
802 if (fabs(lhsWest - rhsWest) > lonDelta)
805 if (lhsWest > 0 && rhsWest < 0) {
807 if (fabs(lhsWest - rhsWest) > lonDelta)
815void GeoDataLatLonBox::clear()
A 3d point representation.
Unit
enum used constructor to specify the units used
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.
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.
QList< GeoDataCoordinates >::ConstIterator constBegin() const
Returns a const iterator that points to the begin of the LineString.
QList< GeoDataCoordinates >::ConstIterator constEnd() const
Returns a const iterator that points to the end of the LineString.
KIOCORE_EXPORT bool operator!=(const UDSEntry &entry, const UDSEntry &other)
bool operator==(const StyleDelim &l, const StyleDelim &r)
Binds a QML item to a specific geodetic location in screen coordinates.
@ SouthPole
Only South Pole.
@ NorthPole
Only North Pole.
void append(QList< T > &&value)
void reserve(qsizetype size)