summaryrefslogtreecommitdiffstats
path: root/server/src/geo/wgs84.cppm
blob: d574d397bc00bfe79c7616e00a18a0dd5cdb3c8f (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
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