summaryrefslogtreecommitdiffstats
path: root/server/src/geo/multizonal.cppm
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/geo/multizonal.cppm')
-rw-r--r--server/src/geo/multizonal.cppm307
1 files changed, 0 insertions, 307 deletions
diff --git a/server/src/geo/multizonal.cppm b/server/src/geo/multizonal.cppm
deleted file mode 100644
index 7930367..0000000
--- a/server/src/geo/multizonal.cppm
+++ /dev/null
@@ -1,307 +0,0 @@
1module;
2
3#include <boost/geometry.hpp>
4#include <boost/geometry/srs/epsg.hpp>
5#include <boost/geometry/srs/transformation.hpp>
6
7export module routemon:geo.multizonal;
8
9import std;
10import :geo;
11import :geo.utm;
12import :geo.utm.zone_local;
13import :geo.wgs84;
14
15namespace routemon::geo::multizonal {
16
17template<std::default_initializable T>
18class multi_zone
19{
20 std::array<T, utm::zone::max().as_index() + 1> zones_;
21
22public:
23 auto operator[](utm::zone z) -> T&
24 {
25 return zones_[z.as_index()];
26 }
27
28 auto operator[](utm::zone z) const -> T const&
29 {
30 return zones_[z.as_index()];
31 }
32};
33
34namespace wgs84
35{
36
37// Transformations from WGS 84 (EPSG:4326)
38using transform_from_t = bgeo::srs::transformation<bgeo::srs::static_epsg<4326>>;
39
40class utm_transform : public transform_from_t
41{
42 utm::zone to_zone_;
43
44public:
45 explicit utm_transform(utm::zone to_zone)
46 : transform_from_t{{}, bgeo::srs::epsg{to_zone.wgs84_proj_epsg()}},
47 to_zone_{to_zone}
48 {
49 }
50
51 utm_transform()
52 : utm_transform{utm::zone::min()}
53 {}
54
55 auto apply(utm::zonable_wgs84_point p) -> utm::zone_local::point
56 {
57 auto local_p = utm::zone_local::point{to_zone_, 0.0, 0.0};
58 forward(p, local_p);
59 return local_p;
60 }
61};
62
63class utm_transforms : public multi_zone<utm_transform>
64{
65 utm_transforms()
66 {
67 for (auto z = utm::zone::min(); z != utm::zone::max(); z = z.next())
68 {
69 (*this)[z] = utm_transform{z};
70 }
71 }
72
73public:
74 static utm_transforms const& instance()
75 {
76 static utm_transforms inst;
77 return inst;
78 }
79};
80
81auto neighbor_utm_zone(utm::zonable_wgs84_point p) -> std::pair<utm::zone, double>
82{
83 auto separating_meridian_lon = std::round(p.lon() / 6.0) * 6.0;
84 auto closest_zone_middle = separating_meridian_lon < p.lon()
85 ? separating_meridian_lon - 3.0
86 : separating_meridian_lon + 3.0;
87 auto closest_zone = utm::zone::for_wgs84_point(utm::zonable_wgs84_point::from(geo::wgs84::normalized_point{geo::wgs84::from_lat_lon(p.lat(), closest_zone_middle)}).value());
88
89 auto separating_meridian_ls = geo::wgs84::linestring{
90 geo::wgs84::from_lat_lon(90.0, 0.0),
91 geo::wgs84::from_lat_lon(0.0, separating_meridian_lon),
92 geo::wgs84::from_lat_lon(-90.0, 0.0),
93 };
94 auto closest_zone_dist = bgeo::distance(p, separating_meridian_ls, geo::wgs84::vincenty_strategy{});
95
96 return std::make_pair(closest_zone, closest_zone_dist);
97}
98
99} // namespace wgs84
100
101struct multizone_linestring
102{
103 multi_zone<std::vector<utm::zone_local::prim::linestring>> segments;
104
105 // TODO: consider taking an input range instead
106 explicit multizone_linestring(utm::zonable_wgs84_linestring const& ls)
107 {
108 auto to_utm = wgs84::utm_transforms::instance();
109 auto working = multi_zone<utm::zone_local::prim::linestring>{};
110 auto mprev_p_zone = std::optional<utm::zone>{};
111 auto mprev_p_alt_zone = std::optional<utm::zone>{};
112
113 auto push = [&](utm::zonable_wgs84_point p, utm::zone z)
114 { working[z].push_back(to_utm[z].apply(p)); };
115 auto flush = [&](utm::zone z)
116 {
117 segments[z].push_back(std::move(working[z]));
118 working[z] = {};
119 };
120
121 for (auto const& p : ls)
122 {
123 auto p_zone = utm::zone::for_wgs84_point(p);
124 auto mp_alt_zone = std::optional<utm::zone>{};
125 if (auto [neighbor_zone, neighbor_zone_dist] = wgs84::neighbor_utm_zone(p);
126 neighbor_zone_dist < 30.0 /* m */)
127 mp_alt_zone = neighbor_zone;
128 assert(!mp_alt_zone || *mp_alt_zone != p_zone);
129
130 push(p, p_zone);
131 if (mp_alt_zone)
132 push(p, *mp_alt_zone);
133 if (mprev_p_zone && *mprev_p_zone != p_zone && mprev_p_zone != mp_alt_zone)
134 {
135 push(p, *mprev_p_zone);
136 flush(*mprev_p_zone);
137 }
138 if (mprev_p_alt_zone && *mprev_p_alt_zone != p_zone && mprev_p_alt_zone != mp_alt_zone)
139 {
140 push(p, *mprev_p_alt_zone);
141 flush(*mprev_p_alt_zone);
142 }
143
144 mprev_p_zone = p_zone;
145 mprev_p_alt_zone = mp_alt_zone;
146 }
147
148 if (mprev_p_zone && !working[*mprev_p_zone].empty())
149 {
150 flush(*mprev_p_zone);
151 }
152 if (mprev_p_alt_zone && !working[*mprev_p_alt_zone].empty())
153 {
154 flush(*mprev_p_alt_zone);
155 }
156 }
157};
158
159struct zoned_linestring_seg_seq
160{
161 std::vector<utm::zone_local::linestring> segments;
162
163 explicit zoned_linestring_seg_seq(utm::zonable_wgs84_linestring ls)
164 {
165 auto to_utm = wgs84::utm_transforms::instance();
166 auto mworking_seg = std::optional<utm::zone_local::linestring>{};
167
168 for (auto const& p : ls)
169 {
170 auto p_zone = utm::zone::for_wgs84_point(p);
171 if (mworking_seg && mworking_seg->zone != p_zone)
172 {
173 mworking_seg->push_back(to_utm[mworking_seg->zone].apply(p));
174 segments.push_back(std::move(*mworking_seg));
175 mworking_seg = std::nullopt;
176 }
177 if (!mworking_seg)
178 mworking_seg = utm::zone_local::linestring{p_zone};
179 mworking_seg->push_back(to_utm[p_zone].apply(p));
180 }
181
182 if (mworking_seg && !mworking_seg->empty())
183 {
184 segments.push_back(std::move(*mworking_seg));
185 }
186 }
187};
188
189template<class T>
190class linestring_rtree
191{
192public:
193 using index_value = std::tuple<utm::zone_local::prim::box, utm::zone_local::prim::linestring, T>;
194
195private:
196 multi_zone<bgeo::index::rtree<index_value, bgeo::index::quadratic<16>>> local_rtrees_;
197
198public:
199 template<std::convertible_to<T> U>
200 auto insert(utm::zonable_wgs84_linestring const& ls, U&& arg) -> void
201 {
202 auto zone_segments = multizone_linestring{ls}.segments;
203 for (auto z = utm::zone::min(); z != utm::zone::max(); z = z.next())
204 {
205 for (auto ls : zone_segments[z])
206 {
207 auto blse =
208 bgeo::return_buffer<utm::zone_local::prim::box>(bgeo::return_envelope<utm::zone_local::prim::box>(ls), 30.0 /* m */);
209 local_rtrees_[z].insert(index_value{blse, std::move(ls), std::forward<U>(arg)});
210 }
211 }
212 }
213
214 auto size() const -> std::size_t
215 {
216 auto total_size = 0uz;
217 for (auto z = utm::zone::min(); z != utm::zone::max(); z = z.next())
218 total_size += local_rtrees_[z].size();
219 return total_size;
220 }
221
222 // Note: the same T may be generated more than once!
223 auto intersection(zoned_linestring_seg_seq const& lss) const -> std::generator<T const&>
224 {
225 for (auto const& ls : lss.segments)
226 {
227 // auto ls_box = bgeo::return_envelope<utm::zone_local::prim::box>(ls);
228 for (auto it = local_rtrees_[ls.zone].qbegin(bgeo::index::intersects(ls));
229 it != local_rtrees_[ls.zone].qend(); it++)
230 {
231 using multi_linestring = bgeo::model::multi_linestring<utm::zone_local::prim::linestring>;
232
233 auto relevant_ls_parts = multi_linestring{};
234 bgeo::intersection(std::get<0>(*it), static_cast<utm::zone_local::prim::linestring const&>(ls), relevant_ls_parts);
235 if (bgeo::distance(relevant_ls_parts, std::get<1>(*it)) < 30.0 /* m */)
236 {
237 co_yield std::get<2>(*it);
238 }
239 }
240 }
241 }
242};
243
244template<class T>
245class point_rtree
246{
247public:
248 using index_value = std::tuple<utm::zone_local::prim::box, utm::zone_local::prim::point, T>;
249
250private:
251 multi_zone<bgeo::index::rtree<index_value, bgeo::index::quadratic<16>>> local_rtrees_;
252
253public:
254 template<std::convertible_to<T> U>
255 auto insert(utm::zonable_wgs84_point const& p, U&& arg) -> void
256 {
257 auto to_utm = wgs84::utm_transforms::instance();
258
259 auto p_zone = utm::zone::for_wgs84_point(p);
260 auto mp_alt_zone = std::optional<utm::zone>{};
261 if (auto [neighbor_zone, neighbor_zone_dist] = wgs84::neighbor_utm_zone(p);
262 neighbor_zone_dist < 30.0 /* m */)
263 mp_alt_zone = neighbor_zone;
264
265 auto insert = [&](utm::zone z) -> void
266 {
267 auto p_utm = to_utm[z].apply(p);
268 auto bpe = bgeo::return_buffer<utm::zone_local::prim::box>(bgeo::return_envelope<utm::zone_local::prim::box>(p_utm), 30.0 /* m */);
269 local_rtrees_[z].insert(index_value{bpe, p_utm, arg});
270 };
271
272 insert(p_zone);
273 if (mp_alt_zone)
274 insert(*mp_alt_zone);
275 }
276
277 auto size() const -> std::size_t
278 {
279 auto total_size = 0uz;
280 for (auto z = utm::zone::min(); z != utm::zone::max(); z = z.next())
281 total_size += local_rtrees_[z].size();
282 return total_size;
283 }
284
285 // Note: the same T may be generated more than once!
286 auto intersection(zoned_linestring_seg_seq const& lss) const -> std::generator<T const&>
287 {
288 for (auto const& ls : lss.segments)
289 {
290 // auto ls_box = bgeo::return_envelope<utm::zone_local::prim::box>(ls);
291 for (auto it = local_rtrees_[ls.zone].qbegin(bgeo::index::intersects(ls));
292 it != local_rtrees_[ls.zone].qend(); it++)
293 {
294 using multi_linestring = bgeo::model::multi_linestring<utm::zone_local::prim::linestring>;
295
296 auto relevant_ls_parts = multi_linestring{};
297 bgeo::intersection(std::get<0>(*it), static_cast<utm::zone_local::prim::linestring const&>(ls), relevant_ls_parts);
298 if (bgeo::distance(relevant_ls_parts, std::get<1>(*it)) < 30.0 /* m */)
299 {
300 co_yield std::get<2>(*it);
301 }
302 }
303 }
304 }
305};
306
307} // namespace routemon::geo::multizonal