30 std::map<std::string, unsigned int>
road;
33 const char *RoadOrMinusOne(
const std::optional<std::string> &
id, std::string &scratch)
const {
34 if (!
id.has_value()) {
41 scratch = std::to_string(it->second);
42 return scratch.c_str();
46 void Allocate(
const std::vector<std::string> &ids,
47 std::set<unsigned int> &used,
48 std::map<std::string, unsigned int> &out) {
49 std::vector<std::string> pending;
50 for (
const std::string &
id : ids) {
51 const bool numeric = !
id.empty() &&
52 id.find_first_not_of(
"0123456789") == std::string::npos;
54 const unsigned long value = std::stoul(
id);
55 if (value >= 1ul && used.count(
static_cast<unsigned int>(value)) == 0u) {
56 out[id] =
static_cast<unsigned int>(value);
57 used.insert(
static_cast<unsigned int>(value));
61 pending.push_back(
id);
63 unsigned int next = used.empty() ? 1u : (*used.rbegin() + 1u);
64 for (
const std::string &
id : pending) {
65 while (used.count(next) != 0u) {
73 IdMap BuildIdMap(
const TwinModelData &model) {
75 std::set<unsigned int> used;
76 std::vector<std::string> road_ids;
77 for (
const Road &
road : model.roads) {
78 road_ids.push_back(
road.id);
80 Allocate(road_ids, used, map.road);
81 std::vector<std::string> junction_ids;
82 for (
const Junction &
junction : model.junctions) {
85 Allocate(junction_ids, used, map.junction);
89 std::string Format(
double value) {
90 std::ostringstream out;
96 std::string Timestamp() {
97 const std::time_t now = std::time(
nullptr);
100 gmtime_s(&utc, &now);
102 gmtime_r(&now, &utc);
104 char buffer[32] = {0};
105 std::strftime(buffer,
sizeof(buffer),
"%Y-%m-%dT%H:%M:%S", &utc);
110 node.append_attribute(name).set_value(Format(value).c_str());
117 SetAttribute(section,
"s", 0.0);
119 std::vector<const Lane *> left;
120 std::vector<const Lane *> right;
121 for (
const Lane &lane :
road.lanes) {
123 left.push_back(&lane);
124 }
else if (lane.id < 0) {
125 right.push_back(&lane);
128 std::sort(left.begin(), left.end(), [](
const Lane *a,
const Lane *b) {
129 return a->id < b->id;
131 std::sort(right.begin(), right.end(), [](
const Lane *a,
const Lane *b) {
132 return a->id > b->id;
138 node.append_attribute(
"type").set_value(
ToString(lane.type));
139 node.append_attribute(
"level").set_value(
"false");
140 if (lane.predecessor_lane.has_value() || lane.successor_lane.has_value()) {
142 if (lane.predecessor_lane.has_value()) {
146 if (lane.successor_lane.has_value()) {
152 SetAttribute(width,
"sOffset", 0.0);
153 SetAttribute(width,
"a", lane.width);
154 SetAttribute(width,
"b", 0.0);
155 SetAttribute(width,
"c", 0.0);
156 SetAttribute(width,
"d", 0.0);
157 if (lane.marking.has_value()) {
159 SetAttribute(mark,
"sOffset", 0.0);
162 SetAttribute(mark,
"width", lane.marking->width);
165 if (lane.speed_limit.has_value()) {
167 SetAttribute(speed,
"sOffset", 0.0);
168 SetAttribute(speed,
"max", *lane.speed_limit);
175 for (
const Lane *lane : left) {
176 write_lane(
node, *lane);
185 if (
road.center_marking.has_value()) {
187 SetAttribute(mark,
"sOffset", 0.0);
190 SetAttribute(mark,
"width",
road.center_marking->width);
194 if (!right.empty()) {
196 for (
const Lane *lane : right) {
197 write_lane(
node, *lane);
203 if (!
road.predecessor.has_value() && !
road.successor.has_value()) {
207 auto write = [&](
const char *name,
const RoadLink &value) {
212 const auto it = pool.find(value.id);
213 node.append_attribute(
"elementId").set_value(it == pool.end() ? 0u : it->second);
214 if (value.contact.has_value()) {
215 node.append_attribute(
"contactPoint").set_value(
ToString(*value.contact));
218 if (
road.predecessor.has_value()) {
219 write(
"predecessor", *
road.predecessor);
221 if (
road.successor.has_value()) {
222 write(
"successor", *
road.successor);
233 SetAttribute(geometry,
"s", fitted.s);
234 SetAttribute(geometry,
"x", fitted.x);
235 SetAttribute(geometry,
"y", fitted.y);
236 SetAttribute(geometry,
"hdg", fitted.hdg);
237 SetAttribute(geometry,
"length", fitted.length);
238 if (fitted.is_line) {
242 static const char *names[8] = {
"aU",
"bU",
"cU",
"dU",
"aV",
"bV",
"cV",
"dV"};
243 for (
int i = 0; i < 8; ++i) {
244 SetAttribute(poly, names[i], fitted.coeffs[
static_cast<std::size_t
>(i)]);
248 s = fitted.s + fitted.length;
257 SetAttribute(
elevation,
"a",
road.reference_line.empty() ? 0.0 :
road.reference_line.front().
z);
267 const TwinModelData &model,
const IdMap &ids,
268 double crosswalk_half) {
269 std::vector<const Signal *> signals;
270 std::vector<const Signal *> crossings;
271 for (
const Signal &signal : model.signals) {
272 if (signal.road_id !=
road.id) {
278 if (!signals.empty()) {
280 unsigned int index = 0u;
281 for (
const Signal *signal : signals) {
284 SetAttribute(element,
"s", signal->s);
285 SetAttribute(element,
"t", signal->t);
290 SetAttribute(element,
"zOffset", 0.0);
294 const long kmh = std::lround(*signal->value * 3.6);
296 SetAttribute(element,
"value",
static_cast<double>(kmh));
302 SetAttribute(element,
"hOffset", signal->h_offset);
303 SetAttribute(element,
"pitch", 0.0);
304 SetAttribute(element,
"roll", 0.0);
308 if (!crossings.empty()) {
310 unsigned int index = 0u;
311 for (
const Signal *crossing : crossings) {
313 SetAttribute(element,
"s", crossing->s);
314 SetAttribute(element,
"t", 0.0);
318 SetAttribute(element,
"zOffset", 0.0);
319 SetAttribute(element,
"hdg", 0.0);
320 SetAttribute(element,
"width", crosswalk_half * 2.0);
323 for (
const Lane &lane :
road.lanes) {
324 half = std::max(half, std::abs(
static_cast<double>(lane.id)) * lane.width);
326 half = std::max(half, 3.5);
327 SetAttribute(element,
"length", half * 2.0);
329 const double corners[4][2] = {{-crosswalk_half, -half}, {crosswalk_half, -half},
330 {crosswalk_half, half}, {-crosswalk_half, half}};
331 for (
const auto &corner : corners) {
333 SetAttribute(
point,
"u", corner[0]);
334 SetAttribute(
point,
"v", corner[1]);
335 SetAttribute(
point,
"z", 0.0);
336 SetAttribute(
point,
"height", 0.0);
363 const IdMap ids = BuildIdMap(model);
368 node.append_attribute(
"id").set_value(ids.road.at(
road.id));
370 node.append_attribute(
"junction").set_value(ids.RoadOrMinusOne(
road.junction_id, scratch));
371 node.append_attribute(
"rule").set_value(
"RHT");
373 const double length = WritePlanView(
node,
road);
374 node.insert_attribute_after(
"length",
node.attribute(
"name")).set_value(Format(length).c_str());
377 WriteLaneSection(lanes,
road);
378 WriteSignalsAndObjects(
node,
road, model, ids, 2.0);
384 node.append_attribute(
"id").set_value(ids.junction.at(
junction.id));
385 unsigned int connection_index = 0u;
387 const auto incoming = ids.road.find(connection.incoming_road);
388 const auto connecting = ids.road.find(connection.connecting_road);
389 if (incoming == ids.road.end() || connecting == ids.road.end()) {
397 for (
const LaneLink &link : connection.lane_links) {
405 unsigned int controller_index = 0u;
409 node.append_attribute(
"name").set_value(controller.id.c_str());
410 node.append_attribute(
"sequence").set_value(controller.sequence);
411 for (
const std::string &signal_id : controller.signal_ids) {
412 unsigned int signal_index = 0u;
414 if (signal.id == signal_id) {
425 std::ostringstream out;
426 document.
save(out,
" ");
std::map< std::string, unsigned int > road
std::map< std::string, unsigned int > junction
bool set_value(const char_t *rhs)
void save(xml_writer &writer, const char_t *indent=PUGIXML_TEXT("\t"), unsigned int flags=format_default, xml_encoding encoding=encoding_auto) const
bool set_value(const char_t *rhs)
xml_node append_child(xml_node_type type=node_element)
xml_attribute append_attribute(const char_t *name)
xml_node prepend_child(xml_node_type type=node_element)
WGS84 <-> local ENU metres, a transverse Mercator centred on the origin.
std::string GetProjString() const
The +proj string written as the OpenDRIVE <geoReference>.
std::string ExportXodr(const TwinModelData &model, const LocalFrame &frame)
Serialise the model as an OpenDRIVE 1.6 document.
std::vector< Geom > FitPlanView(const geom::Line3 &line, double max_offset)
Fit a polyline as a piecewise cubic paramPoly3 planview.
SignalType GetSignalType(SignalKind kind)
const char * ToString(LaneType type)
Everything the OpenDRIVE writer reads.
std::vector< Signal > signals
std::vector< Road > roads
std::vector< Controller > controllers
std::vector< Junction > junctions