Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2002-2024 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file RONetHandler.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Christian Roessel
18 : /// @author Michael Behrisch
19 : /// @author Yun-Pang Floetteroed
20 : /// @date Sept 2002
21 : ///
22 : // The handler for SUMO-Networks
23 : /****************************************************************************/
24 : #include <config.h>
25 :
26 : #include <string>
27 : #include <utils/common/MsgHandler.h>
28 : #include <utils/common/StringTokenizer.h>
29 : #include <utils/common/UtilExceptions.h>
30 : #include <utils/common/ToString.h>
31 : #include <utils/common/StringUtils.h>
32 : #include <utils/geom/PositionVector.h>
33 : #include <utils/geom/GeoConvHelper.h>
34 : #include <utils/vehicle/SUMORouteHandler.h>
35 : #include <utils/xml/SUMOSAXHandler.h>
36 : #include <utils/xml/SUMOXMLDefinitions.h>
37 : #include "ROEdge.h"
38 : #include "ROLane.h"
39 : #include "RONode.h"
40 : #include "RONet.h"
41 : #include "RONetHandler.h"
42 : #include "ROAbstractEdgeBuilder.h"
43 :
44 :
45 : // ===========================================================================
46 : // method definitions
47 : // ===========================================================================
48 10018 : RONetHandler::RONetHandler(RONet& net, ROAbstractEdgeBuilder& eb, const bool ignoreInternal, const double minorPenalty, double tlsPenalty, double turnaroundPenalty) :
49 : SUMOSAXHandler("sumo-network"),
50 10018 : myNet(net),
51 : myNetworkVersion(0, 0),
52 10018 : myEdgeBuilder(eb), myIgnoreInternal(ignoreInternal),
53 10018 : myCurrentName(), myCurrentEdge(nullptr), myCurrentStoppingPlace(nullptr),
54 10018 : myMinorPenalty(minorPenalty),
55 10018 : myTLSPenalty(tlsPenalty),
56 20036 : myTurnaroundPenalty(turnaroundPenalty)
57 10018 : {}
58 :
59 :
60 20036 : RONetHandler::~RONetHandler() {}
61 :
62 :
63 : void
64 2359592 : RONetHandler::myStartElement(int element,
65 : const SUMOSAXAttributes& attrs) {
66 2359592 : switch (element) {
67 10018 : case SUMO_TAG_LOCATION:
68 10018 : setLocation(attrs);
69 10018 : break;
70 10018 : case SUMO_TAG_NET: {
71 : bool ok;
72 20036 : myNetworkVersion = StringUtils::toVersion(attrs.get<std::string>(SUMO_ATTR_VERSION, nullptr, ok, false));
73 : break;
74 : }
75 451220 : case SUMO_TAG_EDGE:
76 : // in the first step, we do need the name to allocate the edge
77 : // in the second, we need it to know to which edge we have to add
78 : // the following edges to
79 451220 : parseEdge(attrs);
80 451208 : break;
81 601658 : case SUMO_TAG_LANE:
82 601658 : parseLane(attrs);
83 601658 : break;
84 149248 : case SUMO_TAG_JUNCTION:
85 149248 : parseJunction(attrs);
86 149248 : break;
87 718377 : case SUMO_TAG_CONNECTION:
88 718377 : parseConnection(attrs);
89 718059 : break;
90 1590 : case SUMO_TAG_BUS_STOP:
91 : case SUMO_TAG_TRAIN_STOP:
92 : case SUMO_TAG_CONTAINER_STOP:
93 : case SUMO_TAG_PARKING_AREA:
94 : case SUMO_TAG_CHARGING_STATION:
95 : case SUMO_TAG_OVERHEAD_WIRE_SEGMENT:
96 1590 : parseStoppingPlace(attrs, (SumoXMLTag)element);
97 1590 : break;
98 1088 : case SUMO_TAG_ACCESS:
99 1088 : parseAccess(attrs);
100 1088 : break;
101 12631 : case SUMO_TAG_TAZ:
102 12631 : parseDistrict(attrs);
103 12631 : break;
104 116 : case SUMO_TAG_TAZSOURCE:
105 116 : parseDistrictEdge(attrs, true);
106 116 : break;
107 117 : case SUMO_TAG_TAZSINK:
108 117 : parseDistrictEdge(attrs, false);
109 117 : break;
110 1782 : case SUMO_TAG_TYPE: {
111 1782 : bool ok = true;
112 3564 : myCurrentTypeID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
113 : break;
114 : }
115 1 : case SUMO_TAG_RESTRICTION: {
116 1 : bool ok = true;
117 2 : const SUMOVehicleClass svc = getVehicleClassID(attrs.get<std::string>(SUMO_ATTR_VCLASS, myCurrentTypeID.c_str(), ok));
118 1 : const double speed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentTypeID.c_str(), ok);
119 1 : if (ok) {
120 1 : myNet.addRestriction(myCurrentTypeID, svc, speed);
121 : }
122 : break;
123 : }
124 52055 : case SUMO_TAG_PARAM:
125 52055 : addParam(attrs);
126 52055 : break;
127 : default:
128 : break;
129 : }
130 2359262 : }
131 :
132 :
133 : void
134 2358497 : RONetHandler::myEndElement(int element) {
135 2358497 : switch (element) {
136 9433 : case SUMO_TAG_NET:
137 : // build junction graph
138 9511 : for (std::set<std::string>::const_iterator it = myUnseenNodeIDs.begin(); it != myUnseenNodeIDs.end(); ++it) {
139 234 : WRITE_ERRORF(TL("Unknown node '%'."), *it);
140 : }
141 : break;
142 : default:
143 : break;
144 : }
145 2358497 : }
146 :
147 :
148 : void
149 52055 : RONetHandler::addParam(const SUMOSAXAttributes& attrs) {
150 52055 : bool ok = true;
151 52055 : const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
152 : // circumventing empty string test
153 52055 : const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
154 : // add parameter in current created element, or in myLoadedParameterised
155 52055 : if (myCurrentEdge != nullptr) {
156 52055 : myCurrentEdge->setParameter(key, val);
157 : }
158 52055 : }
159 :
160 :
161 : void
162 451220 : RONetHandler::parseEdge(const SUMOSAXAttributes& attrs) {
163 : // get the id, report an error if not given or empty...
164 451220 : bool ok = true;
165 451220 : myCurrentName = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
166 451220 : if (!ok) {
167 12 : throw ProcessError();
168 : }
169 451208 : const SumoXMLEdgeFunc func = attrs.getOpt<SumoXMLEdgeFunc>(SUMO_ATTR_FUNCTION, myCurrentName.c_str(), ok, SumoXMLEdgeFunc::NORMAL);
170 451208 : if (!ok) {
171 54 : return;
172 : }
173 : // get the edge
174 : std::string from;
175 : std::string to;
176 : int priority;
177 451202 : myCurrentEdge = nullptr;
178 451202 : if (func == SumoXMLEdgeFunc::INTERNAL || func == SumoXMLEdgeFunc::CROSSING || func == SumoXMLEdgeFunc::WALKINGAREA) {
179 : assert(myCurrentName[0] == ':');
180 598874 : const std::string junctionID = SUMOXMLDefinitions::getJunctionIDFromInternalEdge(myCurrentName);
181 : from = junctionID;
182 : to = junctionID;
183 : priority = -1;
184 299437 : } else {
185 303530 : from = attrs.get<std::string>(SUMO_ATTR_FROM, myCurrentName.c_str(), ok);
186 303530 : to = attrs.get<std::string>(SUMO_ATTR_TO, myCurrentName.c_str(), ok);
187 151765 : priority = attrs.get<int>(SUMO_ATTR_PRIORITY, myCurrentName.c_str(), ok);
188 151765 : if (!ok) {
189 : return;
190 : }
191 : }
192 451154 : RONode* fromNode = myNet.getNode(from);
193 362691 : if (fromNode == nullptr) {
194 : myUnseenNodeIDs.insert(from);
195 88463 : fromNode = new RONode(from);
196 88463 : myNet.addNode(fromNode);
197 : }
198 451154 : RONode* toNode = myNet.getNode(to);
199 432439 : if (toNode == nullptr) {
200 : myUnseenNodeIDs.insert(to);
201 18715 : toNode = new RONode(to);
202 18715 : myNet.addNode(toNode);
203 : }
204 : // build the edge
205 451154 : myCurrentEdge = myEdgeBuilder.buildEdge(myCurrentName, fromNode, toNode, priority);
206 : // set the type
207 451154 : myCurrentEdge->setRestrictions(myNet.getRestrictions(attrs.getOpt<std::string>(SUMO_ATTR_TYPE, myCurrentName.c_str(), ok, "")));
208 451154 : myCurrentEdge->setFunction(func);
209 :
210 451154 : if (myNet.addEdge(myCurrentEdge)) {
211 451142 : fromNode->addOutgoing(myCurrentEdge);
212 451142 : toNode->addIncoming(myCurrentEdge);
213 902284 : const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, myCurrentName.c_str(), ok, "");
214 451142 : if (bidi != "") {
215 6444 : myBidiEdges[myCurrentEdge] = bidi;
216 : }
217 : } else {
218 12 : myCurrentEdge = nullptr;
219 : }
220 : }
221 :
222 :
223 : void
224 601658 : RONetHandler::parseLane(const SUMOSAXAttributes& attrs) {
225 601658 : if (myCurrentEdge == nullptr) {
226 : // was an internal edge to skip or an error occurred
227 282 : return;
228 : }
229 601532 : bool ok = true;
230 : // get the id, report an error if not given or empty...
231 601532 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
232 601532 : if (!ok) {
233 : return;
234 : }
235 : // get the speed
236 601508 : double maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
237 601508 : double length = attrs.get<double>(SUMO_ATTR_LENGTH, id.c_str(), ok);
238 601508 : std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, id.c_str(), ok, "");
239 1203016 : std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
240 601508 : const PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
241 601508 : if (!ok) {
242 : return;
243 : }
244 601400 : if (shape.size() < 2) {
245 72 : WRITE_ERRORF(TL("Ignoring lane '%' with broken shape."), id);
246 24 : return;
247 : }
248 : // get the length
249 : // get the vehicle classes
250 601376 : SVCPermissions permissions = parseVehicleClasses(allow, disallow, myNetworkVersion);
251 601376 : if (permissions != SVCAll) {
252 139172 : myNet.setPermissionsFound();
253 : }
254 : // add when both values are valid
255 601376 : if (maxSpeed > 0 && length > 0 && id.length() > 0) {
256 601376 : myCurrentEdge->addLane(new ROLane(id, myCurrentEdge, length, maxSpeed, permissions, shape));
257 : } else {
258 0 : WRITE_WARNING("Ignoring lane '" + id + "' with speed " + toString(maxSpeed) + " and length " + toString(length));
259 : }
260 601508 : }
261 :
262 :
263 : void
264 149248 : RONetHandler::parseJunction(const SUMOSAXAttributes& attrs) {
265 149248 : bool ok = true;
266 : // get the id, report an error if not given or empty...
267 149248 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
268 149248 : const SumoXMLNodeType type = attrs.get<SumoXMLNodeType>(SUMO_ATTR_TYPE, id.c_str(), ok);
269 149248 : if (type == SumoXMLNodeType::INTERNAL) {
270 : return;
271 : }
272 : myUnseenNodeIDs.erase(id);
273 : // get the position of the node
274 106670 : const double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
275 106670 : const double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
276 106670 : const double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0.);
277 106670 : if (!ok) {
278 : return;
279 : }
280 106625 : RONode* n = myNet.getNode(id);
281 106521 : if (n == nullptr) {
282 312 : WRITE_WARNINGF(TL("Skipping isolated junction '%'."), id);
283 : } else {
284 106521 : n->setPosition(Position(x, y, z));
285 : }
286 : }
287 :
288 :
289 : void
290 718377 : RONetHandler::parseConnection(const SUMOSAXAttributes& attrs) {
291 718377 : bool ok = true;
292 718377 : std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
293 718377 : std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
294 718377 : const int fromLane = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
295 718377 : const int toLane = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
296 718377 : std::string dir = attrs.get<std::string>(SUMO_ATTR_DIR, nullptr, ok);
297 718695 : std::string viaID = attrs.getOpt<std::string>(SUMO_ATTR_VIA, nullptr, ok, "");
298 718695 : std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
299 718377 : ROEdge* from = myNet.getEdge(fromID);
300 : ROEdge* to = myNet.getEdge(toID);
301 718377 : if (from == nullptr) {
302 207 : throw ProcessError(TLF("unknown from-edge '%' in connection", fromID));
303 : }
304 718308 : if (to == nullptr) {
305 207 : throw ProcessError(TLF("unknown to-edge '%' in connection", toID));
306 : }
307 718239 : if ((int)from->getLanes().size() <= fromLane) {
308 180 : throw ProcessError("invalid fromLane '" + toString(fromLane) + "' in connection from '" + fromID + "'.");
309 : }
310 718149 : if ((int)to->getLanes().size() <= toLane) {
311 180 : throw ProcessError("invalid toLane '" + toString(toLane) + "' in connection to '" + toID + "'.");
312 : }
313 718059 : if (myIgnoreInternal || viaID == "") {
314 395286 : std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
315 395286 : std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
316 : ROEdge* dummyVia = nullptr;
317 : SVCPermissions permissions;
318 395286 : if (allow == "" && disallow == "") {
319 : permissions = SVC_UNSPECIFIED;
320 : } else {
321 13 : myNet.setPermissionsFound();
322 : // dummyVia is only needed to hold permissions
323 13 : permissions = parseVehicleClasses(allow, disallow);
324 26 : dummyVia = new ROEdge("dummyVia_" + from->getLanes()[fromLane]->getID() + "->" + to->getLanes()[toLane]->getID(),
325 26 : from->getToJunction(), from->getToJunction(), permissions);
326 : }
327 395286 : from->getLanes()[fromLane]->addOutgoingLane(to->getLanes()[toLane], dummyVia);
328 1185858 : from->addSuccessor(to, nullptr, dir);
329 : } else {
330 322773 : ROEdge* const via = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(viaID));
331 322773 : if (via == nullptr) {
332 0 : throw ProcessError(TLF("unknown via-edge '%' in connection", viaID));
333 : }
334 322773 : from->getLanes()[fromLane]->addOutgoingLane(to->getLanes()[toLane], via);
335 645546 : from->addSuccessor(to, via, dir);
336 645546 : via->addSuccessor(to, nullptr, dir);
337 322773 : LinkState state = SUMOXMLDefinitions::LinkStates.get(attrs.get<std::string>(SUMO_ATTR_STATE, nullptr, ok));
338 322773 : if (state == LINKSTATE_MINOR || state == LINKSTATE_EQUAL || state == LINKSTATE_STOP || state == LINKSTATE_ALLWAY_STOP) {
339 159171 : via->setTimePenalty(myMinorPenalty);
340 : }
341 573865 : if (dir == toString(LinkDirection::TURN) || dir == toString(LinkDirection::TURN_LEFTHAND)) {
342 71999 : via->setTimePenalty(myTurnaroundPenalty);
343 : }
344 322773 : if (tlID != "") {
345 7747 : via->setTimePenalty(myTLSPenalty);
346 : }
347 : }
348 718059 : if (to->isCrossing()) {
349 6226 : to->setTimePenalty(myTLSPenalty);
350 : }
351 718059 : }
352 :
353 :
354 : void
355 1590 : RONetHandler::parseStoppingPlace(const SUMOSAXAttributes& attrs, const SumoXMLTag element) {
356 1590 : bool ok = true;
357 1590 : myCurrentStoppingPlace = new SUMOVehicleParameter::Stop();
358 : // get the id, throw if not given or empty...
359 1590 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, toString(element).c_str(), ok);
360 : // get the lane
361 1590 : myCurrentStoppingPlace->lane = attrs.get<std::string>(SUMO_ATTR_LANE, toString(element).c_str(), ok);
362 1590 : if (!ok) {
363 0 : throw ProcessError();
364 : }
365 1590 : const ROEdge* edge = myNet.getEdgeForLaneID(myCurrentStoppingPlace->lane);
366 1590 : if (edge == nullptr) {
367 0 : throw InvalidArgument("Unknown lane '" + myCurrentStoppingPlace->lane + "' for " + toString(element) + " '" + id + "'.");
368 : }
369 : // get the positions
370 1590 : myCurrentStoppingPlace->startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0.);
371 1590 : myCurrentStoppingPlace->endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, edge->getLength());
372 1590 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
373 1590 : if (!ok || (SUMORouteHandler::checkStopPos(myCurrentStoppingPlace->startPos, myCurrentStoppingPlace->endPos, edge->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
374 0 : throw InvalidArgument("Invalid position for " + toString(element) + " '" + id + "'.");
375 : }
376 : // this is a hack: the busstop attribute is meant to hold the id within the simulation context but this is not used within the router context
377 1590 : myCurrentStoppingPlace->busstop = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
378 : // this is a hack: the actType is not used when using this to encode a stopping place
379 1590 : myCurrentStoppingPlace->actType = toString(element);
380 1590 : myNet.addStoppingPlace(id, element, myCurrentStoppingPlace);
381 1590 : }
382 :
383 :
384 : void
385 1088 : RONetHandler::parseAccess(const SUMOSAXAttributes& attrs) {
386 1088 : bool ok = true;
387 1088 : const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, "access", ok);
388 1088 : const ROEdge* edge = myNet.getEdgeForLaneID(lane);
389 1088 : if (edge == nullptr) {
390 0 : throw InvalidArgument("Unknown lane '" + lane + "' for access.");
391 : }
392 1088 : if ((edge->getPermissions() & SVC_PEDESTRIAN) == 0) {
393 0 : WRITE_WARNINGF(TL("Ignoring invalid access from non-pedestrian edge '%'."), edge->getID());
394 : return;
395 : }
396 1088 : const bool random = attrs.getOpt<std::string>(SUMO_ATTR_POSITION, "access", ok) == "random";
397 1088 : double pos = random ? edge->getLength() / 2. : attrs.getOpt<double>(SUMO_ATTR_POSITION, "access", ok, 0.);
398 1088 : double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "access", ok, -1);
399 1088 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, "access", ok, false);
400 1088 : if (!ok || (SUMORouteHandler::checkStopPos(pos, pos, edge->getLength(), 0., friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
401 0 : throw InvalidArgument("Invalid position " + toString(pos) + " for access on lane '" + lane + "'.");
402 : }
403 : if (!ok) {
404 : throw ProcessError();
405 : }
406 1088 : if (length < 0) {
407 568 : const Position accPos = myNet.getLane(lane)->geometryPositionAtOffset(pos);
408 568 : const double stopCenter = (myCurrentStoppingPlace->startPos + myCurrentStoppingPlace->endPos) / 2;
409 568 : const Position stopPos = myNet.getLane(myCurrentStoppingPlace->lane)->geometryPositionAtOffset(stopCenter);
410 568 : length = accPos.distanceTo(stopPos);
411 : }
412 2176 : myCurrentStoppingPlace->accessPos.push_back(std::make_tuple(lane, pos, length));
413 : }
414 :
415 :
416 : void
417 12631 : RONetHandler::parseDistrict(const SUMOSAXAttributes& attrs) {
418 12631 : myCurrentEdge = nullptr;
419 12631 : bool ok = true;
420 12631 : myCurrentName = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
421 12631 : if (!ok) {
422 0 : return;
423 : }
424 12631 : ROEdge* const sink = myEdgeBuilder.buildEdge(myCurrentName + "-sink", nullptr, nullptr, 0);
425 12631 : ROEdge* const source = myEdgeBuilder.buildEdge(myCurrentName + "-source", nullptr, nullptr, 0);
426 12631 : myNet.addDistrict(myCurrentName, source, sink);
427 12631 : if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
428 12502 : const std::vector<std::string>& desc = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, myCurrentName.c_str(), ok);
429 31434 : for (const std::string& eID : desc) {
430 56796 : myNet.addDistrictEdge(myCurrentName, eID, true);
431 56796 : myNet.addDistrictEdge(myCurrentName, eID, false);
432 : }
433 12502 : }
434 : }
435 :
436 :
437 : void
438 233 : RONetHandler::parseDistrictEdge(const SUMOSAXAttributes& attrs, bool isSource) {
439 233 : bool ok = true;
440 233 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, myCurrentName.c_str(), ok);
441 699 : myNet.addDistrictEdge(myCurrentName, id, isSource);
442 233 : }
443 :
444 : void
445 10018 : RONetHandler::setLocation(const SUMOSAXAttributes& attrs) {
446 10018 : bool ok = true;
447 10018 : PositionVector s = attrs.get<PositionVector>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
448 10018 : Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
449 10018 : Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
450 10018 : std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
451 10018 : if (ok) {
452 10018 : Position networkOffset = s[0];
453 10018 : GeoConvHelper::init(proj, networkOffset, origBoundary, convBoundary);
454 : }
455 10018 : }
456 :
457 :
458 : /****************************************************************************/
|