SUMO - Simulation of Urban MObility
NWWriter_OpenDrive.cpp
Go to the documentation of this file.
1 /****************************************************************************/
8 // Exporter writing networks using the openDRIVE format
9 /****************************************************************************/
10 // SUMO, Simulation of Urban MObility; see http://sumo.dlr.de/
11 // Copyright (C) 2011-2016 DLR (http://www.dlr.de/) and contributors
12 /****************************************************************************/
13 //
14 // This file is part of SUMO.
15 // SUMO is free software: you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation, either version 3 of the License, or
18 // (at your option) any later version.
19 //
20 /****************************************************************************/
21 
22 
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #ifdef _MSC_VER
27 #include <windows_config.h>
28 #else
29 #include <config.h>
30 #endif
31 
32 #include <ctime>
33 #include "NWWriter_OpenDrive.h"
36 #include <netbuild/NBEdge.h>
37 #include <netbuild/NBEdgeCont.h>
38 #include <netbuild/NBNode.h>
39 #include <netbuild/NBNodeCont.h>
40 #include <netbuild/NBNetBuilder.h>
43 #include <utils/geom/bezier.h>
45 #include <utils/common/StdDefs.h>
48 
49 #ifdef CHECK_MEMORY_LEAKS
50 #include <foreign/nvwa/debug_new.h>
51 #endif // CHECK_MEMORY_LEAKS
52 
53 //#define DEBUG_SMOOTH_GEOM
54 #define DEBUGCOND true
55 
56 #define MIN_TURN_DIAMETER 2.0
57 
58 
59 // ===========================================================================
60 // method definitions
61 // ===========================================================================
62 // ---------------------------------------------------------------------------
63 // static methods
64 // ---------------------------------------------------------------------------
65 void
67  // check whether an opendrive-file shall be generated
68  if (!oc.isSet("opendrive-output")) {
69  return;
70  }
71  const bool origNames = oc.getBool("output.original-names");
72  // some internal mapping containers
73  int edgeID = 1;
74  int nodeID = 1;
75  StringBijection<int> edgeMap;
76  StringBijection<int> nodeMap;
77  //
78  OutputDevice& device = OutputDevice::getDevice(oc.getString("opendrive-output"));
79  device << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
80  device.openTag("OpenDRIVE");
81  time_t now = time(0);
82  std::string dstr(ctime(&now));
83  const NBNodeCont& nc = nb.getNodeCont();
84  const NBEdgeCont& ec = nb.getEdgeCont();
86  // write header
87  device.openTag("header");
88  device.writeAttr("revMajor", "1");
89  device.writeAttr("revMinor", "4");
90  device.writeAttr("name", "");
91  device.writeAttr("version", "1.00");
92  device.writeAttr("date", dstr.substr(0, dstr.length() - 1));
93  device.writeAttr("north", b.ymax());
94  device.writeAttr("south", b.ymin());
95  device.writeAttr("east", b.xmax());
96  device.writeAttr("west", b.xmin());
97  /* @note obsolete in 1.4
98  device.writeAttr("maxRoad", ec.size());
99  device.writeAttr("maxJunc", nc.size());
100  device.writeAttr("maxPrg", 0);
101  */
102  device.closeTag();
103 
104  // write normal edges (road)
105  for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
106  const NBEdge* e = (*i).second;
107  device.openTag("road");
108  device.writeAttr("name", StringUtils::escapeXML(e->getStreetName()));
109  device.writeAttr("length", e->getLength());
110  device.writeAttr("id", getID(e->getID(), edgeMap, edgeID));
111  device.writeAttr("junction", -1);
112  device.openTag("link");
113  device.openTag("predecessor");
114  device.writeAttr("elementType", "junction");
115  device.writeAttr("elementId", getID(e->getFromNode()->getID(), nodeMap, nodeID));
116  device.closeTag();
117  device.openTag("successor");
118  device.writeAttr("elementType", "junction");
119  device.writeAttr("elementId", getID(e->getToNode()->getID(), nodeMap, nodeID));
120  device.closeTag();
121  device.closeTag();
122  device.openTag("type").writeAttr("s", 0).writeAttr("type", "town").closeTag();
123  device.openTag("planView");
124  device.setPrecision(8); // geometry hdg requires higher precision
125  // for the shape we need to use the leftmost border of the leftmost lane
126  const std::vector<NBEdge::Lane>& lanes = e->getLanes();
128 #ifdef DEBUG_SMOOTH_GEOM
129  if (DEBUGCOND) {
130  std::cout << "write planview for edge " << e->getID() << "\n";
131  }
132 #endif
133  OutputDevice_String elevationOSS(false, 3);
134  elevationOSS.setPrecision(8);
135  if (ls.size() == 2 || e->getPermissions() == SVC_PEDESTRIAN) {
136  // foot paths may contain sharp angles
137  writeGeomLines(ls, device, elevationOSS);
138  } else {
139  bool ok = writeGeomSmooth(ls, e->getSpeed(), device, elevationOSS);
140  if (!ok) {
141  WRITE_WARNING("Could not compute smooth shape for edge '" + e->getID() + "'.");
142  }
143  }
144  // check if the lane geometries are compatible with OpenDRIVE assumptions (colinear stop line)
145  if (e->getNumLanes() > 1) {
146  // compute 'stop line' of rightmost lane
147  const PositionVector shape0 = e->getLaneShape(0);
148  assert(shape0.size() >= 2);
149  const Position& from = shape0[-2];
150  const Position& to = shape0[-1];
151  PositionVector stopLine;
152  stopLine.push_back(to);
153  stopLine.push_back(to - PositionVector::sideOffset(from, to, -1000.0));
154  // endpoints of all other lanes should be on the stop line
155  for (int lane = 1; lane < e->getNumLanes(); ++lane) {
156  const SUMOReal dist = stopLine.distance2D(e->getLaneShape(lane)[-1]);
157  if (dist > NUMERICAL_EPS) {
158  WRITE_WARNING("Uneven stop line at lane '" + e->getLaneID(lane) + "' (dist=" + toString(dist) + ") cannot be represented in OpenDRIVE.");
159  }
160  }
161  }
162 
163 
164  device.setPrecision(OUTPUT_ACCURACY);
165  device.closeTag();
166  writeElevationProfile(ls, device, elevationOSS);
167  device << " <lateralProfile/>\n";
168  device << " <lanes>\n";
169  device << " <laneSection s=\"0\">\n";
170  writeEmptyCenterLane(device, "solid", 0.13);
171  device << " <right>\n";
172  for (int j = e->getNumLanes(); --j >= 0;) {
173  device << " <lane id=\"-" << e->getNumLanes() - j << "\" type=\"" << getLaneType(e->getPermissions(j)) << "\" level=\"true\">\n";
174  device << " <link/>\n";
175  // this could be used for geometry-link junctions without u-turn,
176  // predecessor and sucessors would be lane indices,
177  // road predecessor / succesfors would be of type 'road' rather than
178  // 'junction'
179  //device << " <predecessor id=\"-1\"/>\n";
180  //device << " <successor id=\"-1\"/>\n";
181  //device << " </link>\n";
182  device << " <width sOffset=\"0\" a=\"" << e->getLaneWidth(j) << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
183  std::string markType = "broken";
184  if (j == 0) {
185  markType = "solid";
186  }
187  device << " <roadMark sOffset=\"0\" type=\"" << markType << "\" weight=\"standard\" color=\"standard\" width=\"0.13\"/>\n";
188  device << " <speed sOffset=\"0\" max=\"" << lanes[j].speed << "\"/>\n";
189  device << " </lane>\n";
190  }
191  device << " </right>\n";
192  device << " </laneSection>\n";
193  device << " </lanes>\n";
194  device << " <objects/>\n";
195  device << " <signals/>\n";
196  if (origNames) {
197  device << " <userData code=\"sumoId\" value=\"" << e->getID() << "\"/>\n";
198  }
199  device.closeTag();
200  }
201  device.lf();
202  // write junction-internal edges (road). In OpenDRIVE these are called 'paths' or 'connecting roads'
203  for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
204  NBNode* n = (*i).second;
205  const std::vector<NBEdge*>& incoming = (*i).second->getIncomingEdges();
206  for (std::vector<NBEdge*>::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
207  const NBEdge* inEdge = *j;
208  const std::vector<NBEdge::Connection>& elv = inEdge->getConnections();
209  for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
210  const NBEdge::Connection& c = *k;
211  const NBEdge* outEdge = c.toEdge;
212  if (outEdge == 0) {
213  continue;
214  }
215  const SUMOReal width = c.toEdge->getLaneWidth(c.toLane);
216  const PositionVector begShape = getLeftLaneBorder(inEdge, c.fromLane);
217  const PositionVector endShape = getLeftLaneBorder(outEdge, c.toLane);
218  //std::cout << "computing reference line for internal lane " << c.getInternalLaneID() << " begLane=" << inEdge->getLaneShape(c.fromLane) << " endLane=" << outEdge->getLaneShape(c.toLane) << "\n";
219 
220  SUMOReal length;
221  PositionVector fallBackShape;
222  const bool turnaround = inEdge->isTurningDirectionAt(outEdge);
223  bool ok = true;
224  PositionVector init = NBNode::bezierControlPoints(begShape, endShape, turnaround, 25, 25, ok);
225  if (init.size() == 0) {
226  fallBackShape.push_back(begShape.back());
227  fallBackShape.push_back(endShape.front());
228  length = fallBackShape.length2D();
229  // problem with turnarounds is known, method currently returns 'ok' (#2539)
230  if (!ok) {
231  WRITE_WARNING("Could not compute smooth shape from lane '" + inEdge->getLaneID(c.fromLane) + "' to lane '" + outEdge->getLaneID(c.toLane) + "'. Use option 'junctions.scurve-stretch' or increase radius of junction '" + inEdge->getToNode()->getID() + "' to fix this.");
232  }
233  } else {
234  length = bezier(init, 12).length2D();
235  }
236 
237  device.openTag("road");
238  device.writeAttr("name", c.getInternalLaneID());
239  device.writeAttr("length", length);
240  device.writeAttr("id", getID(c.getInternalLaneID(), edgeMap, edgeID));
241  device.writeAttr("junction", getID(n->getID(), nodeMap, nodeID));
242  device.openTag("link");
243  device.openTag("predecessor");
244  device.writeAttr("elementType", "road");
245  device.writeAttr("elementId", getID(inEdge->getID(), edgeMap, edgeID));
246  device.writeAttr("contactPoint", "end");
247  device.closeTag();
248  device.openTag("successor");
249  device.writeAttr("elementType", "road");
250  device.writeAttr("elementId", getID(outEdge->getID(), edgeMap, edgeID));
251  device.writeAttr("contactPoint", "start");
252  device.closeTag();
253  device.closeTag();
254  device.openTag("type").writeAttr("s", 0).writeAttr("type", "town").closeTag();
255  device.openTag("planView");
256  device.setPrecision(8); // geometry hdg requires higher precision
257  OutputDevice_String elevationOSS(false, 3);
258  if (init.size() == 0) {
259  writeGeomLines(fallBackShape, device, elevationOSS);
260  } else {
261  writeGeomPP3(device, elevationOSS, init, length);
262  }
263  device.setPrecision(OUTPUT_ACCURACY);
264  device.closeTag();
265  writeElevationProfile(fallBackShape, device, elevationOSS);
266  device << " <lateralProfile/>\n";
267  device << " <lanes>\n";
268  device << " <laneSection s=\"0\">\n";
269  writeEmptyCenterLane(device, "none", 0);
270  device << " <right>\n";
271  device << " <lane id=\"-1\" type=\"" << getLaneType(outEdge->getPermissions(c.toLane)) << "\" level=\"true\">\n";
272  device << " <link>\n";
273  device << " <predecessor id=\"-" << inEdge->getNumLanes() - c.fromLane << "\"/>\n";
274  device << " <successor id=\"-" << outEdge->getNumLanes() - c.toLane << "\"/>\n";
275  device << " </link>\n";
276  device << " <width sOffset=\"0\" a=\"" << width << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
277  device << " <roadMark sOffset=\"0\" type=\"none\" weight=\"standard\" color=\"standard\" width=\"0.13\"/>\n";
278  device << " </lane>\n";
279  device << " </right>\n";
280  device << " </laneSection>\n";
281  device << " </lanes>\n";
282  device << " <objects/>\n";
283  device << " <signals/>\n";
284  device.closeTag();
285  }
286  }
287  }
288 
289  // write junctions (junction)
290  for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
291  NBNode* n = (*i).second;
292  const std::vector<NBEdge*>& incoming = n->getIncomingEdges();
293  // check if any connections must be written
294  int numConnections = 0;
295  for (std::vector<NBEdge*>::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
296  numConnections += (int)((*j)->getConnections().size());
297  }
298  if (numConnections == 0) {
299  continue;
300  }
301  device << " <junction name=\"" << n->getID() << "\" id=\"" << getID(n->getID(), nodeMap, nodeID) << "\">\n";
302  int index = 0;
303  for (std::vector<NBEdge*>::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
304  const NBEdge* inEdge = *j;
305  const std::vector<NBEdge::Connection>& elv = inEdge->getConnections();
306  for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
307  const NBEdge::Connection& c = *k;
308  const NBEdge* outEdge = c.toEdge;
309  if (outEdge == 0) {
310  continue;
311  }
312  device << " <connection id=\""
313  << index << "\" incomingRoad=\"" << getID(inEdge->getID(), edgeMap, edgeID)
314  << "\" connectingRoad=\""
315  << getID(c.getInternalLaneID(), edgeMap, edgeID)
316  << "\" contactPoint=\"start\">\n";
317  device << " <laneLink from=\"-" << inEdge->getNumLanes() - c.fromLane
318  << "\" to=\"-1" // every connection has its own edge
319  << "\"/>\n";
320  device << " </connection>\n";
321  ++index;
322  }
323  }
324  device << " </junction>\n";
325  }
326 
327  device.closeTag();
328  device.close();
329 }
330 
331 
332 SUMOReal
333 NWWriter_OpenDrive::writeGeomLines(const PositionVector& shape, OutputDevice& device, OutputDevice& elevationDevice, SUMOReal offset) {
334  for (int j = 0; j < (int)shape.size() - 1; ++j) {
335  const Position& p = shape[j];
336  const Position& p2 = shape[j + 1];
337  const SUMOReal hdg = shape.angleAt2D(j);
338  const SUMOReal length = p.distanceTo2D(p2);
339  device.openTag("geometry");
340  device.writeAttr("s", offset);
341  device.writeAttr("x", p.x());
342  device.writeAttr("y", p.y());
343  device.writeAttr("hdg", hdg);
344  device.writeAttr("length", length);
345  device.openTag("line").closeTag();
346  device.closeTag();
347  elevationDevice << " <elevation s=\"" << offset << "\" a=\"" << p.z() << "\" b=\"" << (p2.z() - p.z()) / MAX2(POSITION_EPS, length) << "\" c=\"0\" d=\"0\"/>\n";
348  offset += length;
349  }
350  return offset;
351 }
352 
353 
354 void
355 NWWriter_OpenDrive::writeEmptyCenterLane(OutputDevice& device, const std::string& mark, SUMOReal markWidth) {
356  device << " <center>\n";
357  device << " <lane id=\"0\" type=\"none\" level=\"true\">\n";
358  device << " <link/>\n";
359  device << " <roadMark sOffset=\"0\" type=\"" << mark << "\" weight=\"standard\" color=\"standard\" width=\"" << markWidth << "\"/>\n";
360  device << " </lane>\n";
361  device << " </center>\n";
362 }
363 
364 
365 int
366 NWWriter_OpenDrive::getID(const std::string& origID, StringBijection<int>& map, int& lastID) {
367  if (map.hasString(origID)) {
368  return map.get(origID);
369  }
370  map.insert(origID, lastID++);
371  return lastID - 1;
372 }
373 
374 
375 std::string
377  switch (permissions) {
378  case SVC_PEDESTRIAN:
379  return "sidewalk";
380  //case (SVC_BICYCLE | SVC_PEDESTRIAN):
381  // WRITE_WARNING("Ambiguous lane type (biking+driving) for road '" + roadID + "'");
382  // return "sidewalk";
383  case SVC_BICYCLE:
384  return "biking";
385  case 0:
386  // ambiguous
387  return "none";
388  case SVC_RAIL:
389  case SVC_RAIL_URBAN:
390  case SVC_RAIL_ELECTRIC:
391  return "rail";
392  case SVC_TRAM:
393  return "tram";
394  default: {
395  // complex permissions
396  if (permissions == SVCAll) {
397  return "driving";
398  } else if (isRailway(permissions)) {
399  return "rail";
400  } else if ((permissions & SVC_PASSENGER) != 0) {
401  return "driving";
402  } else {
403  return "restricted";
404  }
405  }
406  }
407 }
408 
409 
411 NWWriter_OpenDrive::getLeftLaneBorder(const NBEdge* edge, int laneIndex) {
412  if (laneIndex == -1) {
413  // leftmost lane
414  laneIndex = (int)edge->getNumLanes() - 1;
415  }
417  // PositionVector result = edge->getLaneShape(laneIndex);
418  // (and the moveo2side)
419  // However, the lanes in SUMO have a small lateral gap (SUMO_const_laneOffset) to account for markings
420  // In OpenDRIVE this gap does not exists so we have to do all lateral
421  // computations based on the reference line
422  // This assumes that the 'stop line' for all lanes is colinear!
423  const int leftmost = (int)edge->getNumLanes() - 1;
424  SUMOReal widthOffset = -(edge->getLaneWidth(leftmost) / 2);
425  // collect lane widths from left border of edge to left border of lane to connect to
426  for (int i = leftmost; i > laneIndex; i--) {
427  widthOffset += edge->getLaneWidth(i);
428  }
429  PositionVector result = edge->getLaneShape(leftmost);
430  try {
431  result.move2side(widthOffset);
432  } catch (InvalidArgument&) { }
433  return result;
434 }
435 
436 
437 SUMOReal
439  OutputDevice& device,
440  OutputDevice& elevationDevice,
441  PositionVector init,
442  SUMOReal length,
443  SUMOReal offset) {
444  assert(init.size() == 3 || init.size() == 4);
445 
446  // avoid division by 0
447  length = MAX2(POSITION_EPS, length);
448 
449  const Position p = init.front();
450  const SUMOReal hdg = init.angleAt2D(0);
451  // translate to u,v coordinates
452  init.add(-p.x(), -p.y(), -p.z());
453  init.rotate2D(-hdg);
454 
455  // parametric coefficients
456  SUMOReal aU, bU, cU, dU;
457  SUMOReal aV, bV, cV, dV;
458  SUMOReal aZ, bZ, cZ, dZ;
459 
460  // unfactor the Bernstein polynomials of degree 2 (or 3) and collect the coefficients
461  if (init.size() == 3) {
462  //f(x, a, b ,c) = a + (2*b - 2*a)*x + (a - 2*b + c)*x*x
463  aU = init[0].x();
464  bU = 2 * init[1].x() - 2 * init[0].x();
465  cU = init[0].x() - 2 * init[1].x() + init[2].x();
466  dU = 0;
467 
468  aV = init[0].y();
469  bV = 2 * init[1].y() - 2 * init[0].y();
470  cV = init[0].y() - 2 * init[1].y() + init[2].y();
471  dV = 0;
472 
473  // elevation is not parameteric on [0:1] but on [0:length]
474  aZ = init[0].z();
475  bZ = (2 * init[1].z() - 2 * init[0].z()) / length;
476  cZ = (init[0].z() - 2 * init[1].z() + init[2].z()) / (length * length);
477  dZ = 0;
478 
479  } else {
480  // f(x, a, b, c, d) = a + (x*((3*b) - (3*a))) + ((x*x)*((3*a) + (3*c) - (6*b))) + ((x*x*x)*((3*b) - (3*c) - a + d))
481  aU = init[0].x();
482  bU = 3 * init[1].x() - 3 * init[0].x();
483  cU = 3 * init[0].x() - 6 * init[1].x() + 3 * init[2].x();
484  dU = -init[0].x() + 3 * init[1].x() - 3 * init[2].x() + init[3].x();
485 
486  aV = init[0].y();
487  bV = 3 * init[1].y() - 3 * init[0].y();
488  cV = 3 * init[0].y() - 6 * init[1].y() + 3 * init[2].y();
489  dV = -init[0].y() + 3 * init[1].y() - 3 * init[2].y() + init[3].y();
490 
491  // elevation is not parameteric on [0:1] but on [0:length]
492  aZ = init[0].z();
493  bZ = (3 * init[1].z() - 3 * init[0].z()) / length;
494  cZ = (3 * init[0].z() - 6 * init[1].z() + 3 * init[2].z()) / (length * length);
495  dZ = (-init[0].z() + 3 * init[1].z() - 3 * init[2].z() + init[3].z()) / (length * length * length);
496  }
497 
498  device.openTag("geometry");
499  device.writeAttr("s", offset);
500  device.writeAttr("x", p.x());
501  device.writeAttr("y", p.y());
502  device.writeAttr("hdg", hdg);
503  device.writeAttr("length", length);
504 
505  device.openTag("paramPoly3");
506  device.writeAttr("aU", aU);
507  device.writeAttr("bU", bU);
508  device.writeAttr("cU", cU);
509  device.writeAttr("dU", dU);
510  device.writeAttr("aV", aV);
511  device.writeAttr("bV", bV);
512  device.writeAttr("cV", cV);
513  device.writeAttr("dV", dV);
514  device.closeTag();
515  device.closeTag();
516 
517  // write elevation
518  elevationDevice.openTag("elevation");
519  elevationDevice.writeAttr("s", offset);
520  elevationDevice.writeAttr("a", aZ);
521  elevationDevice.writeAttr("b", bZ);
522  elevationDevice.writeAttr("c", cZ);
523  elevationDevice.writeAttr("d", dZ);
524  elevationDevice.closeTag();
525 
526  return offset + length;
527 }
528 
529 
530 bool
532 #ifdef DEBUG_SMOOTH_GEOM
533  if (DEBUGCOND) {
534  std::cout << "writeGeomSmooth\n n=" << shape.size() << " shape=" << toString(shape) << "\n";
535  }
536 #endif
537  bool ok = true;
538  const SUMOReal angleThresh = DEG2RAD(5); // changes below thresh are considered to be straight (make configurable)
539  const SUMOReal longThresh = speed; // 16.0; // make user-configurable (should match the sampling rate of the source data)
540  const SUMOReal curveCutout = longThresh / 2; // 8.0; // make user-configurable (related to the maximum turning rate)
541  // the length of the segment that is added for cutting a corner can be bounded by 2*curveCutout (prevent the segment to be classified as 'long')
542  assert(longThresh >= 2 * curveCutout);
543  assert(shape.size() > 2);
544  // add intermediate points wherever there is a strong angular change between long segments
545  // assume the geometry is simplified so as not to contain consecutive colinear points
546  PositionVector shape2 = shape;
547  SUMOReal maxAngleDiff = 0;
548  SUMOReal offset = 0;
549  for (int j = 1; j < (int)shape.size() - 1; ++j) {
550  //const SUMOReal hdg = shape.angleAt2D(j);
551  const Position& p0 = shape[j - 1];
552  const Position& p1 = shape[j];
553  const Position& p2 = shape[j + 1];
554  const SUMOReal dAngle = fabs(GeomHelper::angleDiff(p0.angleTo2D(p1), p1.angleTo2D(p2)));
555  const SUMOReal length1 = p0.distanceTo2D(p1);
556  const SUMOReal length2 = p1.distanceTo2D(p2);
557  maxAngleDiff = MAX2(maxAngleDiff, dAngle);
558 #ifdef DEBUG_SMOOTH_GEOM
559  if (DEBUGCOND) {
560  std::cout << " j=" << j << " dAngle=" << RAD2DEG(dAngle) << " length1=" << length1 << " length2=" << length2 << "\n";
561  }
562 #endif
563  if (dAngle > angleThresh
564  && (length1 > longThresh || j == 1)
565  && (length2 > longThresh || j == (int)shape.size() - 2)) {
566  shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 - MIN2(length1 - POSITION_EPS, curveCutout)));
567  shape2.insertAtClosest(shape.positionAtOffset2D(offset + length1 + MIN2(length2 - POSITION_EPS, curveCutout)));
568  shape2.removeClosest(p1);
569  }
570  offset += length1;
571  }
572  const int numPoints = (int)shape2.size();
573 #ifdef DEBUG_SMOOTH_GEOM
574  if (DEBUGCOND) {
575  std::cout << " n=" << numPoints << " shape2=" << toString(shape2) << "\n";
576  }
577 #endif
578 
579  if (maxAngleDiff < angleThresh) {
580  writeGeomLines(shape2, device, elevationDevice, 0);
581 #ifdef DEBUG_SMOOTH_GEOM
582  if (DEBUGCOND) {
583  std::cout << " special case: all lines. maxAngleDiff=" << maxAngleDiff << "\n";
584  }
585 #endif
586  return ok;
587  }
588 
589  // write the long segments as lines, short segments as curves
590  offset = 0;
591  for (int j = 0; j < numPoints - 1; ++j) {
592  const Position& p0 = shape2[j];
593  const Position& p1 = shape2[j + 1];
594  PositionVector line;
595  line.push_back(p0);
596  line.push_back(p1);
597  const SUMOReal lineLength = line.length2D();
598  if (lineLength >= longThresh) {
599  offset = writeGeomLines(line, device, elevationDevice, offset);
600 #ifdef DEBUG_SMOOTH_GEOM
601  if (DEBUGCOND) {
602  std::cout << " writeLine=" << toString(line) << "\n";
603  }
604 #endif
605  } else {
606  // find control points
607  PositionVector begShape;
608  PositionVector endShape;
609  if (j == 0) {
610  // keep the angle of the first segment but end at the front of the shape
611  begShape = line;
612  begShape.add(p0 - begShape.back());
613  } else if (j == 1 || p0.distanceTo2D(shape2[j - 1]) > longThresh) {
614  // use the previous segment if it is long or the first one
615  begShape.push_back(shape2[j - 1]);
616  begShape.push_back(p0);
617  } else {
618  // end at p0 with mean angle of the previous and current segment
619  begShape.push_back(shape2[j - 1]);
620  begShape.push_back(p1);
621  begShape.add(p0 - begShape.back());
622  }
623  if (j == numPoints - 2) {
624  // keep the angle of the last segment but start at the end of the shape
625  endShape = line;
626  endShape.add(p1 - endShape.front());
627  } else if (j == numPoints - 3 || p1.distanceTo2D(shape2[j + 2]) > longThresh) {
628  // use the next segment if it is long or the final one
629  endShape.push_back(p1);
630  endShape.push_back(shape2[j + 2]);
631  } else {
632  // start at p1 with mean angle of the current and next segment
633  endShape.push_back(p0);
634  endShape.push_back(shape2[j + 2]);
635  endShape.add(p1 - endShape.front());
636  }
637  PositionVector init = NBNode::bezierControlPoints(begShape, endShape, false, 25, 25, ok);
638  if (init.size() == 0) {
639  // could not compute control points, write line
640  offset = writeGeomLines(line, device, elevationDevice, offset);
641 #ifdef DEBUG_SMOOTH_GEOM
642  if (DEBUGCOND) {
643  std::cout << " writeLine lineLength=" << lineLength << " begShape=" << toString(begShape) << " endShape=" << toString(endShape) << " init=" << toString(init) << "\n";
644  }
645 #endif
646  } else {
647  // write bezier
648  const SUMOReal curveLength = bezier(init, 12).length2D();
649  offset = writeGeomPP3(device, elevationDevice, init, curveLength, offset);
650 #ifdef DEBUG_SMOOTH_GEOM
651  if (DEBUGCOND) {
652  std::cout << " writeCurve lineLength=" << lineLength << " curveLength=" << curveLength << " begShape=" << toString(begShape) << " endShape=" << toString(endShape) << " init=" << toString(init) << "\n";
653  }
654 #endif
655  }
656  }
657  }
658  return ok;
659 }
660 
661 
662 void
664  // check if the shape is flat
665  bool flat = true;
666  SUMOReal z = shape.size() == 0 ? 0 : shape[0].z();
667  for (int i = 1; i < (int)shape.size(); ++i) {
668  if (fabs(shape[i].z() - z) > NUMERICAL_EPS) {
669  flat = false;
670  break;
671  }
672  }
673  device << " <elevationProfile>\n";
674  if (flat) {
675  device << " <elevation s=\"0\" a=\"" << z << "\" b=\"0\" c=\"0\" d=\"0\"/>\n";
676  } else {
677  device << elevationDevice.getString();
678  }
679  device << " </elevationProfile>\n";
680 
681 }
682 
683 
684 /****************************************************************************/
685 
static SUMOReal writeGeomLines(const PositionVector &shape, OutputDevice &device, OutputDevice &elevationDevice, SUMOReal offset=0)
write geometry as sequence of lines (sumo style)
#define DEBUGCOND
static SUMOReal writeGeomPP3(OutputDevice &device, OutputDevice &elevationDevice, PositionVector init, SUMOReal length, SUMOReal offset=0)
write geometry as a single bezier curve (paramPoly3)
static bool writeGeomSmooth(const PositionVector &shape, SUMOReal speed, OutputDevice &device, OutputDevice &elevationDevice)
write geometry as sequence of lines and bezier curves
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:257
static Position sideOffset(const Position &beg, const Position &end, const SUMOReal amount)
get a side position of position vector using a offset
A structure which describes a connection between edges or lanes.
Definition: NBEdge.h:157
int toLane
The lane the connections yields in.
Definition: NBEdge.h:178
is a pedestrian
std::map< std::string, NBNode * >::const_iterator begin() const
Returns the pointer to the begin of the stored nodes.
Definition: NBNodeCont.h:126
SUMOReal angleTo2D(const Position &other) const
returns the angle in the plane of the vector pointing from here to the other position ...
Definition: Position.h:243
NBEdge * toEdge
The edge the connections yields in.
Definition: NBEdge.h:175
const Boundary & getConvBoundary() const
Returns the converted boundary.
std::map< std::string, NBNode * >::const_iterator end() const
Returns the pointer to the end of the stored nodes.
Definition: NBNodeCont.h:134
vehicle is a not electrified rail
vehicle is a bicycle
std::string getString() const
Returns the current content as a string.
int SVCPermissions
The representation of a single edge during network building.
Definition: NBEdge.h:71
static std::string escapeXML(const std::string &orig)
Replaces the standard escapes by their XML entities.
void setPrecision(int precision=OUTPUT_ACCURACY)
Sets the precison or resets it to default.
vehicle is a light rail
SUMOReal ymin() const
Returns minimum y-coordinate.
Definition: Boundary.cpp:142
T MAX2(T a, T b)
Definition: StdDefs.h:75
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition: NBEdge.h:552
std::map< std::string, NBEdge * >::const_iterator end() const
Returns the pointer to the end of the stored edges.
Definition: NBEdgeCont.h:198
#define RAD2DEG(x)
Definition: GeomHelper.h:46
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
SUMOReal ymax() const
Returns maximum y-coordinate.
Definition: Boundary.cpp:148
const std::string & getID() const
Returns the id.
Definition: Named.h:66
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permission is a railway edge.
const SVCPermissions SVCAll
static SUMOReal angleDiff(const SUMOReal angle1, const SUMOReal angle2)
Returns the difference of the second angle to the first angle in radiants.
Definition: GeomHelper.cpp:178
A class that stores a 2D geometrical boundary.
Definition: Boundary.h:48
SUMOReal xmin() const
Returns minimum x-coordinate.
Definition: Boundary.cpp:130
vehicle is a (possibly fast moving) electric rail
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:200
vehicle is a city rail
#define OUTPUT_ACCURACY
Definition: config.h:163
SUMOReal distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:232
void rotate2D(SUMOReal angle)
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
void insert(const std::string str, const T key, bool checkDuplicates=true)
static void writeNetwork(const OptionsCont &oc, NBNetBuilder &nb)
Writes the network into a openDRIVE-file.
std::map< std::string, NBEdge * >::const_iterator begin() const
Returns the pointer to the begin of the stored edges.
Definition: NBEdgeCont.h:190
SUMOReal angleAt2D(int pos) const
get angle in certain position of position vector
SUMOReal z() const
Returns the z-position.
Definition: Position.h:73
std::string getLaneID(int lane) const
get Lane ID (Secure)
Definition: NBEdge.cpp:2499
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:395
int fromLane
The lane the connections starts at.
Definition: NBEdge.h:172
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:46
NBEdgeCont & getEdgeCont()
Returns the edge container.
Definition: NBNetBuilder.h:153
A list of positions.
void add(SUMOReal xoff, SUMOReal yoff, SUMOReal zoff)
T get(const std::string &str) const
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
static void writeEmptyCenterLane(OutputDevice &device, const std::string &mark, SUMOReal markWidth)
Storage for edges, including some functionality operating on multiple edges.
Definition: NBEdgeCont.h:66
T MIN2(T a, T b)
Definition: StdDefs.h:69
SUMOReal x() const
Returns the x-position.
Definition: Position.h:63
void bezier(int npts, SUMOReal b[], int cpts, SUMOReal p[])
Definition: bezier.cpp:101
#define POSITION_EPS
Definition: config.h:187
const std::string & getStreetName() const
Returns the street name of this edge.
Definition: NBEdge.h:518
SUMOReal length2D() const
Returns the length.
SUMOReal distance2D(const Position &p, bool perpendicular=false) const
closest 2D-distance to point p (or -1 if perpendicular is true and the point is beyond this vector) ...
#define DEG2RAD(x)
Definition: GeomHelper.h:45
std::string toString(const T &t, std::streamsize accuracy=OUTPUT_ACCURACY)
Definition: ToString.h:55
static PositionVector bezierControlPoints(const PositionVector &begShape, const PositionVector &endShape, bool isTurnaround, SUMOReal extrapolateBeg, SUMOReal extrapolateEnd, bool &ok, NBNode *recordError=0)
Definition: NBNode.cpp:501
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition: NBEdge.cpp:2726
vehicle is a passenger car (a "normal" car)
static std::string getLaneType(SVCPermissions permissions)
SUMOReal xmax() const
Returns maximum x-coordinate.
Definition: Boundary.cpp:136
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition: NBEdge.cpp:659
const EdgeVector & getIncomingEdges() const
Returns this node&#39;s incoming edges.
Definition: NBNode.h:240
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition: NBEdge.h:803
NBNodeCont & getNodeCont()
Returns the node container.
Definition: NBNetBuilder.h:161
Instance responsible for building networks.
Definition: NBNetBuilder.h:112
static OutputDevice & getDevice(const std::string &name)
Returns the described OutputDevice.
A storage for options typed value containers)
Definition: OptionsCont.h:99
static PositionVector getLeftLaneBorder(const NBEdge *edge, int laneIndex=-1)
get the left border of the given lane (the leftmost one by default)
static const GeoConvHelper & getFinal()
the coordinate transformation for writing the location element and for tracking the original coordina...
SUMOReal getSpeed() const
Returns the speed allowed on this edge.
Definition: NBEdge.h:489
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition: NBEdge.cpp:2189
Represents a single node (junction) during network building.
Definition: NBNode.h:74
Position positionAtOffset2D(SUMOReal pos, SUMOReal lateralOffset=0) const
Returns the position at the given length.
void move2side(SUMOReal amount)
move position vector to side using certain ammount
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:71
bool closeTag()
Closes the most recently opened tag.
#define SUMOReal
Definition: config.h:213
#define NUMERICAL_EPS
Definition: config.h:160
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:409
Container for nodes during the netbuilding process.
Definition: NBNodeCont.h:63
std::string getInternalLaneID() const
get ID of internal lnae
Definition: NBEdge.cpp:82
bool hasString(const std::string &str) const
SUMOReal y() const
Returns the y-position.
Definition: Position.h:68
static int getID(const std::string &origID, StringBijection< int > &map, int &lastID)
SUMOReal getLaneWidth() const
Returns the default width of lanes of this edge.
Definition: NBEdge.h:505
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:416
static void writeElevationProfile(const PositionVector &shape, OutputDevice &device, const OutputDevice_String &elevationDevice)
SUMOReal getLength() const
Returns the computed length of the edge.
Definition: NBEdge.h:463
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
An output device that encapsulates an ofstream.