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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
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>;
auto from_lat_lon(double lat, double lon) -> point
{
return point{lon, lat};
}
auto lat(point p) -> double
{
return bgeo::get<1>(p);
}
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);
}
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:
normalized_point(point p)
: p_{is_normalized(p) ? p : normalize(p)}
{}
normalized_point()
: p_{}
{}
auto lat() const -> double
{
return bgeo::get<1>(p_);
}
auto lon() const -> double
{
return bgeo::get<0>(p_);
}
auto lat(double lat) -> void
{
if (lat < -90 || lat > 90)
throw std::range_error{"latitude out of range"};
bgeo::set<1>(p_, lat);
}
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:
auto empty() const -> bool
{
return ls_.empty();
}
auto push_back(normalized_point p) -> void
{
ls_.push_back(p);
}
};
} // namespace routemon::geo::wgs84
|