diff options
| author | Rutger Broekhoff | 2026-08-30 14:52:18 +0200 |
|---|---|---|
| committer | Rutger Broekhoff | 2026-08-30 14:52:18 +0200 |
| commit | d6d21a9c7c5acbe5b37daa9f6307d8498bc1a97b (patch) | |
| tree | e516a61db2957e73908fad9bbd1f707883c43348 /server/src | |
| parent | 11e9e77cf822c5eb1563b854a15ad61c55f0ff45 (diff) | |
| download | routemon-d6d21a9c7c5acbe5b37daa9f6307d8498bc1a97b.tar.gz routemon-d6d21a9c7c5acbe5b37daa9f6307d8498bc1a97b.zip | |
Buffer situation line segments when indexing
Diffstat (limited to 'server/src')
| -rw-r--r-- | server/src/api.cpp | 93 | ||||
| -rw-r--r-- | server/src/api.cppm | 5 | ||||
| -rw-r--r-- | server/src/geo.cppm | 2 | ||||
| -rw-r--r-- | server/src/main.cpp | 2 |
4 files changed, 65 insertions, 37 deletions
diff --git a/server/src/api.cpp b/server/src/api.cpp index 18fbc91..9953675 100644 --- a/server/src/api.cpp +++ b/server/src/api.cpp | |||
| @@ -100,9 +100,36 @@ auto tag_invoke(json::value_from_tag, json::value& jv, sysinfo const& info) | |||
| 100 | handler::handler(log::logger const& l, datex2::situation_publication pub) | 100 | handler::handler(log::logger const& l, datex2::situation_publication pub) |
| 101 | : l_{l.sub("handler")}, pub_{std::move(pub)} | 101 | : l_{l.sub("handler")}, pub_{std::move(pub)} |
| 102 | { | 102 | { |
| 103 | l_.info("Building indices"); | 103 | auto const nsit = pub_.situations.size(); |
| 104 | auto progress_percent = | ||
| 105 | [nsit](decltype(pub_.situations)::difference_type i) -> int | ||
| 106 | { | ||
| 107 | return static_cast<int>( | ||
| 108 | static_cast<float>(i) / static_cast<float>(nsit) * 100.0f); | ||
| 109 | }; | ||
| 110 | |||
| 111 | l_.info("Building indices 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 | |||
| 130 | l_.info("Progress: 0%"); | ||
| 104 | auto const before_build = chrono::steady_clock::now(); | 131 | auto const before_build = chrono::steady_clock::now(); |
| 105 | for (auto const& sit : pub_.situations) | 132 | for (auto const& [i, sit] : pub_.situations | std::views::enumerate) |
| 106 | { | 133 | { |
| 107 | for (auto const& rc : sit->road_closures) | 134 | for (auto const& rc : sit->road_closures) |
| 108 | { | 135 | { |
| @@ -110,18 +137,30 @@ handler::handler(log::logger const& l, datex2::situation_publication pub) | |||
| 110 | { | 137 | { |
| 111 | auto box = geo::box{}; | 138 | auto box = geo::box{}; |
| 112 | bgeo::envelope(*ls, box); | 139 | bgeo::envelope(*ls, box); |
| 113 | lse_index_.insert(std::make_tuple(box, ls, rc)); | 140 | auto buffered_ls = geo::multi_polygon{}; |
| 141 | bgeo::buffer( | ||
| 142 | *ls, buffered_ls, distance_strategy, side_strategy, join_strategy, | ||
| 143 | end_strategy, circle_strategy); | ||
| 144 | lse_index_.insert(std::make_tuple(box, buffered_ls, rc)); | ||
| 114 | } | 145 | } |
| 115 | for (auto p : rc->relevant_points) | 146 | for (auto p : rc->relevant_points) |
| 116 | { | 147 | { |
| 117 | p_index_.insert(std::make_pair(p, rc)); | 148 | p_index_.insert(std::make_pair(p, rc)); |
| 118 | } | 149 | } |
| 119 | } | 150 | } |
| 151 | |||
| 152 | auto prev_prog = progress_percent(i); | ||
| 153 | auto cur_prog = progress_percent(i + 1); | ||
| 154 | if (cur_prog > prev_prog && cur_prog % 5 == 0 && cur_prog != 100) | ||
| 155 | { | ||
| 156 | l_.info("Progress: {}%", cur_prog); | ||
| 157 | } | ||
| 120 | } | 158 | } |
| 159 | l_.info("Progress: 100%"); | ||
| 160 | |||
| 121 | auto const after_build = chrono::steady_clock::now(); | 161 | auto const after_build = chrono::steady_clock::now(); |
| 122 | auto const dur_build = | 162 | auto const dur_build_s = chrono::duration<double>{after_build - before_build}; |
| 123 | chrono::duration_cast<chrono::milliseconds>(after_build - before_build); | 163 | l_.info("Indices built in {}", dur_build_s); |
| 124 | l_.info("Indices built in {}", dur_build); | ||
| 125 | l_.info("LSE index size: {}", lse_index_.size()); | 164 | l_.info("LSE index size: {}", lse_index_.size()); |
| 126 | l_.info("Point index size: {}", p_index_.size()); | 165 | l_.info("Point index size: {}", p_index_.size()); |
| 127 | } | 166 | } |
| @@ -129,8 +168,6 @@ handler::handler(log::logger const& l, datex2::situation_publication pub) | |||
| 129 | auto handler::process_gpx(gpx::file&& gpx_file) | 168 | auto handler::process_gpx(gpx::file&& gpx_file) |
| 130 | -> std::optional<process_gpx_result> | 169 | -> std::optional<process_gpx_result> |
| 131 | { | 170 | { |
| 132 | const auto min_distance = 5.0; | ||
| 133 | |||
| 134 | auto const now = chrono::utc_clock::now(); | 171 | auto const now = chrono::utc_clock::now(); |
| 135 | auto const relevant = std::initializer_list<time::period>{ | 172 | auto const relevant = std::initializer_list<time::period>{ |
| 136 | time::period{now - chrono::days(7), now + chrono::days(7)} | 173 | time::period{now - chrono::days(7), now + chrono::days(7)} |
| @@ -149,26 +186,6 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 149 | 186 | ||
| 150 | auto vincenty_strategy = geo::vincenty_strategy{}; | 187 | auto vincenty_strategy = geo::vincenty_strategy{}; |
| 151 | 188 | ||
| 152 | const auto buffer_distance = min_distance; | ||
| 153 | const auto points_per_circle = 8; | ||
| 154 | // Note: thomas strategy does not work for geographic_join_round; | ||
| 155 | // need to use andoyer for that. | ||
| 156 | using formula = bgeo::strategy::thomas; | ||
| 157 | bgeo::strategy::buffer::distance_symmetric<double> distance_strategy{ | ||
| 158 | buffer_distance | ||
| 159 | }; | ||
| 160 | bgeo::strategy::buffer::geographic_join_miter<formula> join_strategy{ | ||
| 161 | buffer_distance | ||
| 162 | }; | ||
| 163 | bgeo::strategy::buffer::geographic_end_round<formula> end_strategy{4}; | ||
| 164 | bgeo::strategy::buffer::geographic_point_circle<formula> circle_strategy{ | ||
| 165 | points_per_circle | ||
| 166 | }; | ||
| 167 | bgeo::strategy::buffer::geographic_side_straight<formula> side_strategy; | ||
| 168 | |||
| 169 | using polygon = bgeo::model::polygon<geo::point>; | ||
| 170 | auto buffered_ls = bgeo::model::multi_polygon<polygon>{}; | ||
| 171 | |||
| 172 | l_.debug("Querying for relevant situations"); | 189 | l_.debug("Querying for relevant situations"); |
| 173 | auto relevant_road_closures = | 190 | auto relevant_road_closures = |
| 174 | std::unordered_set<std::shared_ptr<datex2::road_closure>>{}; | 191 | std::unordered_set<std::shared_ptr<datex2::road_closure>>{}; |
| @@ -179,7 +196,7 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 179 | { | 196 | { |
| 180 | l_.debug("Checking part [{}/{}]", ++i, splits_with_overlap_segments.size()); | 197 | l_.debug("Checking part [{}/{}]", ++i, splits_with_overlap_segments.size()); |
| 181 | 198 | ||
| 182 | // TODO: consider buffering with min_distance | 199 | // TODO: consider buffering with min_distance_ |
| 183 | auto part_box = geo::box{}; | 200 | auto part_box = geo::box{}; |
| 184 | bgeo::envelope(part, part_box); | 201 | bgeo::envelope(part, part_box); |
| 185 | 202 | ||
| @@ -193,16 +210,24 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 193 | // boost::geometry::model::linestring<geo::point> is part of the | 210 | // boost::geometry::model::linestring<geo::point> is part of the |
| 194 | // whole tuple type (lse_index_value) that is the value_type of the | 211 | // whole tuple type (lse_index_value) that is the value_type of the |
| 195 | // iterator. | 212 | // iterator. |
| 196 | std::shared_ptr<geo::linestring> const& ls = std::get<1>(*it); | 213 | |
| 214 | // geo::box const& lse = std::get<0>(*it); | ||
| 215 | geo::multi_polygon const& buffered_ls = std::get<1>(*it); | ||
| 197 | std::shared_ptr<datex2::road_closure> const& rc = std::get<2>(*it); | 216 | std::shared_ptr<datex2::road_closure> const& rc = std::get<2>(*it); |
| 198 | if (rc->validity | 217 | if (rc->validity |
| 199 | && rc->validity->intersect(check_periods).periods().empty()) | 218 | && rc->validity->intersect(check_periods).periods().empty()) |
| 200 | continue; | 219 | continue; |
| 201 | bgeo::buffer( | 220 | |
| 202 | *ls, buffered_ls, distance_strategy, side_strategy, join_strategy, | 221 | // auto small_parts = bgeo::model::multi_linestring<geo::linestring>{}; |
| 203 | end_strategy, circle_strategy); | 222 | // bgeo::intersection(part, lse, small_parts); |
| 223 | |||
| 204 | if (bgeo::intersects(buffered_ls, part)) | 224 | if (bgeo::intersects(buffered_ls, part)) |
| 205 | relevant_road_closures.emplace(rc); | 225 | relevant_road_closures.emplace(rc); |
| 226 | |||
| 227 | // if (!bgeo::is_empty(small_parts)) | ||
| 228 | // if (bgeo::distance(*ls, small_parts, vincenty_strategy) < | ||
| 229 | // min_distance_) | ||
| 230 | // relevant_road_closures.emplace(rc); | ||
| 206 | ls_checked++; | 231 | ls_checked++; |
| 207 | } | 232 | } |
| 208 | for (auto it = p_index_.qbegin(bgeo::index::intersects(part_box)); | 233 | for (auto it = p_index_.qbegin(bgeo::index::intersects(part_box)); |
| @@ -214,7 +239,7 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 214 | if (rc->validity | 239 | if (rc->validity |
| 215 | && rc->validity->intersect(check_periods).periods().empty()) | 240 | && rc->validity->intersect(check_periods).periods().empty()) |
| 216 | continue; | 241 | continue; |
| 217 | if (bgeo::distance(p, part, vincenty_strategy) < min_distance) | 242 | if (bgeo::distance(p, part, vincenty_strategy) < min_distance_) |
| 218 | relevant_road_closures.emplace(rc); | 243 | relevant_road_closures.emplace(rc); |
| 219 | p_checked++; | 244 | p_checked++; |
| 220 | } | 245 | } |
diff --git a/server/src/api.cppm b/server/src/api.cppm index 8f639fd..6f301c4 100644 --- a/server/src/api.cppm +++ b/server/src/api.cppm | |||
| @@ -77,9 +77,10 @@ auto tag_invoke(json::value_from_tag, json::value& jv, sysinfo const& info) | |||
| 77 | 77 | ||
| 78 | class handler | 78 | class handler |
| 79 | { | 79 | { |
| 80 | constexpr static auto const min_distance_ = 5.0; | ||
| 81 | |||
| 80 | using lse_index_value = std::tuple< | 82 | using lse_index_value = std::tuple< |
| 81 | geo::box, std::shared_ptr<geo::linestring>, | 83 | geo::box, geo::multi_polygon, std::shared_ptr<datex2::road_closure> |
| 82 | std::shared_ptr<datex2::road_closure> | ||
| 83 | >; | 84 | >; |
| 84 | using p_index_value = | 85 | using p_index_value = |
| 85 | std::pair<geo::point, std::shared_ptr<datex2::road_closure>>; | 86 | std::pair<geo::point, std::shared_ptr<datex2::road_closure>>; |
diff --git a/server/src/geo.cppm b/server/src/geo.cppm index ae0d3cc..b3513c8 100644 --- a/server/src/geo.cppm +++ b/server/src/geo.cppm | |||
| @@ -11,6 +11,8 @@ export namespace routemon::geo { | |||
| 11 | using point = bgeo::model::point<double, 2, bgeo::cs::geographic<bgeo::degree>>; | 11 | using point = bgeo::model::point<double, 2, bgeo::cs::geographic<bgeo::degree>>; |
| 12 | using linestring = bgeo::model::linestring<point>; | 12 | using linestring = bgeo::model::linestring<point>; |
| 13 | using box = bgeo::model::box<point>; | 13 | using box = bgeo::model::box<point>; |
| 14 | using polygon = bgeo::model::polygon<geo::point>; | ||
| 15 | using multi_polygon = bgeo::model::multi_polygon<polygon>; | ||
| 14 | using stype = bgeo::srs::spheroid<double>; | 16 | using stype = bgeo::srs::spheroid<double>; |
| 15 | using vincenty_strategy = bgeo::strategy::distance::vincenty<stype>; | 17 | using vincenty_strategy = bgeo::strategy::distance::vincenty<stype>; |
| 16 | 18 | ||
diff --git a/server/src/main.cpp b/server/src/main.cpp index b0a9938..99f19da 100644 --- a/server/src/main.cpp +++ b/server/src/main.cpp | |||
| @@ -84,7 +84,7 @@ auto real_main(std::span<char const*> args) -> exit_status | |||
| 84 | { | 84 | { |
| 85 | auto const& warns = d2loader.warnings(); | 85 | auto const& warns = d2loader.warnings(); |
| 86 | l.warn( | 86 | l.warn( |
| 87 | "Encountered {} unique warnings while loading DATEX II situations " | 87 | "Encountered {} unique warning(s) while loading DATEX II situations " |
| 88 | "publication", | 88 | "publication", |
| 89 | warns.size()); | 89 | warns.size()); |
| 90 | auto i = 0uz; | 90 | auto i = 0uz; |