1
1
#include " osmlr/output/geojson.hpp"
2
+ #include < valhalla/midgard/util.h>
2
3
#include < stdexcept>
3
4
5
+ namespace vm = valhalla::midgard;
4
6
namespace vb = valhalla::baldr;
5
7
6
8
namespace {
7
9
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
+
8
16
const std::string k_geojson_header = " {\" type\" :\" FeatureCollection\" ,\" features\" :[" ;
9
17
const std::string k_geojson_footer = " ]}" ;
10
18
@@ -28,8 +36,99 @@ geojson::~geojson() {
28
36
}
29
37
30
38
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) {
31
130
std::ostringstream out;
32
- out.precision (17 );
131
+ out.precision (9 );
33
132
34
133
auto tile_id = p.m_start .Tile_Base ();
35
134
@@ -58,21 +157,25 @@ void geojson::add_path(const vb::merge::path &p) {
58
157
if (directededge->classification () < best_frc) {
59
158
best_frc = directededge->classification ();
60
159
}
160
+
161
+ // Get the edge shape. reverse the order if needed
61
162
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
+ }
64
167
168
+ // Serialize the shape
65
169
out << " [" ;
66
170
bool first_pt = true ;
67
- while (!decoder. empty () ) {
171
+ for ( const auto & pt : shape ) {
68
172
if (first_pt) { first_pt = false ; } else { out << " ," ; }
69
-
70
- auto pt = decoder.pop ();
71
173
out << " [" << pt.lng () << " ," << pt.lat () << " ]" ;
72
174
}
73
175
out << " ]" ;
74
176
}
75
177
178
+ // Add properties for this OSMLR segment
76
179
vb::GraphId osmlr_id (tile_id.tileid (), tile_id.level (), tile_path_itr->second );
77
180
out << " ]},\" properties\" :{"
78
181
<< " \" tile_id\" :" << tile_id.tileid () << " ,"
@@ -96,6 +199,55 @@ void geojson::add_path(const vb::merge::path &p) {
96
199
tile_path_itr->second += 1 ;
97
200
}
98
201
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
+
99
251
void geojson::finish () {
100
252
for (auto entry : m_tile_path_ids) {
101
253
m_writer.write_to (entry.first , k_geojson_footer);
0 commit comments