Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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 RORouteHandler.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Sascha Krieg
18 : /// @author Michael Behrisch
19 : /// @date Mon, 9 Jul 2001
20 : ///
21 : // Parser and container for routes during their loading
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <string>
26 : #include <map>
27 : #include <vector>
28 : #include <iostream>
29 : #include <utils/iodevices/OutputDevice.h>
30 : #include <utils/xml/SUMOSAXHandler.h>
31 : #include <utils/xml/SUMOXMLDefinitions.h>
32 : #include <utils/geom/GeoConvHelper.h>
33 : #include <utils/common/FileHelpers.h>
34 : #include <utils/common/MsgHandler.h>
35 : #include <utils/common/StringTokenizer.h>
36 : #include <utils/common/UtilExceptions.h>
37 : #include <utils/options/OptionsCont.h>
38 : #include <utils/vehicle/SUMOVehicleParserHelper.h>
39 : #include <utils/xml/SUMOSAXReader.h>
40 : #include <utils/xml/XMLSubSys.h>
41 : #include <utils/iodevices/OutputDevice_String.h>
42 : #include "RONet.h"
43 : #include "ROEdge.h"
44 : #include "ROLane.h"
45 : #include "RORouteDef.h"
46 : #include "RORouteHandler.h"
47 :
48 : // ===========================================================================
49 : // method definitions
50 : // ===========================================================================
51 3797 : RORouteHandler::RORouteHandler(RONet& net, const std::string& file,
52 : const bool tryRepair,
53 : const bool emptyDestinationsAllowed,
54 : const bool ignoreErrors,
55 3797 : const bool checkSchema) :
56 : SUMORouteHandler(file, checkSchema ? "routes" : "", true),
57 7594 : MapMatcher(OptionsCont::getOptions().getBool("mapmatch.junctions"),
58 3797 : OptionsCont::getOptions().getFloat("mapmatch.distance"),
59 3797 : ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
60 3797 : myNet(net),
61 3797 : myActiveRouteRepeat(0),
62 3797 : myActiveRoutePeriod(0),
63 3797 : myActivePlan(nullptr),
64 3797 : myActiveContainerPlan(nullptr),
65 3797 : myActiveContainerPlanSize(0),
66 3797 : myTryRepair(tryRepair),
67 3797 : myEmptyDestinationsAllowed(emptyDestinationsAllowed),
68 3797 : myErrorOutput(ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
69 3797 : myBegin(string2time(OptionsCont::getOptions().getString("begin"))),
70 3797 : myKeepVTypeDist(OptionsCont::getOptions().getBool("keep-vtype-distributions")),
71 11332 : myUnsortedInput(OptionsCont::getOptions().exists("unsorted-input") && OptionsCont::getOptions().getBool("unsorted-input")),
72 3797 : myCurrentVTypeDistribution(nullptr),
73 15882 : myCurrentAlternatives(nullptr) {
74 3797 : myActiveRoute.reserve(100);
75 3797 : }
76 :
77 :
78 7594 : RORouteHandler::~RORouteHandler() {
79 3797 : delete myCurrentAlternatives;
80 7594 : }
81 :
82 :
83 : void
84 352 : RORouteHandler::deleteActivePlanAndVehicleParameter() {
85 352 : if (myActivePlan != nullptr) {
86 4 : for (ROPerson::PlanItem* const it : *myActivePlan) {
87 0 : delete it;
88 : }
89 8 : delete myActivePlan;
90 4 : myActivePlan = nullptr;
91 : }
92 352 : delete myActiveContainerPlan;
93 352 : myActiveContainerPlan = nullptr;
94 352 : delete myVehicleParameter;
95 352 : myVehicleParameter = nullptr;
96 352 : }
97 :
98 :
99 : void
100 65436 : RORouteHandler::parseFromViaTo(SumoXMLTag tag, const SUMOSAXAttributes& attrs, bool& ok) {
101 65436 : const std::string element = toString(tag);
102 65436 : myActiveRoute.clear();
103 65436 : bool useTaz = OptionsCont::getOptions().getBool("with-taz");
104 65436 : if (useTaz && !myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) && !myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
105 0 : WRITE_WARNINGF(TL("Taz usage was requested but no taz present in % '%'!"), element, myVehicleParameter->id);
106 : useTaz = false;
107 : }
108 : SUMOVehicleClass vClass = SVC_PASSENGER;
109 65436 : if (!myNet.getVTypeDistribution(myVehicleParameter->vtypeid)) {
110 65404 : SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
111 65404 : if (type != nullptr) {
112 65330 : vClass = type->vehicleClass;
113 : }
114 : }
115 : // from-attributes
116 130872 : const std::string rid = "for " + element + " '" + myVehicleParameter->id + "'";
117 88198 : if ((useTaz || !attrs.hasAttribute(SUMO_ATTR_FROM)) &&
118 45490 : (attrs.hasAttribute(SUMO_ATTR_FROM_TAZ) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION))) {
119 21926 : const bool useJunction = attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION);
120 21960 : const std::string tazType = useJunction ? "junction" : "taz";
121 21960 : const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_FROM_JUNCTION : SUMO_ATTR_FROM_TAZ, myVehicleParameter->id.c_str(), ok, true);
122 43852 : const ROEdge* fromTaz = myNet.getEdge(tazID + "-source");
123 21926 : if (fromTaz == nullptr) {
124 24 : myErrorOutput->inform("Source " + tazType + " '" + tazID + "' not known for " + element + " '" + myVehicleParameter->id + "'!"
125 24 : + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
126 8 : ok = false;
127 21918 : } else if (fromTaz->getNumSuccessors() == 0 && tag != SUMO_TAG_PERSON) {
128 0 : myErrorOutput->inform("Source " + tazType + " '" + tazID + "' has no outgoing edges for " + element + " '" + myVehicleParameter->id + "'!");
129 0 : ok = false;
130 : } else {
131 21918 : myActiveRoute.push_back(fromTaz);
132 : }
133 43510 : } else if (attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
134 76 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMXY, myVehicleParameter->id.c_str(), ok), false, vClass, myActiveRoute, rid, true, ok);
135 43434 : } else if (attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
136 20 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMLONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, myActiveRoute, rid, true, ok);
137 : } else {
138 86828 : parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok), myActiveRoute, rid, ok);
139 : }
140 65436 : if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_VIALONLAT) && !attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
141 50812 : myInsertStopEdgesAt = (int)myActiveRoute.size();
142 : }
143 :
144 : // via-attributes
145 : ConstROEdgeVector viaEdges;
146 65436 : if (attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
147 28 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIAXY, myVehicleParameter->id.c_str(), ok), false, vClass, viaEdges, rid, false, ok);
148 65408 : } else if (attrs.hasAttribute(SUMO_ATTR_VIALONLAT)) {
149 4 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIALONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, viaEdges, rid, false, ok);
150 65404 : } else if (attrs.hasAttribute(SUMO_ATTR_VIAJUNCTIONS)) {
151 24 : for (std::string junctionID : attrs.get<std::vector<std::string> >(SUMO_ATTR_VIAJUNCTIONS, myVehicleParameter->id.c_str(), ok)) {
152 32 : const ROEdge* viaSink = myNet.getEdge(junctionID + "-sink");
153 16 : if (viaSink == nullptr) {
154 0 : myErrorOutput->inform("Junction-taz '" + junctionID + "' not found." + JUNCTION_TAZ_MISSING_HELP);
155 0 : ok = false;
156 : } else {
157 16 : viaEdges.push_back(viaSink);
158 : }
159 8 : }
160 : } else {
161 130792 : parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok, "", true), viaEdges, rid, ok);
162 : }
163 99006 : for (const ROEdge* e : viaEdges) {
164 33570 : myActiveRoute.push_back(e);
165 33570 : myVehicleParameter->via.push_back(e->getID());
166 : }
167 :
168 : // to-attributes
169 89204 : if ((useTaz || !attrs.hasAttribute(SUMO_ATTR_TO)) &&
170 47502 : (attrs.hasAttribute(SUMO_ATTR_TO_TAZ) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION))) {
171 21918 : const bool useJunction = attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION);
172 21952 : const std::string tazType = useJunction ? "junction" : "taz";
173 21952 : const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_TO_JUNCTION : SUMO_ATTR_TO_TAZ, myVehicleParameter->id.c_str(), ok, true);
174 43836 : const ROEdge* toTaz = myNet.getEdge(tazID + "-sink");
175 21918 : if (toTaz == nullptr) {
176 0 : myErrorOutput->inform("Sink " + tazType + " '" + tazID + "' not known for " + element + " '" + myVehicleParameter->id + "'!"
177 0 : + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
178 0 : ok = false;
179 21918 : } else if (toTaz->getNumPredecessors() == 0 && tag != SUMO_TAG_PERSON) {
180 0 : myErrorOutput->inform("Sink " + tazType + " '" + tazID + "' has no incoming edges for " + element + " '" + myVehicleParameter->id + "'!");
181 0 : ok = false;
182 : } else {
183 21918 : myActiveRoute.push_back(toTaz);
184 : }
185 43518 : } else if (attrs.hasAttribute(SUMO_ATTR_TOXY)) {
186 76 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOXY, myVehicleParameter->id.c_str(), ok, true), false, vClass, myActiveRoute, rid, false, ok);
187 43442 : } else if (attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
188 20 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOLONLAT, myVehicleParameter->id.c_str(), ok, true), true, vClass, myActiveRoute, rid, false, ok);
189 : } else {
190 86844 : parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true), myActiveRoute, rid, ok);
191 : }
192 65436 : myActiveRouteID = "!" + myVehicleParameter->id;
193 65436 : if (myVehicleParameter->routeid == "") {
194 64965 : myVehicleParameter->routeid = myActiveRouteID;
195 : }
196 65436 : }
197 :
198 :
199 : void
200 900137 : RORouteHandler::myStartElement(int element,
201 : const SUMOSAXAttributes& attrs) {
202 : try {
203 900137 : if (myActivePlan != nullptr && myActivePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED && element != SUMO_TAG_RIDE && element != SUMO_TAG_PARAM) {
204 16 : throw ProcessError(TLF("Triggered departure for person '%' requires starting with a ride.", myVehicleParameter->id));
205 900133 : } else if (myActiveContainerPlan != nullptr && myActiveContainerPlanSize == 0 && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED && element != SUMO_TAG_TRANSPORT && element != SUMO_TAG_PARAM) {
206 16 : throw ProcessError(TLF("Triggered departure for container '%' requires starting with a transport.", myVehicleParameter->id));
207 : }
208 900129 : SUMORouteHandler::myStartElement(element, attrs);
209 899789 : bool ok = true;
210 899789 : switch (element) {
211 1790 : case SUMO_TAG_PERSON:
212 : case SUMO_TAG_PERSONFLOW: {
213 1790 : myActivePlan = new std::vector<ROPerson::PlanItem*>();
214 1790 : break;
215 : }
216 : case SUMO_TAG_RIDE:
217 : break; // handled in addRide, called from SUMORouteHandler::myStartElement
218 64 : case SUMO_TAG_CONTAINER:
219 : case SUMO_TAG_CONTAINERFLOW:
220 64 : myActiveContainerPlan = new OutputDevice_String(1);
221 64 : myActiveContainerPlanSize = 0;
222 64 : myActiveContainerPlan->openTag((SumoXMLTag)element);
223 64 : (*myActiveContainerPlan) << attrs;
224 : break;
225 60 : case SUMO_TAG_TRANSPORT:
226 : case SUMO_TAG_TRANSHIP:
227 60 : if (myActiveContainerPlan == nullptr) {
228 12 : throw ProcessError(TLF("Found % outside container element", toString((SumoXMLTag)element)));
229 : }
230 : // copy container elements
231 56 : myActiveContainerPlan->openTag((SumoXMLTag)element);
232 56 : (*myActiveContainerPlan) << attrs;
233 56 : myActiveContainerPlan->closeTag();
234 56 : myActiveContainerPlanSize++;
235 56 : break;
236 1960 : case SUMO_TAG_FLOW:
237 1960 : myActiveRouteProbability = DEFAULT_VEH_PROB;
238 1960 : parseFromViaTo((SumoXMLTag)element, attrs, ok);
239 : break;
240 61746 : case SUMO_TAG_TRIP:
241 61746 : myActiveRouteProbability = DEFAULT_VEH_PROB;
242 61746 : parseFromViaTo((SumoXMLTag)element, attrs, ok);
243 : break;
244 : default:
245 : break;
246 : }
247 352 : } catch (ProcessError&) {
248 352 : deleteActivePlanAndVehicleParameter();
249 352 : throw;
250 352 : }
251 899785 : }
252 :
253 :
254 : void
255 52 : RORouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
256 52 : bool ok = true;
257 52 : myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
258 52 : if (ok) {
259 52 : myCurrentVTypeDistribution = new RandomDistributor<SUMOVTypeParameter*>();
260 52 : if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
261 : std::vector<double> probs;
262 36 : if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
263 8 : StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentVTypeDistributionID.c_str(), ok));
264 24 : while (st.hasNext()) {
265 16 : probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
266 : }
267 8 : }
268 36 : const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
269 36 : StringTokenizer st(vTypes);
270 36 : int probIndex = 0;
271 84 : while (st.hasNext()) {
272 48 : const std::string typeID = st.next();
273 48 : const RandomDistributor<SUMOVTypeParameter*>* const dist = myNet.getVTypeDistribution(typeID);
274 8 : if (dist != nullptr) {
275 8 : const double distProb = ((int)probs.size() > probIndex ? probs[probIndex] : 1.) / dist->getOverallProb();
276 : std::vector<double>::const_iterator probIt = dist->getProbs().begin();
277 24 : for (SUMOVTypeParameter* const type : dist->getVals()) {
278 16 : myCurrentVTypeDistribution->add(type, distProb * *probIt);
279 : probIt++;
280 : }
281 : } else {
282 40 : SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(typeID);
283 40 : if (type == nullptr) {
284 16 : myErrorOutput->inform("Unknown vehicle type '" + typeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
285 : } else {
286 32 : const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : type->defaultProbability);
287 32 : myCurrentVTypeDistribution->add(type, prob);
288 : }
289 : }
290 48 : probIndex++;
291 : }
292 36 : if (probs.size() > 0 && probIndex != (int)probs.size()) {
293 0 : WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
294 : " types in vTypeDistribution '" + myCurrentVTypeDistributionID + "'");
295 : }
296 36 : }
297 : }
298 52 : }
299 :
300 :
301 : void
302 52 : RORouteHandler::closeVehicleTypeDistribution() {
303 52 : if (myCurrentVTypeDistribution != nullptr) {
304 52 : if (myCurrentVTypeDistribution->getOverallProb() == 0) {
305 8 : delete myCurrentVTypeDistribution;
306 16 : myErrorOutput->inform("Vehicle type distribution '" + myCurrentVTypeDistributionID + "' is empty.");
307 44 : } else if (!myNet.addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
308 0 : delete myCurrentVTypeDistribution;
309 0 : myErrorOutput->inform("Another vehicle type (or distribution) with the id '" + myCurrentVTypeDistributionID + "' exists.");
310 : }
311 52 : myCurrentVTypeDistribution = nullptr;
312 : }
313 52 : }
314 :
315 :
316 : void
317 461962 : RORouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
318 461962 : myActiveRoute.clear();
319 461962 : myInsertStopEdgesAt = -1;
320 : // check whether the id is really necessary
321 : std::string rid;
322 461962 : if (myCurrentAlternatives != nullptr) {
323 459776 : myActiveRouteID = myCurrentAlternatives->getID();
324 919552 : rid = "distribution '" + myCurrentAlternatives->getID() + "'";
325 2186 : } else if (myVehicleParameter != nullptr) {
326 : // ok, a vehicle is wrapping the route,
327 : // we may use this vehicle's id as default
328 1343 : myVehicleParameter->routeid = myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
329 1343 : if (attrs.hasAttribute(SUMO_ATTR_ID)) {
330 0 : WRITE_WARNINGF(TL("Ids of internal routes are ignored (vehicle '%')."), myVehicleParameter->id);
331 : }
332 : } else {
333 843 : bool ok = true;
334 843 : myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
335 843 : if (!ok) {
336 16 : return;
337 : }
338 1654 : rid = "'" + myActiveRouteID + "'";
339 : }
340 461946 : if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
341 920198 : rid = "for vehicle '" + myVehicleParameter->id + "'";
342 : }
343 461946 : bool ok = true;
344 461946 : if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
345 923876 : parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid, ok);
346 : }
347 923892 : myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
348 461946 : if (myActiveRouteRefID != "" && myNet.getRouteDef(myActiveRouteRefID) == nullptr) {
349 0 : myErrorOutput->inform("Invalid reference to route '" + myActiveRouteRefID + "' in route " + rid + ".");
350 : }
351 461946 : if (myCurrentAlternatives != nullptr && !attrs.hasAttribute(SUMO_ATTR_PROB)) {
352 60 : WRITE_WARNINGF(TL("No probability for route %, using default."), rid);
353 : }
354 461946 : myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
355 461946 : if (ok && myActiveRouteProbability < 0) {
356 16 : myErrorOutput->inform("Invalid probability for route '" + myActiveRouteID + "'.");
357 : }
358 461946 : myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
359 461946 : ok = true;
360 461946 : myActiveRouteRepeat = attrs.getOpt<int>(SUMO_ATTR_REPEAT, myActiveRouteID.c_str(), ok, 0);
361 461946 : myActiveRoutePeriod = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CYCLETIME, myActiveRouteID.c_str(), ok, 0);
362 461946 : if (myActiveRouteRepeat > 0) {
363 : SUMOVehicleClass vClass = SVC_IGNORING;
364 8 : if (myVehicleParameter != nullptr) {
365 0 : SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
366 0 : if (type != nullptr) {
367 0 : vClass = type->vehicleClass;
368 : }
369 : }
370 8 : if (myActiveRoute.size() > 0 && !myActiveRoute.back()->isConnectedTo(*myActiveRoute.front(), vClass)) {
371 0 : myErrorOutput->inform("Disconnected route " + rid + " when repeating.");
372 : }
373 : }
374 461946 : myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
375 461946 : if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
376 16 : myErrorOutput->inform("Invalid cost for route '" + myActiveRouteID + "'.");
377 : }
378 : }
379 :
380 :
381 : void
382 1521 : RORouteHandler::openFlow(const SUMOSAXAttributes& /*attrs*/) {
383 : // currently unused
384 1521 : }
385 :
386 :
387 : void
388 439 : RORouteHandler::openRouteFlow(const SUMOSAXAttributes& /*attrs*/) {
389 : // currently unused
390 439 : }
391 :
392 :
393 : void
394 61746 : RORouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
395 : // currently unused
396 61746 : }
397 :
398 :
399 : void
400 525164 : RORouteHandler::closeRoute(const bool mayBeDisconnected) {
401 525164 : const bool mustReroute = myActiveRoute.size() == 0 && myActiveRouteStops.size() != 0;
402 : if (mustReroute) {
403 : // implicit route from stops
404 20 : for (const SUMOVehicleParameter::Stop& stop : myActiveRouteStops) {
405 16 : ROEdge* edge = myNet.getEdge(stop.edge);
406 16 : myActiveRoute.push_back(edge);
407 : }
408 : }
409 525164 : if (myActiveRoute.size() == 0) {
410 20 : if (myActiveRouteRefID != "" && myCurrentAlternatives != nullptr) {
411 8 : myCurrentAlternatives->addAlternativeDef(myNet.getRouteDef(myActiveRouteRefID));
412 4 : myActiveRouteID = "";
413 : myActiveRouteRefID = "";
414 4 : return;
415 : }
416 16 : if (myVehicleParameter != nullptr) {
417 0 : myErrorOutput->inform("The route for vehicle '" + myVehicleParameter->id + "' has no edges.");
418 : } else {
419 32 : myErrorOutput->inform("Route '" + myActiveRouteID + "' has no edges.");
420 : }
421 16 : myActiveRouteID = "";
422 : myActiveRouteStops.clear();
423 16 : return;
424 : }
425 525144 : if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
426 0 : myErrorOutput->inform("The routing information for vehicle '" + myVehicleParameter->id + "' is insufficient.");
427 0 : myActiveRouteID = "";
428 : myActiveRouteStops.clear();
429 0 : return;
430 : }
431 1449030 : if (!mayBeDisconnected && OptionsCont::getOptions().exists("no-internal-links") && !OptionsCont::getOptions().getBool("no-internal-links")) {
432 : // fix internal edges which did not get parsed
433 : const ROEdge* last = nullptr;
434 : ConstROEdgeVector fullRoute;
435 2964341 : for (const ROEdge* roe : myActiveRoute) {
436 2502401 : if (last != nullptr) {
437 4962168 : for (const ROEdge* intern : last->getSuccessors()) {
438 2921707 : if (intern->isInternal() && intern->getSuccessors().size() == 1 && intern->getSuccessors().front() == roe) {
439 0 : fullRoute.push_back(intern);
440 : }
441 : }
442 : }
443 2502401 : fullRoute.push_back(roe);
444 2502401 : last = roe;
445 : }
446 461940 : myActiveRoute = fullRoute;
447 : }
448 525144 : if (myActiveRouteRepeat > 0) {
449 : // duplicate route
450 8 : ConstROEdgeVector tmpEdges = myActiveRoute;
451 8 : auto tmpStops = myActiveRouteStops;
452 24 : for (int i = 0; i < myActiveRouteRepeat; i++) {
453 16 : myActiveRoute.insert(myActiveRoute.begin(), tmpEdges.begin(), tmpEdges.end());
454 80 : for (SUMOVehicleParameter::Stop stop : tmpStops) {
455 64 : if (stop.until > 0) {
456 32 : if (myActiveRoutePeriod <= 0) {
457 0 : const std::string description = myVehicleParameter != nullptr
458 0 : ? "for vehicle '" + myVehicleParameter->id + "'"
459 0 : : "'" + myActiveRouteID + "'";
460 0 : throw ProcessError(TLF("Cannot repeat stops with 'until' in route % because no cycleTime is defined.", description));
461 : }
462 32 : stop.until += myActiveRoutePeriod * (i + 1);
463 32 : stop.arrival += myActiveRoutePeriod * (i + 1);
464 : }
465 64 : myActiveRouteStops.push_back(stop);
466 64 : }
467 : }
468 8 : }
469 525144 : RORoute* route = new RORoute(myActiveRouteID, myCurrentCosts, myActiveRouteProbability, myActiveRoute,
470 525144 : myActiveRouteColor, myActiveRouteStops);
471 : myActiveRoute.clear();
472 525144 : if (myCurrentAlternatives == nullptr) {
473 65372 : if (myNet.getRouteDef(myActiveRouteID) != nullptr) {
474 0 : delete route;
475 0 : if (myVehicleParameter != nullptr) {
476 0 : myErrorOutput->inform("Another route for vehicle '" + myVehicleParameter->id + "' exists.");
477 : } else {
478 0 : myErrorOutput->inform("Another route (or distribution) with the id '" + myActiveRouteID + "' exists.");
479 : }
480 : myActiveRouteID = "";
481 : myActiveRouteStops.clear();
482 0 : return;
483 : } else {
484 67493 : myCurrentAlternatives = new RORouteDef(myActiveRouteID, 0, mayBeDisconnected || myTryRepair, mayBeDisconnected);
485 65372 : myCurrentAlternatives->addLoadedAlternative(route);
486 65372 : myNet.addRouteDef(myCurrentAlternatives);
487 65372 : myCurrentAlternatives = nullptr;
488 : }
489 : } else {
490 459772 : myCurrentAlternatives->addLoadedAlternative(route);
491 : }
492 : myActiveRouteID = "";
493 : myActiveRouteStops.clear();
494 : }
495 :
496 :
497 : void
498 178335 : RORouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
499 : // check whether the id is really necessary
500 178335 : bool ok = true;
501 : std::string id;
502 178335 : if (myVehicleParameter != nullptr) {
503 : // ok, a vehicle is wrapping the route,
504 : // we may use this vehicle's id as default
505 177311 : myVehicleParameter->routeid = id = "!" + myVehicleParameter->id; // !!! document this
506 177311 : if (attrs.hasAttribute(SUMO_ATTR_ID)) {
507 0 : WRITE_WARNINGF(TL("Ids of internal route distributions are ignored (vehicle '%')."), myVehicleParameter->id);
508 : }
509 : } else {
510 1024 : id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
511 1024 : if (!ok) {
512 : return;
513 : }
514 : }
515 : // try to get the index of the last element
516 178327 : int index = attrs.getOpt<int>(SUMO_ATTR_LAST, id.c_str(), ok, 0);
517 178327 : if (ok && index < 0) {
518 16 : myErrorOutput->inform("Negative index of a route alternative (id='" + id + "').");
519 8 : return;
520 : }
521 : // build the alternative cont
522 178319 : myCurrentAlternatives = new RORouteDef(id, index, myTryRepair, false);
523 178319 : if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
524 0 : ok = true;
525 0 : StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, id.c_str(), ok));
526 0 : while (st.hasNext()) {
527 0 : const std::string routeID = st.next();
528 0 : const RORouteDef* route = myNet.getRouteDef(routeID);
529 0 : if (route == nullptr) {
530 0 : myErrorOutput->inform("Unknown route '" + routeID + "' in distribution '" + id + "'.");
531 : } else {
532 0 : myCurrentAlternatives->addAlternativeDef(route);
533 : }
534 : }
535 0 : }
536 : }
537 :
538 :
539 : void
540 178319 : RORouteHandler::closeRouteDistribution() {
541 178319 : if (myCurrentAlternatives != nullptr) {
542 178303 : if (myCurrentAlternatives->getOverallProb() == 0) {
543 0 : myErrorOutput->inform("Route distribution '" + myCurrentAlternatives->getID() + "' is empty.");
544 0 : delete myCurrentAlternatives;
545 178303 : } else if (!myNet.addRouteDef(myCurrentAlternatives)) {
546 0 : myErrorOutput->inform("Another route (or distribution) with the id '" + myCurrentAlternatives->getID() + "' exists.");
547 0 : delete myCurrentAlternatives;
548 : }
549 178303 : myCurrentAlternatives = nullptr;
550 : }
551 178319 : }
552 :
553 :
554 : void
555 241707 : RORouteHandler::closeVehicle() {
556 241707 : checkLastDepart();
557 : // get the vehicle id
558 241707 : if (myVehicleParameter->departProcedure == DepartDefinition::GIVEN && myVehicleParameter->depart < myBegin) {
559 : return;
560 : }
561 : // get vehicle type
562 241707 : SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
563 241707 : if (type == nullptr) {
564 272 : myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
565 136 : type = myNet.getVehicleTypeSecure(DEFAULT_VTYPE_ID);
566 : } else {
567 241571 : if (!myKeepVTypeDist) {
568 : // fix the type id in case we used a distribution
569 241567 : myVehicleParameter->vtypeid = type->id;
570 : }
571 : }
572 241707 : if (type->vehicleClass == SVC_PEDESTRIAN) {
573 120 : WRITE_WARNINGF(TL("Vehicle type '%' with vClass=pedestrian should only be used for persons and not for vehicle '%'."), type->id, myVehicleParameter->id);
574 : }
575 : // get the route
576 241707 : RORouteDef* route = myNet.getRouteDef(myVehicleParameter->routeid);
577 241683 : if (route == nullptr) {
578 48 : myErrorOutput->inform("The route of the vehicle '" + myVehicleParameter->id + "' is not known.");
579 24 : return;
580 : }
581 241683 : if (MsgHandler::getErrorInstance()->wasInformed()) {
582 : return;
583 : }
584 241535 : const bool needCopy = route->getID()[0] != '!';
585 241535 : if (needCopy) {
586 2564 : route = route->copy("!" + myVehicleParameter->id, myVehicleParameter->depart);
587 : }
588 : // build the vehicle
589 241535 : ROVehicle* veh = new ROVehicle(*myVehicleParameter, route, type, &myNet, myErrorOutput);
590 241535 : if (myNet.addVehicle(myVehicleParameter->id, veh)) {
591 241531 : registerLastDepart();
592 4 : } else if (needCopy) {
593 4 : delete route;
594 : }
595 241535 : delete myVehicleParameter;
596 241535 : myVehicleParameter = nullptr;
597 : }
598 :
599 :
600 : void
601 1997 : RORouteHandler::closeVType() {
602 1997 : if (myNet.addVehicleType(myCurrentVType)) {
603 1997 : if (myCurrentVTypeDistribution != nullptr) {
604 32 : myCurrentVTypeDistribution->add(myCurrentVType, myCurrentVType->defaultProbability);
605 : }
606 : }
607 3994 : if (OptionsCont::getOptions().isSet("restriction-params")) {
608 12 : const std::vector<std::string> paramKeys = OptionsCont::getOptions().getStringVector("restriction-params");
609 12 : myCurrentVType->cacheParamRestrictions(paramKeys);
610 12 : }
611 1997 : myCurrentVType = nullptr;
612 1997 : }
613 :
614 :
615 : void
616 1758 : RORouteHandler::closePerson() {
617 1758 : SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
618 1758 : if (type == nullptr) {
619 0 : myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for person '" + myVehicleParameter->id + "' is not known.");
620 0 : type = myNet.getVehicleTypeSecure(DEFAULT_PEDTYPE_ID);
621 : }
622 1758 : if (myActivePlan == nullptr || myActivePlan->empty()) {
623 40 : WRITE_WARNINGF(TL("Discarding person '%' because her plan is empty"), myVehicleParameter->id);
624 : } else {
625 1738 : ROPerson* person = new ROPerson(*myVehicleParameter, type);
626 3612 : for (ROPerson::PlanItem* item : *myActivePlan) {
627 1874 : person->getPlan().push_back(item);
628 : }
629 1738 : if (myNet.addPerson(person)) {
630 1738 : checkLastDepart();
631 1738 : registerLastDepart();
632 : }
633 : }
634 1758 : delete myVehicleParameter;
635 1758 : myVehicleParameter = nullptr;
636 3516 : delete myActivePlan;
637 1758 : myActivePlan = nullptr;
638 1758 : }
639 :
640 :
641 : void
642 28 : RORouteHandler::closePersonFlow() {
643 : std::string typeID = DEFAULT_PEDTYPE_ID;
644 28 : if (myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid) == nullptr) {
645 0 : myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for personFlow '" + myVehicleParameter->id + "' is not known.");
646 : } else {
647 28 : typeID = myVehicleParameter->vtypeid;
648 : }
649 28 : if (myActivePlan == nullptr || myActivePlan->empty()) {
650 0 : WRITE_WARNINGF(TL("Discarding personFlow '%' because their plan is empty"), myVehicleParameter->id);
651 : } else {
652 28 : checkLastDepart();
653 : // instantiate all persons of this flow
654 : int i = 0;
655 28 : std::string baseID = myVehicleParameter->id;
656 28 : if (myVehicleParameter->repetitionProbability > 0) {
657 4 : if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
658 0 : throw ProcessError(TLF("probabilistic personFlow '%' must specify end time", myVehicleParameter->id));
659 : } else {
660 44 : for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
661 40 : if (RandHelper::rand() < myVehicleParameter->repetitionProbability) {
662 20 : addFlowPerson(typeID, t, baseID, i++);
663 : }
664 : }
665 : }
666 : } else {
667 24 : SUMOTime depart = myVehicleParameter->depart;
668 : // uniform sampling of departures from range is equivalent to poisson flow (encoded by negative offset)
669 48 : if (OptionsCont::getOptions().getBool("randomize-flows") && myVehicleParameter->repetitionOffset >= 0) {
670 : std::vector<SUMOTime> departures;
671 4 : const SUMOTime range = myVehicleParameter->repetitionNumber * myVehicleParameter->repetitionOffset;
672 24 : for (int j = 0; j < myVehicleParameter->repetitionNumber; ++j) {
673 20 : departures.push_back(depart + RandHelper::rand(range));
674 : }
675 4 : std::sort(departures.begin(), departures.end());
676 : std::reverse(departures.begin(), departures.end());
677 24 : for (; i < myVehicleParameter->repetitionNumber; i++) {
678 20 : addFlowPerson(typeID, departures[i], baseID, i);
679 : depart += myVehicleParameter->repetitionOffset;
680 : }
681 : } else {
682 20 : const bool triggered = myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED;
683 20 : if (myVehicleParameter->repetitionOffset < 0) {
684 : // poisson: randomize first depart
685 4 : myVehicleParameter->incrementFlow(1);
686 : }
687 172 : for (; i < myVehicleParameter->repetitionNumber && (triggered || depart + myVehicleParameter->repetitionTotalOffset <= myVehicleParameter->repetitionEnd); i++) {
688 152 : addFlowPerson(typeID, depart + myVehicleParameter->repetitionTotalOffset, baseID, i);
689 152 : if (myVehicleParameter->departProcedure != DepartDefinition::TRIGGERED) {
690 152 : myVehicleParameter->incrementFlow(1);
691 : }
692 : }
693 : }
694 : }
695 : }
696 28 : delete myVehicleParameter;
697 28 : myVehicleParameter = nullptr;
698 56 : for (ROPerson::PlanItem* const it : *myActivePlan) {
699 28 : delete it;
700 : }
701 56 : delete myActivePlan;
702 28 : myActivePlan = nullptr;
703 28 : }
704 :
705 :
706 : void
707 192 : RORouteHandler::addFlowPerson(const std::string& typeID, SUMOTime depart, const std::string& baseID, int i) {
708 192 : SUMOVehicleParameter pars = *myVehicleParameter;
709 384 : pars.id = baseID + "." + toString(i);
710 192 : pars.depart = depart;
711 192 : const SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(typeID);
712 192 : if (!myKeepVTypeDist) {
713 192 : pars.vtypeid = type->id;
714 : }
715 192 : ROPerson* person = new ROPerson(pars, type);
716 384 : for (ROPerson::PlanItem* item : *myActivePlan) {
717 192 : person->getPlan().push_back(item->clone());
718 : }
719 192 : if (myNet.addPerson(person)) {
720 192 : if (i == 0) {
721 28 : registerLastDepart();
722 : }
723 : }
724 192 : }
725 :
726 :
727 : void
728 36 : RORouteHandler::closeContainer() {
729 36 : myActiveContainerPlan->closeTag();
730 36 : if (myActiveContainerPlanSize > 0) {
731 36 : myNet.addContainer(myVehicleParameter->depart, myActiveContainerPlan->getString());
732 36 : checkLastDepart();
733 36 : registerLastDepart();
734 : } else {
735 0 : WRITE_WARNINGF(TL("Discarding container '%' because its plan is empty"), myVehicleParameter->id);
736 : }
737 36 : delete myVehicleParameter;
738 36 : myVehicleParameter = nullptr;
739 36 : delete myActiveContainerPlan;
740 36 : myActiveContainerPlan = nullptr;
741 36 : myActiveContainerPlanSize = 0;
742 36 : }
743 :
744 :
745 20 : void RORouteHandler::closeContainerFlow() {
746 20 : myActiveContainerPlan->closeTag();
747 20 : if (myActiveContainerPlanSize > 0) {
748 20 : myNet.addContainer(myVehicleParameter->depart, myActiveContainerPlan->getString());
749 20 : checkLastDepart();
750 20 : registerLastDepart();
751 : } else {
752 0 : WRITE_WARNINGF(TL("Discarding containerFlow '%' because its plan is empty"), myVehicleParameter->id);
753 : }
754 20 : delete myVehicleParameter;
755 20 : myVehicleParameter = nullptr;
756 20 : delete myActiveContainerPlan;
757 20 : myActiveContainerPlan = nullptr;
758 20 : myActiveContainerPlanSize = 0;
759 20 : }
760 :
761 :
762 : void
763 1960 : RORouteHandler::closeFlow() {
764 1960 : checkLastDepart();
765 : // @todo: consider myScale?
766 1960 : if (myVehicleParameter->repetitionNumber == 0) {
767 8 : delete myVehicleParameter;
768 8 : myVehicleParameter = nullptr;
769 8 : return;
770 : }
771 : // let's check whether vehicles had to depart before the simulation starts
772 1952 : myVehicleParameter->repetitionsDone = 0;
773 1952 : const SUMOTime offsetToBegin = myBegin - myVehicleParameter->depart;
774 2136 : while (myVehicleParameter->repetitionTotalOffset < offsetToBegin) {
775 186 : myVehicleParameter->incrementFlow(1);
776 186 : if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
777 2 : delete myVehicleParameter;
778 2 : myVehicleParameter = nullptr;
779 2 : return;
780 : }
781 : }
782 1950 : if (myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid) == nullptr) {
783 36 : myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
784 : }
785 1950 : if (myVehicleParameter->routeid[0] == '!' && myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
786 1456 : closeRoute(true);
787 : }
788 1950 : if (myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
789 0 : myErrorOutput->inform("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
790 0 : delete myVehicleParameter;
791 0 : myVehicleParameter = nullptr;
792 0 : return;
793 : }
794 1950 : myActiveRouteID = "";
795 1950 : if (!MsgHandler::getErrorInstance()->wasInformed()) {
796 3882 : if (myNet.addFlow(myVehicleParameter, OptionsCont::getOptions().getBool("randomize-flows"))) {
797 1937 : registerLastDepart();
798 : } else {
799 8 : myErrorOutput->inform("Another flow with the id '" + myVehicleParameter->id + "' exists.");
800 4 : delete myVehicleParameter;
801 : }
802 : } else {
803 9 : delete myVehicleParameter;
804 : }
805 1950 : myVehicleParameter = nullptr;
806 1950 : myInsertStopEdgesAt = -1;
807 : }
808 :
809 :
810 : void
811 61746 : RORouteHandler::closeTrip() {
812 61746 : closeRoute(true);
813 61746 : closeVehicle();
814 61746 : }
815 :
816 :
817 : const SUMOVehicleParameter::Stop*
818 3981 : RORouteHandler::retrieveStoppingPlace(const SUMOSAXAttributes& attrs, const std::string& errorSuffix, std::string& id, const SUMOVehicleParameter::Stop* stopParam) {
819 : // dummy stop parameter to hold the attributes
820 3981 : SUMOVehicleParameter::Stop stop;
821 3981 : if (stopParam != nullptr) {
822 2179 : stop = *stopParam;
823 : } else {
824 1802 : bool ok = true;
825 3604 : stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
826 3604 : stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_TRAIN_STOP, nullptr, ok, stop.busstop); // alias
827 3604 : stop.chargingStation = attrs.getOpt<std::string>(SUMO_ATTR_CHARGING_STATION, nullptr, ok, "");
828 3604 : stop.overheadWireSegment = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENT, nullptr, ok, "");
829 3604 : stop.containerstop = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
830 3604 : stop.parkingarea = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, nullptr, ok, "");
831 : }
832 : const SUMOVehicleParameter::Stop* toStop = nullptr;
833 3981 : if (stop.busstop != "") {
834 2132 : toStop = myNet.getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
835 : id = stop.busstop;
836 2132 : if (toStop == nullptr) {
837 0 : WRITE_ERROR("The busStop '" + stop.busstop + "' is not known" + errorSuffix);
838 : }
839 1849 : } else if (stop.containerstop != "") {
840 32 : toStop = myNet.getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
841 : id = stop.containerstop;
842 32 : if (toStop == nullptr) {
843 0 : WRITE_ERROR("The containerStop '" + stop.containerstop + "' is not known" + errorSuffix);
844 : }
845 1817 : } else if (stop.parkingarea != "") {
846 24 : toStop = myNet.getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
847 : id = stop.parkingarea;
848 24 : if (toStop == nullptr) {
849 0 : WRITE_ERROR("The parkingArea '" + stop.parkingarea + "' is not known" + errorSuffix);
850 : }
851 1793 : } else if (stop.chargingStation != "") {
852 : // ok, we have a charging station
853 20 : toStop = myNet.getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
854 : id = stop.chargingStation;
855 20 : if (toStop == nullptr) {
856 0 : WRITE_ERROR("The chargingStation '" + stop.chargingStation + "' is not known" + errorSuffix);
857 : }
858 1773 : } else if (stop.overheadWireSegment != "") {
859 : // ok, we have an overhead wire segment
860 0 : toStop = myNet.getStoppingPlace(stop.overheadWireSegment, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
861 : id = stop.overheadWireSegment;
862 0 : if (toStop == nullptr) {
863 0 : WRITE_ERROR("The overhead wire segment '" + stop.overheadWireSegment + "' is not known" + errorSuffix);
864 : }
865 : }
866 3981 : return toStop;
867 3981 : }
868 :
869 : Parameterised*
870 2195 : RORouteHandler::addStop(const SUMOSAXAttributes& attrs) {
871 : Parameterised* result = nullptr;
872 2195 : if (myActiveContainerPlan != nullptr) {
873 16 : myActiveContainerPlan->openTag(SUMO_TAG_STOP);
874 16 : (*myActiveContainerPlan) << attrs;
875 16 : myActiveContainerPlan->closeTag();
876 16 : myActiveContainerPlanSize++;
877 16 : return result;
878 : }
879 : std::string errorSuffix;
880 2179 : if (myActivePlan != nullptr) {
881 248 : errorSuffix = " in person '" + myVehicleParameter->id + "'.";
882 : } else if (myActiveContainerPlan != nullptr) {
883 : errorSuffix = " in container '" + myVehicleParameter->id + "'.";
884 2055 : } else if (myVehicleParameter != nullptr) {
885 1338 : errorSuffix = " in vehicle '" + myVehicleParameter->id + "'.";
886 : } else {
887 2772 : errorSuffix = " in route '" + myActiveRouteID + "'.";
888 : }
889 2179 : SUMOVehicleParameter::Stop stop;
890 4358 : bool ok = parseStop(stop, attrs, errorSuffix, myErrorOutput);
891 2179 : if (!ok) {
892 : return result;
893 : }
894 : // try to parse the assigned bus stop
895 2179 : const ROEdge* edge = nullptr;
896 : std::string stoppingPlaceID;
897 2179 : const SUMOVehicleParameter::Stop* stoppingPlace = retrieveStoppingPlace(attrs, errorSuffix, stoppingPlaceID, &stop);
898 : bool hasPos = false;
899 2179 : if (stoppingPlace != nullptr) {
900 2024 : stop.lane = stoppingPlace->lane;
901 2024 : stop.endPos = stoppingPlace->endPos;
902 2024 : stop.startPos = stoppingPlace->startPos;
903 6072 : edge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
904 : } else {
905 : // no, the lane and the position should be given
906 310 : stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
907 310 : stop.edge = attrs.getOpt<std::string>(SUMO_ATTR_EDGE, nullptr, ok, "");
908 310 : if (ok && stop.edge != "") {
909 28 : edge = myNet.getEdge(stop.edge);
910 28 : if (edge == nullptr) {
911 0 : myErrorOutput->inform("The edge '" + stop.edge + "' for a stop is not known" + errorSuffix);
912 0 : return result;
913 : }
914 254 : } else if (ok && stop.lane != "") {
915 309 : edge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
916 103 : if (edge == nullptr) {
917 0 : myErrorOutput->inform("The lane '" + stop.lane + "' for a stop is not known" + errorSuffix);
918 0 : return result;
919 : }
920 24 : } else if (ok && ((attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y))
921 16 : || (attrs.hasAttribute(SUMO_ATTR_LON) && attrs.hasAttribute(SUMO_ATTR_LAT)))) {
922 : Position pos;
923 : bool geo = false;
924 24 : if (attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y)) {
925 8 : pos = Position(attrs.get<double>(SUMO_ATTR_X, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_Y, myVehicleParameter->id.c_str(), ok));
926 : } else {
927 16 : pos = Position(attrs.get<double>(SUMO_ATTR_LON, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_LAT, myVehicleParameter->id.c_str(), ok));
928 : geo = true;
929 : }
930 24 : PositionVector positions;
931 24 : positions.push_back(pos);
932 : ConstROEdgeVector geoEdges;
933 : SUMOVehicleClass vClass = SVC_PASSENGER;
934 24 : if (!myNet.getVTypeDistribution(myVehicleParameter->vtypeid)) {
935 24 : SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
936 24 : if (type != nullptr) {
937 24 : vClass = type->vehicleClass;
938 : }
939 : }
940 24 : parseGeoEdges(positions, geo, vClass, geoEdges, myVehicleParameter->id, true, ok);
941 24 : if (ok) {
942 24 : edge = geoEdges.front();
943 : hasPos = true;
944 24 : if (geo) {
945 16 : GeoConvHelper::getFinal().x2cartesian_const(pos);
946 : }
947 24 : stop.parametersSet |= STOP_END_SET;
948 24 : stop.endPos = edge->getLanes()[0]->getShape().nearest_offset_to_point2D(pos, false);
949 : } else {
950 : return result;
951 : }
952 24 : } else if (!ok || (stop.lane == "" && stop.edge == "")) {
953 0 : myErrorOutput->inform("A stop must be placed on a bus stop, a container stop, a parking area, an edge or a lane" + errorSuffix);
954 0 : return result;
955 : }
956 : if (!hasPos) {
957 131 : stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, edge->getLength());
958 : }
959 155 : stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, stop.endPos - 2 * POSITION_EPS);
960 186 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, !attrs.hasAttribute(SUMO_ATTR_STARTPOS) && !attrs.hasAttribute(SUMO_ATTR_ENDPOS));
961 155 : const double endPosOffset = edge->isInternal() ? edge->getNormalBefore()->getLength() : 0;
962 155 : if (!ok || (checkStopPos(stop.startPos, stop.endPos, edge->getLength() + endPosOffset, POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
963 0 : myErrorOutput->inform("Invalid start or end position for stop" + errorSuffix);
964 0 : return result;
965 : }
966 : }
967 2179 : stop.edge = edge->getID();
968 2179 : if (myActivePlan != nullptr) {
969 124 : ROPerson::addStop(*myActivePlan, stop, edge);
970 124 : result = myActivePlan->back()->getStopParameters();
971 2055 : } else if (myVehicleParameter != nullptr) {
972 669 : myVehicleParameter->stops.push_back(stop);
973 669 : result = &myVehicleParameter->stops.back();
974 : } else {
975 1386 : myActiveRouteStops.push_back(stop);
976 : result = &myActiveRouteStops.back();
977 : }
978 2179 : if (myInsertStopEdgesAt >= 0) {
979 440 : myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
980 440 : myInsertStopEdgesAt++;
981 : }
982 : return result;
983 2179 : }
984 :
985 :
986 : void
987 64 : RORouteHandler::addRide(const SUMOSAXAttributes& attrs) {
988 64 : bool ok = true;
989 64 : std::vector<ROPerson::PlanItem*>& plan = *myActivePlan;
990 64 : const std::string pid = myVehicleParameter->id;
991 :
992 : const ROEdge* from = nullptr;
993 : const ROEdge* to = nullptr;
994 64 : parseFromViaTo(SUMO_TAG_PERSON, attrs, ok);
995 72 : if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
996 72 : || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT) || attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
997 60 : if (ok) {
998 60 : from = myActiveRoute.front();
999 : }
1000 4 : } else if (plan.empty()) {
1001 0 : myErrorOutput->inform("The start edge for person '" + pid + "' is not known.");
1002 0 : return;
1003 : }
1004 : std::string stoppingPlaceID;
1005 128 : const SUMOVehicleParameter::Stop* stop = retrieveStoppingPlace(attrs, " for ride of person '" + myVehicleParameter->id + "'", stoppingPlaceID);
1006 64 : if (stop != nullptr) {
1007 84 : to = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop->lane));
1008 : } else {
1009 40 : if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
1010 40 : || attrs.hasAttribute(SUMO_ATTR_TOLONLAT) || attrs.hasAttribute(SUMO_ATTR_TOXY)) {
1011 36 : to = myActiveRoute.back();
1012 : } else {
1013 0 : myErrorOutput->inform("The to edge is missing within a ride of '" + myVehicleParameter->id + "'.");
1014 0 : return;
1015 : }
1016 : }
1017 64 : double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok,
1018 : stop == nullptr ? std::numeric_limits<double>::infinity() : stop->endPos);
1019 64 : const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
1020 128 : const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, pid.c_str(), ok, "");
1021 :
1022 64 : if (plan.empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
1023 20 : StringTokenizer st(desc);
1024 20 : if (st.size() != 1) {
1025 0 : myErrorOutput->inform("Triggered departure for person '" + pid + "' requires a unique lines value.");
1026 0 : return;
1027 : }
1028 20 : const std::string vehID = st.front();
1029 20 : if (!myNet.knowsVehicle(vehID)) {
1030 16 : myErrorOutput->inform("Unknown vehicle '" + vehID + "' in triggered departure for person '" + pid + "'.");
1031 8 : return;
1032 : }
1033 12 : SUMOTime vehDepart = myNet.getDeparture(vehID);
1034 12 : if (vehDepart == -1) {
1035 0 : myErrorOutput->inform("Cannot use triggered vehicle '" + vehID + "' in triggered departure for person '" + pid + "'.");
1036 0 : return;
1037 : }
1038 12 : myVehicleParameter->depart = vehDepart + 1; // write person after vehicle
1039 20 : }
1040 56 : ROPerson::addRide(plan, from, to, desc, arrivalPos, stoppingPlaceID, group);
1041 : }
1042 :
1043 :
1044 : void
1045 32 : RORouteHandler::addTransport(const SUMOSAXAttributes& attrs) {
1046 32 : if (myActiveContainerPlan != nullptr && myActiveContainerPlanSize == 0 && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
1047 12 : bool ok = true;
1048 : const std::string pid = myVehicleParameter->id;
1049 12 : const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
1050 16 : StringTokenizer st(desc);
1051 12 : if (st.size() != 1) {
1052 0 : throw ProcessError(TLF("Triggered departure for container '%' requires a unique lines value.", pid));
1053 : }
1054 12 : const std::string vehID = st.front();
1055 12 : if (!myNet.knowsVehicle(vehID)) {
1056 8 : throw ProcessError("Unknown vehicle '" + vehID + "' in triggered departure for container '" + pid + "'.");
1057 : }
1058 8 : SUMOTime vehDepart = myNet.getDeparture(vehID);
1059 8 : if (vehDepart == -1) {
1060 0 : throw ProcessError("Cannot use triggered vehicle '" + vehID + "' in triggered departure for container '" + pid + "'.");
1061 : }
1062 8 : myVehicleParameter->depart = vehDepart + 1; // write container after vehicle
1063 12 : }
1064 28 : }
1065 :
1066 :
1067 : void
1068 32 : RORouteHandler::addTranship(const SUMOSAXAttributes& /*attrs*/) {
1069 32 : }
1070 :
1071 :
1072 : void
1073 614230 : RORouteHandler::parseEdges(const std::string& desc, ConstROEdgeVector& into,
1074 : const std::string& rid, bool& ok) {
1075 3848886 : for (StringTokenizer st(desc); st.hasNext();) {
1076 2620426 : const std::string id = st.next();
1077 2620426 : const ROEdge* edge = myNet.getEdge(id);
1078 2620426 : if (edge == nullptr) {
1079 42 : myErrorOutput->inform("The edge '" + id + "' within the route " + rid + " is not known.");
1080 21 : ok = false;
1081 : } else {
1082 2620405 : into.push_back(edge);
1083 : }
1084 614230 : }
1085 614230 : }
1086 :
1087 :
1088 : void
1089 1666 : RORouteHandler::parseWalkPositions(const SUMOSAXAttributes& attrs, const std::string& personID,
1090 : const ROEdge* /*fromEdge*/, const ROEdge*& toEdge,
1091 : double& departPos, double& arrivalPos, std::string& busStopID,
1092 : const ROPerson::PlanItem* const lastStage, bool& ok) {
1093 1666 : const std::string description = "walk or personTrip of '" + personID + "'.";
1094 1666 : if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
1095 0 : WRITE_WARNING(TL("The attribute departPos is no longer supported for walks, please use the person attribute, the arrivalPos of the previous step or explicit stops."));
1096 : }
1097 1666 : departPos = myVehicleParameter->departPos;
1098 1666 : if (lastStage != nullptr) {
1099 96 : departPos = lastStage->getDestinationPos();
1100 : }
1101 :
1102 3332 : busStopID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
1103 :
1104 1666 : const SUMOVehicleParameter::Stop* bs = retrieveStoppingPlace(attrs, description, busStopID);
1105 1666 : if (bs != nullptr) {
1106 396 : toEdge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(bs->lane));
1107 132 : arrivalPos = (bs->startPos + bs->endPos) / 2;
1108 : }
1109 1666 : if (toEdge != nullptr) {
1110 1666 : if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1111 56 : arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS,
1112 56 : myHardFail, description, toEdge->getLength(),
1113 112 : attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok));
1114 : }
1115 : } else {
1116 0 : throw ProcessError(TLF("No destination edge for %.", description));
1117 : }
1118 1666 : }
1119 :
1120 :
1121 : void
1122 1666 : RORouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
1123 1666 : bool ok = true;
1124 1666 : const char* const id = myVehicleParameter->id.c_str();
1125 : assert(!attrs.hasAttribute(SUMO_ATTR_EDGES));
1126 : const ROEdge* from = nullptr;
1127 1666 : const ROEdge* to = nullptr;
1128 1666 : parseFromViaTo(SUMO_TAG_PERSON, attrs, ok);
1129 1666 : myInsertStopEdgesAt = -1;
1130 2026 : if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
1131 1770 : || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT) || attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
1132 1574 : if (ok) {
1133 1562 : from = myActiveRoute.front();
1134 : }
1135 92 : } else if (myActivePlan->empty()) {
1136 0 : throw ProcessError(TLF("Start edge not defined for person '%'.", myVehicleParameter->id));
1137 : } else {
1138 92 : from = myActivePlan->back()->getDestination();
1139 : }
1140 2062 : if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
1141 1810 : || attrs.hasAttribute(SUMO_ATTR_TOLONLAT) || attrs.hasAttribute(SUMO_ATTR_TOXY)) {
1142 1534 : to = myActiveRoute.back();
1143 : } // else, to may also be derived from stopping place
1144 :
1145 1666 : const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
1146 1666 : if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1147 0 : throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
1148 : }
1149 :
1150 1666 : double departPos = 0;
1151 1666 : double arrivalPos = std::numeric_limits<double>::infinity();
1152 : std::string busStopID;
1153 1666 : const ROPerson::PlanItem* const lastStage = myActivePlan->empty() ? nullptr : myActivePlan->back();
1154 1666 : parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, busStopID, lastStage, ok);
1155 :
1156 1666 : const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
1157 3332 : const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, id, ok, "");
1158 : SVCPermissions modeSet = 0;
1159 5126 : for (StringTokenizer st(modes); st.hasNext();) {
1160 1794 : const std::string mode = st.next();
1161 1794 : if (mode == "car") {
1162 580 : modeSet |= SVC_PASSENGER;
1163 1214 : } else if (mode == "taxi") {
1164 48 : modeSet |= SVC_TAXI;
1165 1166 : } else if (mode == "bicycle") {
1166 12 : modeSet |= SVC_BICYCLE;
1167 1154 : } else if (mode == "public") {
1168 1154 : modeSet |= SVC_BUS;
1169 : } else {
1170 0 : throw InvalidArgument("Unknown person mode '" + mode + "'.");
1171 : }
1172 1666 : }
1173 1666 : const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, "");
1174 1666 : double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
1175 1666 : if (ok) {
1176 1654 : const std::string originStopID = myActivePlan->empty() ? "" : myActivePlan->back()->getStopDest();
1177 1654 : ROPerson::addTrip(*myActivePlan, myVehicleParameter->id, from, to, modeSet, types,
1178 : departPos, originStopID, arrivalPos, busStopID, walkFactor, group);
1179 1654 : myParamStack.push_back(myActivePlan->back());
1180 : }
1181 1666 : }
1182 :
1183 :
1184 : void
1185 220 : RORouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
1186 : // parse walks from->to as person trips
1187 220 : if (attrs.hasAttribute(SUMO_ATTR_EDGES) || attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1188 : // XXX allow --repair?
1189 72 : bool ok = true;
1190 72 : if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1191 12 : const std::string routeID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
1192 12 : RORouteDef* routeDef = myNet.getRouteDef(routeID);
1193 12 : const RORoute* route = routeDef != nullptr ? routeDef->getFirstRoute() : nullptr;
1194 12 : if (route == nullptr) {
1195 0 : throw ProcessError("The route '" + routeID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
1196 : }
1197 12 : myActiveRoute = route->getEdgeVector();
1198 : } else {
1199 60 : myActiveRoute.clear();
1200 180 : parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, " walk for person '" + myVehicleParameter->id + "'", ok);
1201 : }
1202 72 : const char* const objId = myVehicleParameter->id.c_str();
1203 72 : const double duration = attrs.getOpt<double>(SUMO_ATTR_DURATION, objId, ok, -1);
1204 72 : if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1205 0 : throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
1206 : }
1207 72 : const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, objId, ok, -1.);
1208 72 : if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
1209 0 : throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
1210 : }
1211 : double departPos = 0.;
1212 : double arrivalPos = std::numeric_limits<double>::infinity();
1213 72 : if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
1214 0 : WRITE_WARNING(TL("The attribute departPos is no longer supported for walks, please use the person attribute, the arrivalPos of the previous step or explicit stops."));
1215 : }
1216 72 : if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1217 32 : arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, objId, myActiveRoute.back()->getLength(), attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, objId, ok));
1218 : }
1219 : std::string stoppingPlaceID;
1220 72 : const std::string errorSuffix = " for walk of person '" + myVehicleParameter->id + "'";
1221 72 : retrieveStoppingPlace(attrs, errorSuffix, stoppingPlaceID);
1222 72 : if (ok) {
1223 72 : ROPerson::addWalk(*myActivePlan, myActiveRoute, duration, speed, departPos, arrivalPos, stoppingPlaceID);
1224 72 : myParamStack.push_back(myActivePlan->back());
1225 : }
1226 : } else {
1227 148 : addPersonTrip(attrs);
1228 : }
1229 220 : }
1230 :
1231 :
1232 : void
1233 50 : RORouteHandler::initLaneTree(NamedRTree* tree) {
1234 3704 : for (const auto& edgeItem : myNet.getEdgeMap()) {
1235 7290 : for (ROLane* lane : edgeItem.second->getLanes()) {
1236 3636 : Boundary b = lane->getShape().getBoxBoundary();
1237 3636 : const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
1238 3636 : const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
1239 3636 : tree->Insert(cmin, cmax, lane);
1240 3636 : }
1241 : }
1242 50 : }
1243 :
1244 :
1245 : ROEdge*
1246 480 : RORouteHandler::retrieveEdge(const std::string& id) {
1247 480 : return myNet.getEdge(id);
1248 : }
1249 :
1250 : bool
1251 245489 : RORouteHandler::checkLastDepart() {
1252 245489 : if (!myUnsortedInput) {
1253 244616 : return SUMORouteHandler::checkLastDepart();
1254 : }
1255 : return true;
1256 : }
1257 :
1258 : /****************************************************************************/
|