Skip to content

Commit c57d51b

Browse files
authored
Merge pull request #20 from opentraffic/max_length
Update OSMLR segment definitions to not exceed kMaximumLength (1km for now).
2 parents 29bcdba + cbf9b66 commit c57d51b

File tree

5 files changed

+406
-195
lines changed

5 files changed

+406
-195
lines changed

include/osmlr/output/geojson.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ struct geojson : public output {
1313
virtual ~geojson();
1414

1515
void add_path(const valhalla::baldr::merge::path &);
16+
void output_segment(const valhalla::baldr::merge::path &p);
17+
void output_segment(const std::vector<valhalla::midgard::PointLL>& shape,
18+
const valhalla::baldr::DirectedEdge* edge,
19+
const valhalla::baldr::GraphId& edgeid);
20+
void split_path(const valhalla::baldr::merge::path& p, const uint32_t total_length);
1621
void finish();
1722

1823
private:

include/osmlr/output/tiles.hpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,67 @@
77
namespace osmlr {
88
namespace output {
99

10+
enum class FormOfWay {
11+
kUndefined = 0,
12+
kMotorway = 1,
13+
kMultipleCarriageway = 2,
14+
kSingleCarriageway = 3,
15+
kRoundabout = 4,
16+
kTrafficSquare = 5,
17+
kSlipRoad = 6,
18+
kOther = 7
19+
};
20+
21+
struct lrp {
22+
bool at_node;
23+
const valhalla::midgard::PointLL coord;
24+
const uint16_t bear;
25+
const valhalla::baldr::RoadClass start_frc;
26+
const FormOfWay start_fow;
27+
const valhalla::baldr::RoadClass least_frc;
28+
const uint32_t length;
29+
30+
lrp(const bool at_node_,
31+
const valhalla::midgard::PointLL &coord_,
32+
uint16_t bear_,
33+
valhalla::baldr::RoadClass start_frc_,
34+
FormOfWay start_fow_,
35+
valhalla::baldr::RoadClass least_frc_,
36+
uint32_t length_)
37+
: at_node(at_node_)
38+
, coord(coord_)
39+
, bear(bear_)
40+
, start_frc(start_frc_)
41+
, start_fow(start_fow_)
42+
, least_frc(least_frc_)
43+
, length(length_)
44+
{}
45+
};
46+
1047
struct tiles : public output {
1148
tiles(valhalla::baldr::GraphReader &reader, std::string base_dir, size_t max_fds, uint32_t max_length = 15000);
1249
virtual ~tiles();
1350

14-
void add_path(const valhalla::baldr::merge::path &);
51+
void add_path(const valhalla::baldr::merge::path &p);
52+
void split_path(const valhalla::baldr::merge::path &p, const uint32_t total_length);
53+
void output_segment(const valhalla::baldr::merge::path &p);
54+
void output_segment(const std::vector<valhalla::midgard::PointLL>& shape,
55+
const valhalla::baldr::DirectedEdge* edge,
56+
const valhalla::baldr::GraphId& edgeid,
57+
const bool start_at_node, const bool end_at_node);
58+
void output_segment(std::vector<lrp>& lrps, const valhalla::baldr::GraphId& tile_id);
1559
void finish();
1660

1761
private:
1862
valhalla::baldr::GraphReader &m_reader;
1963
util::tile_writer m_writer;
2064
uint32_t m_max_length;
65+
66+
std::vector<lrp> build_segment_descriptor(const valhalla::baldr::merge::path &p);
67+
std::vector<lrp> build_segment_descriptor(const std::vector<valhalla::midgard::PointLL>& shape,
68+
const valhalla::baldr::DirectedEdge* edge,
69+
const bool start_at_node,
70+
const bool end_at_node);
2171
};
2272

2373
} // namespace output

proto

src/output/geojson.cpp

Lines changed: 158 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
#include "osmlr/output/geojson.hpp"
2+
#include <valhalla/midgard/util.h>
23
#include <stdexcept>
34

5+
namespace vm = valhalla::midgard;
46
namespace vb = valhalla::baldr;
57

68
namespace {
79

10+
// Minimum length for an OSMLR segment
11+
constexpr uint32_t kMinimumLength = 5;
12+
13+
// Maximum length for an OSMLR segment
14+
constexpr uint32_t kMaximumLength = 1000;
15+
816
const std::string k_geojson_header = "{\"type\":\"FeatureCollection\",\"features\":[";
917
const std::string k_geojson_footer = "]}";
1018

@@ -28,8 +36,99 @@ geojson::~geojson() {
2836
}
2937

3038
void geojson::add_path(const vb::merge::path &p) {
39+
// Get the length of the path
40+
uint32_t total_length = 0;
41+
for (auto edge_id : p.m_edges) {
42+
const auto *tile = m_reader.GetGraphTile(edge_id);
43+
const auto *edge = tile->directededge(edge_id);
44+
total_length += edge->length();
45+
}
46+
47+
// Skip very short segments that are only 1 edge
48+
if (total_length < kMinimumLength && p.m_edges.size() == 1) {
49+
return;
50+
}
51+
52+
// Split longer segments
53+
if (total_length < kMaximumLength) {
54+
output_segment(p);
55+
} else {
56+
split_path(p, total_length);
57+
}
58+
}
59+
60+
void geojson::split_path(const vb::merge::path& p, const uint32_t total_length) {
61+
// Walk the merged path and split where needed
62+
uint32_t accumulated_length = 0;
63+
vb::merge::path split_path(p.m_start);
64+
for (auto edge_id : p.m_edges) {
65+
// TODO - do we need to check if we enter a new tile or does the writer
66+
// handle this?
67+
68+
const auto* tile = m_reader.GetGraphTile(edge_id);
69+
const auto* edge = tile->directededge(edge_id);
70+
uint32_t edge_len = edge->length();
71+
72+
if (edge_len >= kMaximumLength) {
73+
// Output prior segment
74+
if (split_path.m_edges.size() > 0) {
75+
output_segment(split_path);
76+
}
77+
78+
// TODO - split this edge - for now just add the entire edge
79+
uint32_t edgeinfo_offset = edge->edgeinfo_offset();
80+
auto edgeinfo = tile->edgeinfo(edgeinfo_offset);
81+
std::vector<PointLL> shape = tile->edgeinfo(edge->edgeinfo_offset()).shape();
82+
if (!edge->forward()) {
83+
std::reverse(shape.begin(), shape.end());
84+
}
85+
86+
float shape_length = length(shape);
87+
88+
// Split the edge
89+
int n = (edge_len / kMaximumLength);
90+
float dist = static_cast<float>(edge_len) / static_cast<float>(n+1);
91+
uint32_t d = std::ceil(dist);
92+
for (int i = 0; i < n; i++) {
93+
// if (shape.size() == 0) break;
94+
auto sub_shape = trim_front(shape, d);
95+
output_segment(sub_shape, edge, edge_id);
96+
}
97+
if (shape.size() > 0) {
98+
output_segment(shape, edge, edge_id);
99+
}
100+
101+
// Start a new path at the end of this edge
102+
split_path.m_start = edge->endnode();
103+
split_path.m_edges.clear();
104+
accumulated_length = 0;
105+
} else if (accumulated_length + edge_len >= kMaximumLength) {
106+
// TODO - optimize the split to avoid short segments
107+
108+
// Output the current split path and start a new split path
109+
output_segment(split_path);
110+
split_path.m_start = split_path.m_end;
111+
split_path.m_edges.clear();
112+
split_path.m_edges.push_back(edge_id);
113+
split_path.m_end = edge->endnode();
114+
accumulated_length = edge_len;
115+
} else {
116+
// Add this edge to the new path
117+
split_path.m_edges.push_back(edge_id);
118+
split_path.m_end = edge->endnode();
119+
accumulated_length += edge_len;
120+
}
121+
}
122+
123+
// Output the last
124+
if (split_path.m_edges.size() > 0) {
125+
output_segment(split_path);
126+
}
127+
}
128+
129+
void geojson::output_segment(const vb::merge::path &p) {
31130
std::ostringstream out;
32-
out.precision(17);
131+
out.precision(9);
33132

34133
auto tile_id = p.m_start.Tile_Base();
35134

@@ -58,21 +157,25 @@ void geojson::add_path(const vb::merge::path &p) {
58157
if (directededge->classification() < best_frc) {
59158
best_frc = directededge->classification();
60159
}
160+
161+
// Get the edge shape. reverse the order if needed
61162
auto edgeinfo_offset = directededge->edgeinfo_offset();
62-
auto edgeinfo = tile->edgeinfo(edgeinfo_offset);
63-
auto decoder = edgeinfo.lazy_shape();
163+
std::vector<PointLL> shape = tile->edgeinfo(edgeinfo_offset).shape();
164+
if (!directededge->forward()) {
165+
std::reverse(shape.begin(), shape.end());
166+
}
64167

168+
// Serialize the shape
65169
out << "[";
66170
bool first_pt = true;
67-
while (!decoder.empty()) {
171+
for (const auto& pt : shape) {
68172
if (first_pt) { first_pt = false; } else { out << ","; }
69-
70-
auto pt = decoder.pop();
71173
out << "[" << pt.lng() << "," << pt.lat() << "]";
72174
}
73175
out << "]";
74176
}
75177

178+
// Add properties for this OSMLR segment
76179
vb::GraphId osmlr_id(tile_id.tileid(), tile_id.level(), tile_path_itr->second);
77180
out << "]},\"properties\":{"
78181
<< "\"tile_id\":" << tile_id.tileid() << ","
@@ -96,6 +199,55 @@ void geojson::add_path(const vb::merge::path &p) {
96199
tile_path_itr->second += 1;
97200
}
98201

202+
// Output a segment that is part of an edge.
203+
void geojson::output_segment(const std::vector<vm::PointLL>& shape,
204+
const vb::DirectedEdge* edge,
205+
const vb::GraphId& edgeid) {
206+
std::ostringstream out;
207+
out.precision(17);
208+
209+
auto tile_id = edgeid.Tile_Base();
210+
211+
auto tile_path_itr = m_tile_path_ids.find(tile_id);
212+
if (tile_path_itr == m_tile_path_ids.end()) {
213+
out << k_geojson_header;
214+
std::tie(tile_path_itr, std::ignore) = m_tile_path_ids.emplace(tile_id, 0);
215+
} else {
216+
out << ",";
217+
}
218+
219+
out << "{\"type\":\"Feature\",\"geometry\":";
220+
out << "{\"type\":\"MultiLineString\",\"coordinates\":[";
221+
out << "[";
222+
bool first_pt = true;
223+
for (const auto pt : shape) {
224+
if (first_pt) { first_pt = false; } else { out << ","; }
225+
out << "[" << pt.lng() << "," << pt.lat() << "]";
226+
}
227+
out << "]";
228+
229+
vb::GraphId osmlr_id(tile_id.tileid(), tile_id.level(), tile_path_itr->second);
230+
bool first = true;
231+
bool oneway = is_oneway(edge);
232+
bool drive_on_right = edge->drive_on_right();
233+
vb::RoadClass best_frc = edge->classification();
234+
out << "]},\"properties\":{"
235+
<< "\"tile_id\":" << tile_id.tileid() << ","
236+
<< "\"level\":" << tile_id.level() << ","
237+
<< "\"id\":" << tile_path_itr->second << ","
238+
<< "\"osmlr_id\":" << osmlr_id.value << ","
239+
<< "\"best_frc\":\"" << vb::to_string(best_frc) << "\","
240+
<< "\"oneway\":" << oneway << ","
241+
<< "\"drive_on_right\":" << drive_on_right << ","
242+
<< "\"original_edges\":\"";
243+
244+
out << edgeid;
245+
out << "\"}}";
246+
247+
m_writer.write_to(tile_id, out.str());
248+
tile_path_itr->second += 1;
249+
}
250+
99251
void geojson::finish() {
100252
for (auto entry : m_tile_path_ids) {
101253
m_writer.write_to(entry.first, k_geojson_footer);

0 commit comments

Comments
 (0)