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