summaryrefslogtreecommitdiffstats
path: root/server/src/geo/wgs84.cppm
blob: b53a963ea8b57c61202f7d9e33493d5599731f7f (about) (plain) (blame)
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