Skip to content

Commit 99f0543

Browse files
authored
Split shape storage (timetable + routed) (#321)
* split shape storage (timetable + routed) * formatting * fix assert
1 parent d0fb57c commit 99f0543

10 files changed

Lines changed: 125 additions & 59 deletions

File tree

include/nigiri/loader/gtfs/shape.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ using relative_shape_idx_t =
1414
cista::strong<std::uint32_t, struct relative_shape_idx_>;
1515

1616
struct shape_loader_state {
17+
scoped_shape_idx_t get_scoped_shape_idx(relative_shape_idx_t const i) const {
18+
return to_scoped_shape_idx(get_shape_idx(i), shape_source::kTimetable);
19+
}
1720
shape_idx_t get_shape_idx(relative_shape_idx_t const i) const {
1821
return shape_idx_t{to_idx(i) + index_offset_};
1922
}
@@ -28,4 +31,4 @@ struct shape_loader_state {
2831

2932
shape_loader_state parse_shapes(std::string_view const, shapes_storage&);
3033

31-
} // namespace nigiri::loader::gtfs
34+
} // namespace nigiri::loader::gtfs

include/nigiri/shapes_storage.h

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,25 @@ namespace nigiri {
1717
struct shapes_storage {
1818
shapes_storage(std::filesystem::path,
1919
cista::mmap::protection,
20-
bool keep_shape_data = false);
20+
bool keep_routed_shape_data = false);
2121

2222
cista::mmap mm(char const* file, bool keep = false);
2323

24-
std::span<geo::latlng const> get_shape(shape_idx_t) const;
24+
std::span<geo::latlng const> get_shape(scoped_shape_idx_t) const;
2525
std::span<geo::latlng const> get_shape(trip_idx_t) const;
2626
std::span<geo::latlng const> get_shape(trip_idx_t,
2727
interval<stop_idx_t> const&) const;
2828

29-
std::pair<std::span<geo::latlng const>, shape_idx_t> get_shape_with_idx(
30-
trip_idx_t) const;
31-
std::pair<std::span<geo::latlng const>, shape_idx_t> get_shape_with_idx(
32-
trip_idx_t, interval<stop_idx_t> const&) const;
29+
std::pair<std::span<geo::latlng const>, scoped_shape_idx_t>
30+
get_shape_with_idx(trip_idx_t) const;
31+
std::pair<std::span<geo::latlng const>, scoped_shape_idx_t>
32+
get_shape_with_idx(trip_idx_t, interval<stop_idx_t> const&) const;
3333

34-
shape_idx_t get_shape_idx(trip_idx_t) const;
35-
shape_source get_shape_source(shape_idx_t) const;
34+
scoped_shape_idx_t get_shape_idx(trip_idx_t) const;
3635

3736
shape_offset_idx_t add_offsets(std::vector<shape_offset_t> const&);
3837
void add_trip_shape_offsets(
39-
trip_idx_t, cista::pair<shape_idx_t, shape_offset_idx_t> const&);
38+
trip_idx_t, cista::pair<scoped_shape_idx_t, shape_offset_idx_t> const&);
4039

4140
geo::box get_bounding_box(route_idx_t) const;
4241
std::optional<geo::box> get_bounding_box(route_idx_t,
@@ -46,12 +45,12 @@ struct shapes_storage {
4645
std::filesystem::path p_;
4746

4847
mm_paged_vecvec<shape_idx_t, geo::latlng> data_;
48+
mm_paged_vecvec<shape_idx_t, geo::latlng> routed_data_;
4949
mm_vecvec<shape_offset_idx_t, shape_offset_t, std::uint64_t> offsets_;
50-
mm_vec_map<trip_idx_t, cista::pair<shape_idx_t, shape_offset_idx_t>>
50+
mm_vec_map<trip_idx_t, cista::pair<scoped_shape_idx_t, shape_offset_idx_t>>
5151
trip_offset_indices_;
5252
mm_vec_map<route_idx_t, geo::box> route_bboxes_;
5353
mm_vecvec<route_idx_t, geo::box, std::uint64_t> route_segment_bboxes_;
54-
mm_vec_map<shape_idx_t, shape_source> shape_sources_;
5554
};
5655

5756
} // namespace nigiri

include/nigiri/types.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <cassert>
34
#include <chrono>
45
#include <cinttypes>
56
#include <variant>
@@ -169,6 +170,8 @@ using route_idx_t = cista::strong<std::uint32_t, struct _route_idx>;
169170
using section_idx_t = cista::strong<std::uint32_t, struct _section_idx>;
170171
using section_db_idx_t = cista::strong<std::uint32_t, struct _section_db_idx>;
171172
using shape_idx_t = cista::strong<std::uint32_t, struct _shape_idx>;
173+
using scoped_shape_idx_t =
174+
cista::strong<std::uint32_t, struct _scoped_shape_idx>;
172175
using shape_offset_t = cista::strong<std::uint32_t, struct _shape_offset>;
173176
using shape_offset_idx_t =
174177
cista::strong<std::uint32_t, struct _shape_offset_idx>;
@@ -459,6 +462,35 @@ enum class shape_source : std::uint8_t {
459462
kRouted = 2
460463
};
461464

465+
constexpr auto const kShapeSourceBit = std::uint32_t{1U} << 31U;
466+
constexpr auto const kShapeIndexMask = kShapeSourceBit - 1U;
467+
468+
inline shape_source get_shape_source(scoped_shape_idx_t const idx) {
469+
if (idx == scoped_shape_idx_t::invalid()) {
470+
return shape_source::kNone;
471+
}
472+
return (to_idx(idx) & kShapeSourceBit) != 0U ? shape_source::kRouted
473+
: shape_source::kTimetable;
474+
}
475+
476+
inline shape_idx_t get_local_shape_idx(scoped_shape_idx_t const idx) {
477+
return idx == scoped_shape_idx_t::invalid()
478+
? shape_idx_t::invalid()
479+
: shape_idx_t{to_idx(idx) & kShapeIndexMask};
480+
}
481+
482+
inline scoped_shape_idx_t to_scoped_shape_idx(shape_idx_t const local_idx,
483+
shape_source const source) {
484+
if (local_idx == shape_idx_t::invalid() || source == shape_source::kNone) {
485+
return scoped_shape_idx_t::invalid();
486+
}
487+
auto const local = to_idx(local_idx);
488+
assert((local & kShapeSourceBit) == 0U);
489+
return scoped_shape_idx_t{
490+
local |
491+
(source == shape_source::kRouted ? kShapeSourceBit : std::uint32_t{0U})};
492+
}
493+
462494
} // namespace nigiri
463495

464496
#include <iomanip>

src/loader/gtfs/shape.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ shape_loader_state parse_shapes(std::string_view const data,
4444
shapes.emplace_back_empty();
4545
states.distances_.emplace_back();
4646
seq.emplace_back();
47-
shapes_data.shape_sources_.emplace_back(shape_source::kTimetable);
4847
return idx;
4948
});
5049
auto polyline = shapes[shape_idx];

src/loader/gtfs/shape_prepare.cc

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ void process_task(timetable const& tt,
164164
shape_loader_state const& shape_states,
165165
relative_shape_idx_t const i,
166166
task& t) {
167-
auto const shape = shapes_data.get_shape(shape_states.get_shape_idx(i));
167+
auto const shape =
168+
shapes_data.get_shape(shape_states.get_scoped_shape_idx(i));
168169
auto const& shape_distances = shape_states.distances_[i];
169170
for (auto& x : t) {
170171
auto& r = x.result_;
@@ -228,7 +229,8 @@ void assign_shape_offsets(shapes_storage& shapes_data,
228229
auto const shape_idx = trip.shape_idx_;
229230
if (shape_idx == shape_idx_t::invalid()) {
230231
shapes_data.add_trip_shape_offsets(
231-
trip_idx, cista::pair{shape_idx, shape_offset_idx_t::invalid()});
232+
trip_idx, cista::pair{scoped_shape_idx_t::invalid(),
233+
shape_offset_idx_t::invalid()});
232234
} else {
233235
auto const task = tasks[states.get_relative_idx(shape_idx)];
234236
auto const x = std::ranges::lower_bound(
@@ -238,11 +240,13 @@ void assign_shape_offsets(shapes_storage& shapes_data,
238240
if (x != end(task) &&
239241
x->result_.shape_offset_idx_ != shape_offset_idx_t::invalid()) {
240242
shapes_data.add_trip_shape_offsets(
241-
trip_idx, cista::pair{shape_idx, x->result_.shape_offset_idx_});
243+
trip_idx, cista::pair{to_scoped_shape_idx(shape_idx,
244+
shape_source::kTimetable),
245+
x->result_.shape_offset_idx_});
242246
} else {
243247
shapes_data.add_trip_shape_offsets(
244-
trip_idx,
245-
cista::pair{shape_idx_t::invalid(), shape_offset_idx_t::invalid()});
248+
trip_idx, cista::pair{scoped_shape_idx_t::invalid(),
249+
shape_offset_idx_t::invalid()});
246250
}
247251
}
248252
}
@@ -283,12 +287,14 @@ void assign_bounding_boxes(timetable const& tt,
283287
.rt_ = rt_transport_idx_t::invalid()}};
284288
frun.for_each_trip([&](trip_idx_t const trip_idx,
285289
interval<stop_idx_t> const absolute_range) {
286-
auto const [shape_idx, offset_idx] =
290+
auto const [scoped_shape_idx, offset_idx] =
287291
shapes_data.trip_offset_indices_[trip_idx];
288-
if (shape_idx == shape_idx_t::invalid() ||
292+
if (scoped_shape_idx == scoped_shape_idx_t::invalid() ||
293+
get_shape_source(scoped_shape_idx) != shape_source::kTimetable ||
289294
offset_idx == shape_offset_idx_t::invalid()) {
290295
return;
291296
}
297+
auto const shape_idx = get_local_shape_idx(scoped_shape_idx);
292298
auto const& task = tasks[shape_states.get_relative_idx(shape_idx)];
293299
auto const it = utl::find_if(task, [&](stop_seq_dist const& s) {
294300
return s.result_.shape_offset_idx_ == offset_idx;

src/loader/netex/load_timetable.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,8 +1450,8 @@ void load_timetable(loader_config const& config,
14501450
tt.trip_stop_seq_numbers_.add_back_sized(0U);
14511451
if (shapes_data != nullptr) {
14521452
shapes_data->add_trip_shape_offsets(
1453-
trip_idx,
1454-
cista::pair{shape_idx_t::invalid(), shape_offset_idx_t::invalid()});
1453+
trip_idx, cista::pair{scoped_shape_idx_t::invalid(),
1454+
shape_offset_idx_t::invalid()});
14551455
}
14561456

14571457
auto const all_destinations_equal = utl::all_of(

src/rt/frun.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ void frun::for_each_shape_point(
661661
auto const [shape, shape_idx] = shapes_data->get_shape_with_idx(
662662
trip_idx, absolute_range << absolute_trip_offset);
663663
if (!shape.empty()) {
664-
current_shape_source = shapes_data->shape_sources_[shape_idx];
664+
current_shape_source = get_shape_source(shape_idx);
665665
return shape;
666666
}
667667
}

src/shapes_storage.cc

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,28 @@ namespace nigiri {
1717

1818
shapes_storage::shapes_storage(std::filesystem::path path,
1919
cista::mmap::protection const mode,
20-
bool keep_shape_data)
20+
bool const keep_routed_shape_data)
2121
: mode_{mode},
2222
p_{[&]() {
2323
fs::create_directories(path);
2424
return std::move(path);
2525
}()},
2626
data_{mm_paged_vecvec_helper<shape_idx_t, geo::latlng>::data_t{
27-
mm_vec<geo::latlng>{mm("shapes_data.bin", keep_shape_data)}},
27+
mm_vec<geo::latlng>{mm("shapes_data.bin")}},
2828
mm_vec<cista::page<std::uint64_t, std::uint32_t>>{
29-
mm("shapes_idx.bin", keep_shape_data)}},
29+
mm("shapes_idx.bin")}},
30+
routed_data_{mm_paged_vecvec_helper<shape_idx_t, geo::latlng>::data_t{
31+
mm_vec<geo::latlng>{mm("routed_shapes_data.bin",
32+
keep_routed_shape_data)}},
33+
mm_vec<cista::page<std::uint64_t, std::uint32_t>>{
34+
mm("routed_shapes_idx.bin", keep_routed_shape_data)}},
3035
offsets_{mm_vec<shape_offset_t>{mm("shape_offsets_data.bin")},
3136
mm_vec<std::uint64_t>{mm("shape_offsets_idx.bin")}},
3237
trip_offset_indices_{mm("shape_trip_offsets.bin")},
3338
route_bboxes_{mm("shape_route_bboxes.bin")},
3439
route_segment_bboxes_{
3540
mm_vec<geo::box>{mm("shape_route_segment_bboxes_data.bin")},
36-
mm_vec<std::uint64_t>{mm("shape_route_segment_bboxes_idx.bin")}},
37-
shape_sources_{mm("shape_sources.bin", keep_shape_data)} {}
41+
mm_vec<std::uint64_t>{mm("shape_route_segment_bboxes_idx.bin")}} {}
3842

3943
cista::mmap shapes_storage::mm(char const* file, bool const keep) {
4044
auto const p = (p_ / file);
@@ -45,31 +49,50 @@ cista::mmap shapes_storage::mm(char const* file, bool const keep) {
4549
: mode_};
4650
}
4751

48-
std::tuple<std::span<geo::latlng const>, shape_idx_t, shape_offset_idx_t>
52+
std::tuple<std::span<geo::latlng const>, scoped_shape_idx_t, shape_offset_idx_t>
4953
get_shape(shapes_storage const& storage, trip_idx_t const trip_idx) {
5054
if (trip_idx == trip_idx_t::invalid() ||
5155
trip_idx >= storage.trip_offset_indices_.size()) {
5256
return {};
5357
}
5458
auto const [shape_idx, offset_idx] = storage.trip_offset_indices_[trip_idx];
55-
assert((shape_idx == shape_idx_t::invalid()) ==
59+
assert((shape_idx == scoped_shape_idx_t::invalid()) ==
5660
(offset_idx == shape_offset_idx_t::invalid()));
5761
if (offset_idx == shape_offset_idx_t::invalid()) {
5862
return {};
5963
}
6064
return std::tuple{storage.get_shape(shape_idx), shape_idx, offset_idx};
6165
}
6266

63-
std::span<geo::latlng const> shapes_storage::get_shape(
64-
shape_idx_t const shape_idx) const {
67+
std::span<geo::latlng const> get_shape_from_data(
68+
mm_paged_vecvec<shape_idx_t, geo::latlng> const& data,
69+
shape_idx_t const shape_idx) {
6570
if (shape_idx == shape_idx_t::invalid() ||
66-
static_cast<std::size_t>(to_idx(shape_idx)) > data_.size()) {
71+
static_cast<std::size_t>(to_idx(shape_idx)) >= data.size()) {
6772
return {};
6873
}
69-
auto const shape = data_[shape_idx];
74+
auto const shape = data[shape_idx];
7075
return {begin(shape), end(shape)};
7176
}
7277

78+
std::span<geo::latlng const> shapes_storage::get_shape(
79+
scoped_shape_idx_t const shape_idx) const {
80+
if (shape_idx == scoped_shape_idx_t::invalid()) {
81+
return {};
82+
}
83+
84+
auto const local_shape_idx = get_local_shape_idx(shape_idx);
85+
switch (get_shape_source(shape_idx)) {
86+
case shape_source::kTimetable:
87+
return get_shape_from_data(data_, local_shape_idx);
88+
case shape_source::kRouted:
89+
return get_shape_from_data(routed_data_, local_shape_idx);
90+
case shape_source::kNone: return {};
91+
}
92+
93+
return {};
94+
}
95+
7396
std::span<geo::latlng const> shapes_storage::get_shape(
7497
trip_idx_t const trip_idx) const {
7598
auto const [shape, _shape_idx, _offset_idx] =
@@ -98,24 +121,25 @@ std::span<geo::latlng const> shapes_storage::get_shape(
98121
return get_subshape(*this, shape, offset_idx, range);
99122
}
100123

101-
std::pair<std::span<geo::latlng const>, shape_idx_t>
124+
std::pair<std::span<geo::latlng const>, scoped_shape_idx_t>
102125
shapes_storage::get_shape_with_idx(trip_idx_t const trip_idx) const {
103126
auto const [shape, shape_idx, _] = nigiri::get_shape(*this, trip_idx);
104127
return std::pair{shape, shape_idx};
105128
}
106129

107-
std::pair<std::span<geo::latlng const>, shape_idx_t>
130+
std::pair<std::span<geo::latlng const>, scoped_shape_idx_t>
108131
shapes_storage::get_shape_with_idx(trip_idx_t const trip_idx,
109132
interval<stop_idx_t> const& range) const {
110133
auto const [shape, shape_idx, offset_idx] =
111134
nigiri::get_shape(*this, trip_idx);
112135
if (shape.empty()) {
113-
return std::pair{shape, shape_idx_t::invalid()};
136+
return std::pair{shape, scoped_shape_idx_t::invalid()};
114137
}
115138
return std::pair{get_subshape(*this, shape, offset_idx, range), shape_idx};
116139
}
117140

118-
shape_idx_t shapes_storage::get_shape_idx(trip_idx_t const trip_idx) const {
141+
scoped_shape_idx_t shapes_storage::get_shape_idx(
142+
trip_idx_t const trip_idx) const {
119143
if (trip_idx == trip_idx_t::invalid() ||
120144
trip_idx >= trip_offset_indices_.size()) {
121145
return {};
@@ -133,7 +157,7 @@ shape_offset_idx_t shapes_storage::add_offsets(
133157

134158
void shapes_storage::add_trip_shape_offsets(
135159
[[maybe_unused]] trip_idx_t const trip_idx,
136-
cista::pair<shape_idx_t, shape_offset_idx_t> const& offset_idx) {
160+
cista::pair<scoped_shape_idx_t, shape_offset_idx_t> const& offset_idx) {
137161
assert(trip_idx == trip_offset_indices_.size());
138162
trip_offset_indices_.emplace_back(offset_idx);
139163
}
@@ -155,13 +179,4 @@ std::optional<geo::box> shapes_storage::get_bounding_box(
155179
: std::optional<geo::box>{std::nullopt};
156180
}
157181

158-
shape_source shapes_storage::get_shape_source(
159-
shape_idx_t const shape_idx) const {
160-
if (shape_idx == shape_idx_t::invalid() ||
161-
static_cast<std::size_t>(to_idx(shape_idx)) >= shape_sources_.size()) {
162-
return shape_source::kNone;
163-
}
164-
return shape_sources_[shape_idx];
165-
}
166-
167182
} // namespace nigiri

test/loader/gtfs/shape_test.cc

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ TEST(gtfs, shape_get_existing_shape_points) {
4343
{51.543652, 7.217830},
4444
{51.478609, 7.223275},
4545
}),
46-
shapes_data.get_shape(shapes.at("243")));
46+
shapes_data.get_shape(to_scoped_shape_idx(
47+
shapes.at("243"), shape_source::kTimetable)));
4748

4849
EXPECT_EQ((geo::polyline{
4950
{50.553822, 6.356876},
@@ -54,7 +55,8 @@ TEST(gtfs, shape_get_existing_shape_points) {
5455
{50.578249, 6.383394},
5556
{50.581956, 6.379866},
5657
}),
57-
shapes_data.get_shape(shapes.at("3105")));
58+
shapes_data.get_shape((to_scoped_shape_idx(
59+
shapes.at("3105"), shape_source::kTimetable))));
5860
}
5961

6062
TEST(gtfs, shape_not_ascending_sequence) {
@@ -71,7 +73,8 @@ TEST(gtfs, shape_not_ascending_sequence) {
7173
auto const& shapes = shape_states.id_map_;
7274

7375
EXPECT_EQ((geo::polyline{{50.636259, 6.473668}, {50.636512, 6.473487}}),
74-
shapes_data.get_shape(shapes.at("1")));
76+
shapes_data.get_shape((to_scoped_shape_idx(
77+
shapes.at("1"), shape_source::kTimetable))));
7578
}
7679

7780
TEST(gtfs, shape_shuffled_rows) {
@@ -131,7 +134,8 @@ TEST(gtfs, shape_shuffled_rows) {
131134
}},
132135
};
133136
for (auto [id, polyline] : shape_points) {
134-
EXPECT_EQ(polyline, shapes_data.get_shape(shapes.at(id)));
137+
EXPECT_EQ(polyline, shapes_data.get_shape((to_scoped_shape_idx(
138+
shapes.at(id), shape_source::kTimetable))));
135139
}
136140
}
137141

@@ -152,5 +156,6 @@ TEST(gtfs, shape_delay_insert_no_ascending_sequence) {
152156
EXPECT_NE(shape_idx, end(shapes));
153157
EXPECT_NE(shapes.find("2"), end(shapes));
154158
EXPECT_EQ((geo::polyline{{50.636259, 6.473668}, {50.636512, 6.473487}}),
155-
shapes_data.get_shape(shape_idx->second));
156-
}
159+
shapes_data.get_shape((to_scoped_shape_idx(
160+
shape_idx->second, shape_source::kTimetable))));
161+
}

0 commit comments

Comments
 (0)