diff options
| author | Rutger Broekhoff | 2026-09-08 01:36:38 +0200 |
|---|---|---|
| committer | Rutger Broekhoff | 2026-09-08 01:37:34 +0200 |
| commit | 3e8ce840c70ee00925210b96738696d1dc445f8f (patch) | |
| tree | 4eb6b30b1d571daa596394c36e7769f6ee0af956 /server/src/api.cpp | |
| parent | 15bcf23f2e75f9c710eee3753d530ebefc5f78f1 (diff) | |
| download | routemon-3e8ce840c70ee00925210b96738696d1dc445f8f.tar.gz routemon-3e8ce840c70ee00925210b96738696d1dc445f8f.zip | |
UTM projection
Diffstat (limited to 'server/src/api.cpp')
| -rw-r--r-- | server/src/api.cpp | 86 |
1 files changed, 26 insertions, 60 deletions
diff --git a/server/src/api.cpp b/server/src/api.cpp index 5dd3fc5..137666b 100644 --- a/server/src/api.cpp +++ b/server/src/api.cpp | |||
| @@ -17,12 +17,14 @@ namespace views = std::views; | |||
| 17 | 17 | ||
| 18 | namespace routemon::api { | 18 | namespace routemon::api { |
| 19 | 19 | ||
| 20 | auto json_value_from_point(geo::point const& p) -> json::value | 20 | auto json_value_from_point(geo::utm::zonable_wgs84_point const& p) |
| 21 | -> json::value | ||
| 21 | { | 22 | { |
| 22 | return json::array{bgeo::get<1>(p), bgeo::get<0>(p)}; | 23 | return json::array{p.lat(), p.lon()}; |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | auto json_value_from_linestring(geo::linestring const& ls) -> json::value | 26 | auto json_value_from_linestring(geo::utm::zonable_wgs84_linestring const& ls) |
| 27 | -> json::value | ||
| 26 | { | 28 | { |
| 27 | json::array a; | 29 | json::array a; |
| 28 | for (auto const& p : ls) | 30 | for (auto const& p : ls) |
| @@ -30,8 +32,8 @@ auto json_value_from_linestring(geo::linestring const& ls) -> json::value | |||
| 30 | return a; | 32 | return a; |
| 31 | } | 33 | } |
| 32 | 34 | ||
| 33 | auto json_value_from_linestrings(std::vector<geo::linestring> const& lss) | 35 | auto json_value_from_linestrings( |
| 34 | -> json::value | 36 | std::vector<geo::utm::zonable_wgs84_linestring> const& lss) -> json::value |
| 35 | { | 37 | { |
| 36 | json::array a; | 38 | json::array a; |
| 37 | for (auto const& ls : lss) | 39 | for (auto const& ls : lss) |
| @@ -108,24 +110,7 @@ handler::handler(log::logger const& l, datex2::situation_publication pub) | |||
| 108 | static_cast<float>(i) / static_cast<float>(nsit) * 100.0f); | 110 | static_cast<float>(i) / static_cast<float>(nsit) * 100.0f); |
| 109 | }; | 111 | }; |
| 110 | 112 | ||
| 111 | l_.info("Building indices for {} situations", nsit); | 113 | l_.info("Building indexes for {} situations", nsit); |
| 112 | |||
| 113 | auto const buffer_distance = min_distance_; | ||
| 114 | auto const points_per_circle = 8; | ||
| 115 | // Note: thomas strategy does not work for geographic_join_round; | ||
| 116 | // need to use andoyer for that. | ||
| 117 | using formula = bgeo::strategy::andoyer; | ||
| 118 | bgeo::strategy::buffer::distance_symmetric<double> distance_strategy{ | ||
| 119 | buffer_distance | ||
| 120 | }; | ||
| 121 | bgeo::strategy::buffer::geographic_join_round<formula> join_strategy{ | ||
| 122 | points_per_circle | ||
| 123 | }; | ||
| 124 | bgeo::strategy::buffer::geographic_end_round<formula> end_strategy{4}; | ||
| 125 | bgeo::strategy::buffer::geographic_point_circle<formula> circle_strategy{ | ||
| 126 | points_per_circle | ||
| 127 | }; | ||
| 128 | bgeo::strategy::buffer::geographic_side_straight<formula> side_strategy; | ||
| 129 | 114 | ||
| 130 | l_.info("Progress: 0%"); | 115 | l_.info("Progress: 0%"); |
| 131 | auto const before_build = chrono::steady_clock::now(); | 116 | auto const before_build = chrono::steady_clock::now(); |
| @@ -135,17 +120,11 @@ handler::handler(log::logger const& l, datex2::situation_publication pub) | |||
| 135 | { | 120 | { |
| 136 | for (auto const& ls : rc->relevant_line_strings) | 121 | for (auto const& ls : rc->relevant_line_strings) |
| 137 | { | 122 | { |
| 138 | auto buffered_ls = geo::multi_polygon{}; | 123 | blse_index_.insert(*ls, rc); |
| 139 | bgeo::buffer( | ||
| 140 | *ls, buffered_ls, distance_strategy, side_strategy, join_strategy, | ||
| 141 | end_strategy, circle_strategy); | ||
| 142 | auto blse = geo::box{}; | ||
| 143 | bgeo::envelope(buffered_ls, blse); | ||
| 144 | blse_index_.insert(std::make_tuple(blse, buffered_ls, rc)); | ||
| 145 | } | 124 | } |
| 146 | for (auto p : rc->relevant_points) | 125 | for (auto p : rc->relevant_points) |
| 147 | { | 126 | { |
| 148 | p_index_.insert(std::make_pair(p, rc)); | 127 | p_index_.insert(p_index_value{p, rc}); |
| 149 | } | 128 | } |
| 150 | } | 129 | } |
| 151 | 130 | ||
| @@ -160,7 +139,7 @@ handler::handler(log::logger const& l, datex2::situation_publication pub) | |||
| 160 | 139 | ||
| 161 | auto const after_build = chrono::steady_clock::now(); | 140 | auto const after_build = chrono::steady_clock::now(); |
| 162 | auto const dur_build_s = chrono::duration<double>{after_build - before_build}; | 141 | auto const dur_build_s = chrono::duration<double>{after_build - before_build}; |
| 163 | l_.info("Indices built in {}", dur_build_s); | 142 | l_.info("Indexes built in {}", dur_build_s); |
| 164 | l_.info("BLSE index size: {}", blse_index_.size()); | 143 | l_.info("BLSE index size: {}", blse_index_.size()); |
| 165 | l_.info("Point index size: {}", p_index_.size()); | 144 | l_.info("Point index size: {}", p_index_.size()); |
| 166 | } | 145 | } |
| @@ -175,16 +154,13 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 175 | auto const check_periods = time::period_seq{relevant.begin(), relevant.end()}; | 154 | auto const check_periods = time::period_seq{relevant.begin(), relevant.end()}; |
| 176 | 155 | ||
| 177 | // TODO: eliminate use of overlap segments | 156 | // TODO: eliminate use of overlap segments |
| 178 | auto splits_with_overlap_segments = std::vector<geo::linestring>{}; | 157 | auto segments = std::vector<geo::utm::zonable_wgs84_linestring>{}; |
| 179 | for (auto const& track : gpx_file.tracks) | 158 | for (auto const& track : gpx_file.tracks) |
| 180 | for (auto const& seg : track.segments) | 159 | for (auto const& seg : track.segments) |
| 181 | geo::split_linestring_with_overlap_segments( | 160 | segments.push_back(seg.waypoints); |
| 182 | seg.waypoints, | ||
| 183 | 5000 /* meters max total dist until a new split is forced */, | ||
| 184 | splits_with_overlap_segments); | ||
| 185 | auto const before_query = chrono::steady_clock::now(); | 161 | auto const before_query = chrono::steady_clock::now(); |
| 186 | 162 | ||
| 187 | auto vincenty_strategy = geo::vincenty_strategy{}; | 163 | auto vincenty_strategy = geo::wgs84::vincenty_strategy{}; |
| 188 | 164 | ||
| 189 | l_.debug("Querying for relevant situations"); | 165 | l_.debug("Querying for relevant situations"); |
| 190 | auto relevant_road_closures = | 166 | auto relevant_road_closures = |
| @@ -192,28 +168,17 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 192 | auto ls_checked = 0uz; | 168 | auto ls_checked = 0uz; |
| 193 | auto p_checked = 0uz; | 169 | auto p_checked = 0uz; |
| 194 | auto i = 0; | 170 | auto i = 0; |
| 195 | for (geo::linestring const& part : splits_with_overlap_segments) | 171 | for (auto const& part : segments) |
| 196 | { | 172 | { |
| 197 | l_.debug("Checking part [{}/{}]", ++i, splits_with_overlap_segments.size()); | 173 | l_.debug("Checking part [{}/{}]", ++i, segments.size()); |
| 198 | 174 | ||
| 175 | auto part_zone_lss = geo::multizonal::zoned_linestring_seg_seq{part}; | ||
| 199 | // TODO: consider buffering with min_distance_ | 176 | // TODO: consider buffering with min_distance_ |
| 200 | auto part_box = geo::box{}; | 177 | auto part_box = geo::wgs84::box{}; |
| 201 | bgeo::envelope(part, part_box); | 178 | bgeo::envelope(part, part_box); |
| 202 | 179 | ||
| 203 | for (auto it = blse_index_.qbegin(bgeo::index::intersects(part_box)); | 180 | for (auto const& rc : blse_index_.intersection(part_zone_lss)) |
| 204 | it != blse_index_.qend(); it++) | ||
| 205 | { | 181 | { |
| 206 | // Cannot use structured bindings here, as boost::geometry::get | ||
| 207 | // interferes with ADL. It is a candidate as the namespace | ||
| 208 | // boost::geometry is part of the associated namespace set, which | ||
| 209 | // happens because geo::linestring ≡ | ||
| 210 | // boost::geometry::model::linestring<geo::point> is part of the | ||
| 211 | // whole tuple type (blse_index_value) that is the value_type of the | ||
| 212 | // iterator. | ||
| 213 | |||
| 214 | geo::box const& blse = std::get<0>(*it); | ||
| 215 | geo::multi_polygon const& bls = std::get<1>(*it); | ||
| 216 | std::shared_ptr<datex2::road_closure> const& rc = std::get<2>(*it); | ||
| 217 | if (rc->validity | 182 | if (rc->validity |
| 218 | && rc->validity->intersect(check_periods).periods().empty()) | 183 | && rc->validity->intersect(check_periods).periods().empty()) |
| 219 | continue; | 184 | continue; |
| @@ -221,8 +186,7 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 221 | // auto small_parts = bgeo::model::multi_linestring<geo::linestring>{}; | 186 | // auto small_parts = bgeo::model::multi_linestring<geo::linestring>{}; |
| 222 | // bgeo::intersection(part, lse, small_parts); | 187 | // bgeo::intersection(part, lse, small_parts); |
| 223 | 188 | ||
| 224 | if (bgeo::intersects(blse, part) && bgeo::intersects(bls, part)) | 189 | relevant_road_closures.emplace(rc); |
| 225 | relevant_road_closures.emplace(rc); | ||
| 226 | 190 | ||
| 227 | // if (!bgeo::is_empty(small_parts)) | 191 | // if (!bgeo::is_empty(small_parts)) |
| 228 | // if (bgeo::distance(*ls, small_parts, vincenty_strategy) < | 192 | // if (bgeo::distance(*ls, small_parts, vincenty_strategy) < |
| @@ -234,7 +198,7 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 234 | it != p_index_.qend(); it++) | 198 | it != p_index_.qend(); it++) |
| 235 | { | 199 | { |
| 236 | // Cannot use structured bindings here for the same reason as above. | 200 | // Cannot use structured bindings here for the same reason as above. |
| 237 | geo::point const& p = std::get<0>(*it); | 201 | geo::utm::zonable_wgs84_point const& p = std::get<0>(*it); |
| 238 | std::shared_ptr<datex2::road_closure> const& rc = std::get<1>(*it); | 202 | std::shared_ptr<datex2::road_closure> const& rc = std::get<1>(*it); |
| 239 | if (rc->validity | 203 | if (rc->validity |
| 240 | && rc->validity->intersect(check_periods).periods().empty()) | 204 | && rc->validity->intersect(check_periods).periods().empty()) |
| @@ -301,10 +265,12 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 301 | .relevant_lss = | 265 | .relevant_lss = |
| 302 | rc->relevant_line_strings | 266 | rc->relevant_line_strings |
| 303 | | views::transform( | 267 | | views::transform( |
| 304 | [](auto const& lsp) -> geo::linestring | 268 | [](auto const& lsp) |
| 269 | -> geo::utm::zonable_wgs84_linestring | ||
| 305 | { return *lsp; }) | 270 | { return *lsp; }) |
| 306 | | std::ranges:: | 271 | | std::ranges::to<std::vector< |
| 307 | to<std::vector<geo::linestring>>(), | 272 | geo::utm::zonable_wgs84_linestring |
| 273 | >>(), | ||
| 308 | }; | 274 | }; |
| 309 | }) | 275 | }) |
| 310 | | std::ranges::to<std::vector<relevant_road_closure>>(), | 276 | | std::ranges::to<std::vector<relevant_road_closure>>(), |