1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
module;
#include <boost/geometry.hpp>
export module routemon:geo.wgs84;
import :geo;
namespace routemon::geo::wgs84 {
using cs = bgeo::cs::geographic<bgeo::degree>;
using point = bgeo::model::point<double, 2, cs>;
// TODO: guarantee that linestring provides non-static member
// auto reserve(std::size_t) -> void
// Perhaps better yet: guarantee that the backing container is a std::vector.
using linestring =
bgeo::model::linestring<point, std::vector /* the default */>;
using box = bgeo::model::box<point>;
using stype = bgeo::srs::spheroid<double>;
using vincenty_strategy = bgeo::strategy::distance::vincenty<stype>;
inline auto from_lat_lon(double lat, double lon) -> point
{
return point{lon, lat};
}
inline auto lat(point p) -> double { return bgeo::get<1>(p); }
inline auto lon(point p) -> double { return bgeo::get<0>(p); }
// Point p with latitude in [-90, 90] and longitude in [-180, 180)
auto normalize(point p) -> point
{
// Equivalent degrees in the range [0, 360)
auto nonneg_mod360 = [](double t) -> double
{ return std::remainder(std::remainder(t, 360.0) + 360.0, 360.0); };
auto p_lon = lon(p);
auto p_lat = nonneg_mod360(lat(p)) - 90.0;
if (90.0 < p_lat)
{
assert(p_lat < 270.0); // by nonneg_mod360
p_lon += 180.0;
p_lat -= 180.0;
}
p_lon = nonneg_mod360(p_lon + 180.0) - 180.0;
return from_lat_lon(p_lat, p_lon);
}
inline auto is_normalized(point p) -> bool
{
return -90 <= lat(p) && lat(p) <= 90 && -180 <= lon(p) && lon(p) < 180;
}
class normalized_point
{
point p_;
public:
inline normalized_point(point p) : p_{is_normalized(p) ? p : normalize(p)} {}
inline normalized_point() : p_{} {}
inline auto lat() const -> double { return bgeo::get<1>(p_); }
inline auto lon() const -> double { return bgeo::get<0>(p_); }
inline auto lat(double lat) -> void
{
if (lat < -90 || lat > 90)
throw std::range_error{"latitude out of range"};
bgeo::set<1>(p_, lat);
}
inline auto lon(double lon) -> void
{
if (lon < -180 || lon > 180)
throw std::range_error{"longitude out of range"};
bgeo::set<0>(p_, lon);
}
};
class normalized_linestring
{
std::vector<normalized_point> ls_;
public:
inline auto empty() const -> bool { return ls_.empty(); }
inline auto push_back(normalized_point p) -> void { ls_.push_back(p); }
};
} // namespace routemon::geo::wgs84
|