Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extends shapes to store bounding boxes #148

Merged
merged 62 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
1929425
Move bounding box calculation into nigiri
MichaelKutzner Oct 23, 2024
21c6bde
Make bounding boxes for segments optional
MichaelKutzner Oct 23, 2024
ceee6db
Fix duplicated entries
MichaelKutzner Oct 23, 2024
67b6bac
Make filtering more explicit
MichaelKutzner Oct 23, 2024
3fe6905
Delay value calculation until needed
MichaelKutzner Oct 23, 2024
a70323c
Simplify route iteration
MichaelKutzner Oct 23, 2024
1aa404a
Update geo dependency
MichaelKutzner Oct 23, 2024
2a9b6bd
Fix out of bounds shape offset
MichaelKutzner Oct 24, 2024
121b67d
Add test case for invalid offset
MichaelKutzner Oct 24, 2024
c195ca9
Improve comment
MichaelKutzner Oct 24, 2024
2923d91
Merge branch 'main' into bounding_boxes
MichaelKutzner Oct 31, 2024
6044729
Unify boxes size
MichaelKutzner Oct 31, 2024
2a34524
Store stops per shapes to process
MichaelKutzner Oct 31, 2024
9dfc2ed
Move code into struct
MichaelKutzner Oct 31, 2024
e8cc9a8
WIP: Perform parallel calculations
MichaelKutzner Nov 4, 2024
d38704c
Make methods const
MichaelKutzner Nov 4, 2024
8642d1b
Update progress bar
MichaelKutzner Nov 4, 2024
3d82cd7
Move nullptr check into struct
MichaelKutzner Nov 4, 2024
e704874
Check for execution policy support
MichaelKutzner Nov 4, 2024
7d81cb5
Remove unused code
MichaelKutzner Nov 4, 2024
46e1bc5
Merge branch 'main' into bounding_boxes
MichaelKutzner Nov 4, 2024
22ba329
Make nullopt more explicit
MichaelKutzner Nov 4, 2024
60dab28
Fix MSVC build
MichaelKutzner Nov 5, 2024
b09d4c0
Fix formatting
MichaelKutzner Nov 5, 2024
ec3bbdd
Fix formatting
MichaelKutzner Nov 5, 2024
eda90d5
Add test for routes containing sequential trips
MichaelKutzner Nov 5, 2024
339ba6f
Rename shape result objects
MichaelKutzner Nov 5, 2024
b1973f7
Make intended usage more obvious
MichaelKutzner Nov 5, 2024
a2c7d75
Move data on last usage
MichaelKutzner Nov 5, 2024
85d4638
Update variable names
MichaelKutzner Nov 5, 2024
c37e792
Renane variable
MichaelKutzner Nov 5, 2024
b6a1b97
Add missing const
MichaelKutzner Nov 5, 2024
289a000
Apply suggested newline
MichaelKutzner Nov 6, 2024
ecf11c6
Improve readability
MichaelKutzner Nov 6, 2024
38b55a6
Update parallel computations
MichaelKutzner Nov 6, 2024
55925f9
Improve method names
MichaelKutzner Nov 6, 2024
b3272d8
Split shape boxes into two files
MichaelKutzner Nov 6, 2024
e1e8578
Simplify parallel usage
MichaelKutzner Nov 6, 2024
53e9a9e
Rename variables
MichaelKutzner Nov 6, 2024
ec8f3ed
WIP: Change code to tasks
MichaelKutzner Nov 7, 2024
7234e20
Fix use after free
MichaelKutzner Nov 7, 2024
e1e003a
Create offset entries
MichaelKutzner Nov 7, 2024
ea7cddf
Create bounding boxes
MichaelKutzner Nov 7, 2024
20dc322
Remove mutex
MichaelKutzner Nov 7, 2024
ee37475
Delete old code
MichaelKutzner Nov 7, 2024
05ce7c7
Rename variables
MichaelKutzner Nov 7, 2024
d75fb44
Remove intermediate struct
MichaelKutzner Nov 7, 2024
ced2e8d
Fix compile issues
MichaelKutzner Nov 7, 2024
4f09b87
Fix invalid range
MichaelKutzner Nov 7, 2024
edbfeda
Fix empty bound boxes
MichaelKutzner Nov 8, 2024
16ed908
Fix empty bound boxes for sequential trips
MichaelKutzner Nov 8, 2024
dd18872
Reduce stored data for simple sequential trips
MichaelKutzner Nov 8, 2024
94b2af4
Fix formatting
MichaelKutzner Nov 8, 2024
1fac9fb
Fix size type
MichaelKutzner Nov 8, 2024
1437f02
Attempt to fix macos build
MichaelKutzner Nov 8, 2024
244a1c0
wip
felixguendling Nov 11, 2024
ce3dcc8
Apply pending changes
MichaelKutzner Nov 11, 2024
584318d
Delete old code
MichaelKutzner Nov 11, 2024
426dd8e
Remove unused includes
MichaelKutzner Nov 12, 2024
f39b5e4
Cleanup code
MichaelKutzner Nov 12, 2024
f5fdca7
Add missing check for end element
MichaelKutzner Nov 12, 2024
3d83c8e
Update progress status text
MichaelKutzner Nov 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/nigiri/loader/gtfs/shape_prepare.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace nigiri::loader::gtfs {
void calculate_shape_offsets_and_bboxes(
timetable const&,
shapes_storage&,
vector_map<gtfs_trip_idx_t, trip> const&,
shape_loader_state const&);
shape_loader_state const&,
vector_map<gtfs_trip_idx_t, trip> const&);

} // namespace nigiri::loader::gtfs
4 changes: 2 additions & 2 deletions src/loader/gtfs/load_timetable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,8 @@ void load_timetable(loader_config const& config,
}

if (shapes_data != nullptr) {
calculate_shape_offsets_and_bboxes(tt, *shapes_data, trip_data.data_,
shape_states);
calculate_shape_offsets_and_bboxes(tt, *shapes_data, shape_states,
trip_data.data_);
}

// Build location_routes map
Expand Down
13 changes: 7 additions & 6 deletions src/loader/gtfs/shape.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ shape_loader_state parse_shapes(std::string_view const data,
utl::csv_col<double, UTL_NAME("shape_dist_traveled")> distance_;
};

auto const index_offset = static_cast<shape_idx_t>(shapes.size());
auto const index_offset = static_cast<shape_idx_t::value_t>(shapes.size());
auto states = shape_loader_state{
.index_offset_ = index_offset,
};
auto lookup = cached_lookup(states.id_map_);
auto seq = std::vector<std::vector<std::uint32_t>>{};
auto seq = vector_map<relative_shape_idx_t, std::vector<std::uint32_t>>{};

auto const progress_tracker = utl::get_active_progress_tracker();
progress_tracker->status("Parse Shapes")
Expand All @@ -49,7 +49,7 @@ shape_loader_state parse_shapes(std::string_view const data,
});
auto polyline = shapes[shape_idx];
polyline.push_back(geo::latlng{*entry.lat_, *entry.lon_});
auto const state_idx = to_idx(shape_idx - index_offset);
auto const state_idx = states.get_relative_idx(shape_idx);
seq[state_idx].push_back(*entry.seq_);
auto& distances = states.distances_[state_idx];
if (distances.empty()) {
Expand All @@ -65,11 +65,13 @@ shape_loader_state parse_shapes(std::string_view const data,
});

auto polyline = std::vector<geo::latlng>();
auto shape_idx = states.index_offset_;
for (auto i = 0U; i != states.distances_.size(); ++i) {
for (auto const i :
interval{relative_shape_idx_t{0U},
relative_shape_idx_t{states.distances_.size()}}) {
if (utl::is_sorted(seq[i], std::less<>{})) {
continue;
}
auto const shape_idx = states.get_shape_idx(i);

polyline.resize(shapes[shape_idx].size());
for (auto j = 0U; j != shapes[shape_idx].size(); ++j) {
Expand All @@ -79,7 +81,6 @@ shape_loader_state parse_shapes(std::string_view const data,
std::tie(seq[i], states.distances_[i], polyline) =
utl::sort_by(seq[i], states.distances_[i], polyline);
std::copy(begin(polyline), end(polyline), begin(shapes[shape_idx]));
++shape_idx;
}

return states;
Expand Down
294 changes: 105 additions & 189 deletions src/loader/gtfs/shape_prepare.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "geo/polyline.h"

#include "utl/enumerate.h"
#include "utl/helpers/algorithm.h"
felixguendling marked this conversation as resolved.
Show resolved Hide resolved
#include "utl/insert_sorted.h"
#include "utl/pairwise.h"
#include "utl/parallel_for.h"
Expand Down Expand Up @@ -132,180 +133,6 @@ vector_map<relative_shape_idx_t, task> create_offset_tasks(
return tasks;
}

struct bbox_and_offset_data {
struct trip {
stop_seq_t const* stop_seq_;
shape_offset_idx_t shape_offset_idx_;
geo::box trip_bbox_;
std::vector<geo::box> segment_bboxes_;
};
std::vector<trip> trips_;
};

std::vector<bbox_and_offset_data> assign_shape_offsets(
shapes_storage& shapes_data,
vector_map<gtfs_trip_idx_t, trip> const& trips,
std::vector<offset_task>&& tasks,
shape_idx_t const index_offset) {
assert(utl::all_of(tasks, [](offset_task const& task) {
return task.results_ != std::nullopt;
}));
auto const entries =
utl::all(std::move(tasks)) //
| utl::transform([&](offset_task const& task) {
return bbox_and_offset_data{
.trips_ =
utl::all(*task.results_) //
| utl::transform([&](offset_task::result const& result) {
return bbox_and_offset_data::trip{
.stop_seq_ = result.stop_seq_,
.shape_offset_idx_ =
result.offsets_.empty()
? shape_offset_idx_t::invalid()
: shapes_data.add_offsets(result.offsets_),
.trip_bbox_ = result.trip_bbox_,
.segment_bboxes_ = result.segment_bboxes_,
};
}) //
| utl::vec(),
};
}) |
utl::vec();
for (auto const& trip : trips) {
auto const trip_idx = trip.trip_idx_;
auto const shape_idx = trip.shape_idx_;
if (shape_idx == shape_idx_t::invalid()) {
shapes_data.add_trip_shape_offsets(
trip_idx, cista::pair{shape_idx, shape_offset_idx_t::invalid()});
} else {
auto const& results =
entries[cista::to_idx(shape_idx - index_offset)].trips_;
auto const it = std::ranges::lower_bound(
results, trip.stop_seq_,
[&](stop_seq_t const& a, stop_seq_t const& b) { return a < b; },
[](bbox_and_offset_data::trip const& entry) {
return *entry.stop_seq_;
});
shapes_data.add_trip_shape_offsets(
trip_idx, cista::pair{shape_idx, it->shape_offset_idx_});
}
}
return entries;
}

struct bbox_task {
struct result {
geo::box route_bbox_;
std::vector<geo::box> route_segment_bboxes_;
};
std::function<std::optional<result>()> task_;
std::optional<result> result_;
};

std::vector<bbox_task> create_bbox_tasks(
timetable const& tt,
shapes_storage const& shapes_data,
std::vector<bbox_and_offset_data> const& bbox_data,
std::size_t const route_offset,
shape_idx_t const shape_offset) {
auto const new_routes =
interval{static_cast<route_idx_t>(route_offset),
static_cast<route_idx_t>(tt.route_transport_ranges_.size())};
return utl::all(new_routes) //
|
utl::transform([&](route_idx_t const& r) {
return bbox_task{
.task_ =
[&, r, shape_offset]() {
auto const seq = tt.route_location_seq_[r];
assert(seq.size() > 0U);
auto bounding_box = geo::box{};
auto segment_bboxes = std::vector<geo::box>{};
segment_bboxes.resize(seq.size() - 1);
auto bbox_count = static_cast<std::size_t>(0U);
auto const stop_indices = interval{
stop_idx_t{0U}, static_cast<stop_idx_t>(seq.size())};

for (auto const transport_idx :
tt.route_transport_ranges_[r]) {
auto const frun = rt::frun{
tt, nullptr,
rt::run{.t_ = transport{transport_idx},
.stop_range_ = stop_indices,
.rt_ = rt_transport_idx_t::invalid()}};
frun.for_each_trip([&](trip_idx_t const trip_idx,
interval<stop_idx_t> const
absolute_range) {
auto const [shape_idx, offset_idx] =
shapes_data.trip_offset_indices_[trip_idx];
if (shape_idx == shape_idx_t::invalid() ||
offset_idx == shape_offset_idx_t::invalid()) {
return;
}
auto const result = std::ranges::lower_bound(
bbox_data[cista::to_idx(shape_idx - shape_offset)]
.trips_,
offset_idx,
[](shape_offset_idx_t const a,
shape_offset_idx_t const b) { return a < b; },
[](bbox_and_offset_data::trip const& trip) {
return trip.shape_offset_idx_;
});
bounding_box.extend(result->trip_bbox_);
auto const& bboxes = result->segment_bboxes_;
if (bboxes.empty()) {
return;
}
for (auto const [i, bbox] : utl::enumerate(bboxes)) {
segment_bboxes[i + cista::to_idx(
absolute_range.from_)] = bbox;
}
bbox_count =
std::max(bbox_count,
static_cast<std::size_t>(
bboxes.size() +
cista::to_idx(absolute_range.from_)));
});
}
segment_bboxes.resize(bbox_count);
// Extend bounding boxes to contain all stops
for (auto const [i, segment] :
utl::enumerate(utl::pairwise(stop_indices))) {
auto const [from, to] = segment;
auto const from_l =
tt.locations_
.coordinates_[stop{seq[from]}.location_idx()];
auto const to_l =
tt.locations_
.coordinates_[stop{seq[to]}.location_idx()];
if (i == 0U) {
bounding_box.extend(from_l);
}
bounding_box.extend(to_l);
if (i < bbox_count) {
segment_bboxes[i].extend(from_l);
segment_bboxes[i].extend(to_l);
}
}

return std::make_optional(bbox_task::result{
std::move(bounding_box), std::move(segment_bboxes)});
},
.result_ = std::nullopt,
};
}) |
utl::vec();
}

void assign_bounding_boxes(shapes_storage& shapes_data,
std::vector<bbox_task> const&& tasks) {
for (auto const& task : tasks) {
shapes_data.route_bboxes_.emplace_back(task.result_->route_bbox_);
shapes_data.route_segment_bboxes_.emplace_back(
task.result_->route_segment_bboxes_);
}
}

void process_task(timetable const& tt,
shapes_storage const& shapes_data,
shape_loader_state const& shape_states,
Expand Down Expand Up @@ -358,34 +185,123 @@ void process_task(timetable const& tt,
}
}

void assign_shape_offsets(shapes_storage& shapes_data,
vector_map<gtfs_trip_idx_t, trip> const& trips,
vector_map<relative_shape_idx_t, task>& tasks,
shape_loader_state const& states) {
for (auto& shape_tasks : tasks) {
for (auto& task : shape_tasks) {
auto& r = task.result_;
if (!r.offsets_.empty()) {
r.shape_offset_idx_ = shapes_data.add_offsets(std::move(r.offsets_));
}
}
}
for (auto const& trip : trips) {
auto const trip_idx = trip.trip_idx_;
auto const shape_idx = trip.shape_idx_;
if (shape_idx == shape_idx_t::invalid()) {
shapes_data.add_trip_shape_offsets(
trip_idx, cista::pair{shape_idx, shape_offset_idx_t::invalid()});
} else {
auto const shape_tasks = tasks[states.get_relative_idx(shape_idx)];
auto const task = std::ranges::lower_bound(
shape_tasks, trip.stop_seq_,
[&](stop_seq_t const& a, stop_seq_t const& b) { return a < b; },
[](stop_seq_dist const& s) { return *s.stop_seq_; });
shapes_data.add_trip_shape_offsets(
trip_idx, cista::pair{shape_idx, task->result_.shape_offset_idx_});
}
}
}

void assign_bounding_boxes(timetable const& tt,
shapes_storage& shapes_data,
vector_map<relative_shape_idx_t, task>& tasks,
shape_loader_state const& shape_states) {
auto const new_routes =
interval{static_cast<route_idx_t>(shapes_data.route_bboxes_.size()),
static_cast<route_idx_t>(tt.route_transport_ranges_.size())};
for (auto const r : new_routes) {
auto const seq = tt.route_location_seq_[r];
assert(seq.size() > 0U);
auto bounding_box = geo::box{};
auto segment_bboxes = std::vector<geo::box>{};
segment_bboxes.resize(seq.size() - 1);
auto const stop_indices =
interval{stop_idx_t{0U}, static_cast<stop_idx_t>(seq.size())};

// Create basic bounding boxes, span by stops
for (auto const [i, s] : utl::enumerate(stop_indices)) {
auto const pos = tt.locations_.coordinates_[stop{seq[s]}.location_idx()];
bounding_box.extend(pos);
if (i > 0U) {
segment_bboxes[i - 1U].extend(pos);
}
if (i < segment_bboxes.size()) {
segment_bboxes[i].extend(pos);
}
}

auto is_trivial = true;

for (auto const transport_idx : tt.route_transport_ranges_[r]) {
auto const frun = rt::frun{tt, nullptr,
rt::run{.t_ = transport{transport_idx},
.stop_range_ = stop_indices,
.rt_ = rt_transport_idx_t::invalid()}};
frun.for_each_trip([&](trip_idx_t const trip_idx,
interval<stop_idx_t> const absolute_range) {
auto const [shape_idx, offset_idx] =
shapes_data.trip_offset_indices_[trip_idx];
if (shape_idx == shape_idx_t::invalid() ||
offset_idx == shape_offset_idx_t::invalid()) {
return;
}
auto const& shape_tasks =
tasks[shape_states.get_relative_idx(shape_idx)];
auto const it = utl::find_if(shape_tasks, [&](stop_seq_dist const& s) {
return s.result_.shape_offset_idx_ == offset_idx;
});

auto const& res = it->result_;
bounding_box.extend(res.trip_bbox_);
auto const& bboxes = res.segment_bboxes_;
if (!bboxes.empty()) {
for (auto const [i, bbox] : utl::enumerate(bboxes)) {
segment_bboxes[i + cista::to_idx(absolute_range.from_)].extend(
bbox);
}
is_trivial = false;
}
});
}

if (is_trivial) {
segment_bboxes.clear();
}

shapes_data.route_bboxes_.emplace_back(std::move(bounding_box));
shapes_data.route_segment_bboxes_.emplace_back(std::move(segment_bboxes));
}
}

void calculate_shape_offsets_and_bboxes(
timetable const& tt,
shapes_storage& shapes_data,
shape_loader_state const& shape_states,
vector_map<gtfs_trip_idx_t, trip> const& trips) {
utl::get_active_progress_tracker()
->status("Creating trip offsets")
.out_bounds(98.F, 99.F);
.out_bounds(98.F, 100.F);
auto tasks = create_offset_tasks(trips, shape_states);
utl::parallel_for_run(tasks.size(), [&](std::size_t const i) {
auto const s = relative_shape_idx_t{i};
process_task(tt, shapes_data, shape_states, s, tasks[s]);
});

auto bbox_data = assign_shape_offsets(
shapes_data, trips, std::move(offset_tasks), shape_states.index_offset_);

auto bbox_tasks = create_bbox_tasks(tt, shapes_data, bbox_data,
shapes_data.route_bboxes_.size(),
shape_states.index_offset_);
auto const bbox_progress_tracker = utl::get_active_progress_tracker();
bbox_progress_tracker->status("Creating bounding boxes")
.out_bounds(99.F, 100.F);
utl::parallel_for(bbox_tasks, [&bbox_progress_tracker](bbox_task& task) {
bbox_progress_tracker->increment();
task.result_ = task.task_();
});
assign_bounding_boxes(shapes_data, std::move(bbox_tasks));
assign_shape_offsets(shapes_data, trips, tasks, shape_states);
assign_bounding_boxes(tt, shapes_data, tasks, shape_states);
}

} // namespace nigiri::loader::gtfs
Loading