1 #pragma once
2 
3 #include <mbgl/math/clamp.hpp>
4 #include <mbgl/math/wrap.hpp>
5 #include <mbgl/util/constants.hpp>
6 
7 #include <mapbox/geometry/point.hpp>
8 #include <mapbox/geometry/point_arithmetic.hpp>
9 #include <mapbox/geometry/line_string.hpp>
10 #include <mapbox/geometry/box.hpp>
11 
12 #include <cmath>
13 #include <stdexcept>
14 
15 namespace mbgl {
16 
17 class CanonicalTileID;
18 class UnwrappedTileID;
19 
20 using ScreenCoordinate = mapbox::geometry::point<double>;
21 using ScreenLineString = mapbox::geometry::line_string<double>;
22 using ScreenBox        = mapbox::geometry::box<double>;
23 
24 class LatLng {
25 private:
26     double lat;
27     double lon;
28 
29 public:
30     enum WrapMode : bool { Unwrapped, Wrapped };
31 
LatLng(double lat_=0,double lon_=0,WrapMode mode=Unwrapped)32     LatLng(double lat_ = 0, double lon_ = 0, WrapMode mode = Unwrapped)
33         : lat(lat_), lon(lon_) {
34         if (std::isnan(lat)) {
35             throw std::domain_error("latitude must not be NaN");
36         }
37         if (std::isnan(lon)) {
38             throw std::domain_error("longitude must not be NaN");
39         }
40         if (std::abs(lat) > 90.0) {
41             throw std::domain_error("latitude must be between -90 and 90");
42         }
43         if (!std::isfinite(lon)) {
44             throw std::domain_error("longitude must not be infinite");
45         }
46         if (mode == Wrapped) {
47             wrap();
48         }
49     }
50 
latitude() const51     double latitude() const { return lat; }
longitude() const52     double longitude() const { return lon; }
53 
wrapped() const54     LatLng wrapped() const { return { lat, lon, Wrapped }; }
55 
wrap()56     void wrap() {
57         lon = util::wrap(lon, -util::LONGITUDE_MAX, util::LONGITUDE_MAX);
58     }
59 
60     // If the distance from start to end longitudes is between half and full
61     // world, unwrap the start longitude to ensure the shortest path is taken.
unwrapForShortestPath(const LatLng & end)62     void unwrapForShortestPath(const LatLng& end) {
63         const double delta = std::abs(end.lon - lon);
64         if (delta < util::LONGITUDE_MAX || delta > util::DEGREES_MAX) return;
65         if (lon > 0 && end.lon < 0) lon -= util::DEGREES_MAX;
66         else if (lon < 0 && end.lon > 0) lon += util::DEGREES_MAX;
67     }
68 
69     // Constructs a LatLng object with the top left position of the specified tile.
70     LatLng(const CanonicalTileID& id);
71     LatLng(const UnwrappedTileID& id);
72 
operator ==(const LatLng & a,const LatLng & b)73     friend bool operator==(const LatLng& a, const LatLng& b) {
74         return a.lat == b.lat && a.lon == b.lon;
75     }
76 
operator !=(const LatLng & a,const LatLng & b)77     friend bool operator!=(const LatLng& a, const LatLng& b) {
78         return !(a == b);
79     }
80 };
81 
82 class LatLngBounds {
83 public:
84     // Return a bounds covering the entire (unwrapped) world.
world()85     static LatLngBounds world() {
86         return LatLngBounds({-90, -180}, {90, 180});
87     }
88 
89     // Return the bounds consisting of the single point.
singleton(const LatLng & a)90     static LatLngBounds singleton(const LatLng& a) {
91         return LatLngBounds(a, a);
92     }
93 
94     // Return the convex hull of two points; the smallest bounds that contains both.
hull(const LatLng & a,const LatLng & b)95     static LatLngBounds hull(const LatLng& a, const LatLng& b) {
96         LatLngBounds bounds(a, a);
97         bounds.extend(b);
98         return bounds;
99     }
100 
101     // Return a bounds that may serve as the identity element for the extend operation.
empty()102     static LatLngBounds empty() {
103         LatLngBounds bounds = world();
104         std::swap(bounds.sw, bounds.ne);
105         return bounds;
106     }
107 
108     // Constructs a LatLngBounds object with the tile's exact boundaries.
109     LatLngBounds(const CanonicalTileID&);
110 
valid() const111     bool valid() const {
112         return (sw.latitude() <= ne.latitude()) && (sw.longitude() <= ne.longitude());
113     }
114 
south() const115     double south() const { return sw.latitude(); }
west() const116     double west()  const { return sw.longitude(); }
north() const117     double north() const { return ne.latitude(); }
east() const118     double east()  const { return ne.longitude(); }
119 
southwest() const120     LatLng southwest() const { return sw; }
northeast() const121     LatLng northeast() const { return ne; }
southeast() const122     LatLng southeast() const { return LatLng(south(), east()); }
northwest() const123     LatLng northwest() const { return LatLng(north(), west()); }
124 
center() const125     LatLng center() const {
126         return LatLng((sw.latitude() + ne.latitude()) / 2,
127                       (sw.longitude() + ne.longitude()) / 2);
128     }
129 
constrain(const LatLng & p) const130     LatLng constrain(const LatLng& p) const {
131         if (contains(p)) {
132             return p;
133         }
134         return LatLng {
135             util::clamp(p.latitude(), sw.latitude(), ne.latitude()),
136             util::clamp(p.longitude(), sw.longitude(), ne.longitude())
137         };
138     }
139 
extend(const LatLng & point)140     void extend(const LatLng& point) {
141         sw = LatLng(std::min(point.latitude(), sw.latitude()),
142                     std::min(point.longitude(), sw.longitude()));
143         ne = LatLng(std::max(point.latitude(), ne.latitude()),
144                     std::max(point.longitude(), ne.longitude()));
145     }
146 
extend(const LatLngBounds & bounds)147     void extend(const LatLngBounds& bounds) {
148         extend(bounds.sw);
149         extend(bounds.ne);
150     }
151 
isEmpty() const152     bool isEmpty() const {
153         return sw.latitude() > ne.latitude() ||
154                sw.longitude() > ne.longitude();
155     }
156 
crossesAntimeridian() const157     bool crossesAntimeridian() const {
158         return (sw.wrapped().longitude() > ne.wrapped().longitude());
159     }
160 
161     bool contains(const CanonicalTileID& tileID) const;
162     bool contains(const LatLng& point, LatLng::WrapMode wrap = LatLng::Unwrapped) const;
163     bool contains(const LatLngBounds& area, LatLng::WrapMode wrap = LatLng::Unwrapped) const;
164 
165     bool intersects(const LatLngBounds area, LatLng::WrapMode wrap = LatLng::Unwrapped) const;
166 
167 private:
168     LatLng sw;
169     LatLng ne;
170 
LatLngBounds(LatLng sw_,LatLng ne_)171     LatLngBounds(LatLng sw_, LatLng ne_)
172         : sw(std::move(sw_)), ne(std::move(ne_)) {}
173 
operator ==(const LatLngBounds & a,const LatLngBounds & b)174     friend bool operator==(const LatLngBounds& a, const LatLngBounds& b) {
175         return a.sw == b.sw && a.ne == b.ne;
176     }
177 
operator !=(const LatLngBounds & a,const LatLngBounds & b)178     friend bool operator!=(const LatLngBounds& a, const LatLngBounds& b) {
179         return !(a == b);
180     }
181 };
182 
183 // Determines the orientation of the map.
184 enum class NorthOrientation : uint8_t {
185     Upwards, // Default
186     Rightwards,
187     Downwards,
188     Leftwards,
189 };
190 
191 /// The distance on each side between a rectangle and a rectangle within.
192 class EdgeInsets {
193 private:
194     double _top;    // Number of pixels inset from the top edge.
195     double _left;   // Number of pixels inset from the left edge.
196     double _bottom; // Number of pixels inset from the bottom edge.
197     double _right;  // Number of pixels inset from the right edge.
198 
199 public:
EdgeInsets(double t_=0,double l_=0,double b_=0,double r_=0)200     EdgeInsets(double t_ = 0, double l_ = 0, double b_ = 0, double r_ = 0)
201         : _top(t_), _left(l_), _bottom(b_), _right(r_) {
202         if (std::isnan(_top)) {
203             throw std::domain_error("top must not be NaN");
204         }
205         if (std::isnan(_left)) {
206             throw std::domain_error("left must not be NaN");
207         }
208         if (std::isnan(_bottom)) {
209             throw std::domain_error("bottom must not be NaN");
210         }
211         if (std::isnan(_right)) {
212             throw std::domain_error("right must not be NaN");
213         }
214     }
215 
top() const216     double top() const { return _top; }
left() const217     double left() const { return _left; }
bottom() const218     double bottom() const { return _bottom; }
right() const219     double right() const { return _right; }
220 
isFlush() const221     bool isFlush() const {
222         return _top == 0 && _left == 0 && _bottom == 0 && _right == 0;
223     }
224 
operator +=(const EdgeInsets & o)225     void operator+=(const EdgeInsets& o) {
226         _top += o._top;
227         _left += o._left;
228         _bottom += o._bottom;
229         _right += o._right;
230     }
231 
operator +(const EdgeInsets & o) const232     EdgeInsets operator+(const EdgeInsets& o) const {
233         return {
234             _top + o._top, _left + o._left, _bottom + o._bottom, _right + o._right,
235         };
236     }
237 
238     ScreenCoordinate getCenter(uint16_t width, uint16_t height) const;
239 
operator ==(const EdgeInsets & a,const EdgeInsets & b)240     friend bool operator==(const EdgeInsets& a, const EdgeInsets& b) {
241         return a._top == b._top && a._left == b._left && a._bottom == b._bottom && a._right == b._right;
242     }
243 
operator !=(const EdgeInsets & a,const EdgeInsets & b)244     friend bool operator!=(const EdgeInsets& a, const EdgeInsets& b) {
245         return !(a == b);
246     }
247 };
248 
249 } // namespace mbgl
250