Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 NIXMLPTHandler.cpp
15 : /// @author Jakob Erdmann
16 : /// @date Sat, 28 Jul 2018
17 : ///
18 : // Importer for static public transport information
19 : /****************************************************************************/
20 : #include <config.h>
21 :
22 : #include <string>
23 : #include <iostream>
24 : #include <map>
25 : #include <cmath>
26 : #include <utils/xml/SUMOSAXHandler.h>
27 : #include <utils/xml/SUMOXMLDefinitions.h>
28 : #include <utils/common/MsgHandler.h>
29 : #include <utils/common/StringUtils.h>
30 : #include <utils/common/StringTokenizer.h>
31 : #include <utils/geom/GeomConvHelper.h>
32 : #include <utils/common/ToString.h>
33 : #include <utils/options/OptionsCont.h>
34 : #include <utils/geom/GeoConvHelper.h>
35 : #include <netbuild/NBNodeCont.h>
36 : #include <netbuild/NBTypeCont.h>
37 : #include <netbuild/NBNetBuilder.h>
38 : #include <netbuild/NBPTStop.h>
39 : #include "NIImporter_OpenStreetMap.h"
40 : #include "NIXMLNodesHandler.h"
41 : #include "NIXMLPTHandler.h"
42 :
43 :
44 : // ===========================================================================
45 : // method definitions
46 : // ===========================================================================
47 4742 : NIXMLPTHandler::NIXMLPTHandler(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) :
48 : SUMOSAXHandler("public transport - file"),
49 4742 : myEdgeCont(ec),
50 4742 : myStopCont(sc),
51 4742 : myLineCont(lc),
52 : myCurrentStop(nullptr),
53 4742 : myCurrentLine(nullptr),
54 4742 : myCurrentCompletion(0),
55 4742 : myCurrentStopWasIgnored(false),
56 9484 : myWarnOnly(OptionsCont::getOptions().getBool("ignore-errors"))
57 4742 : { }
58 :
59 :
60 14226 : NIXMLPTHandler::~NIXMLPTHandler() {}
61 :
62 :
63 : void
64 245 : NIXMLPTHandler::myStartElement(int element,
65 : const SUMOSAXAttributes& attrs) {
66 245 : switch (element) {
67 139 : case SUMO_TAG_BUS_STOP:
68 : case SUMO_TAG_TRAIN_STOP:
69 : case SUMO_TAG_STOP:
70 139 : if (myCurrentRouteID != "") {
71 6 : addRouteStop(attrs);
72 133 : } else if (myCurrentLine == nullptr) {
73 99 : addPTStop(element, attrs);
74 : } else {
75 34 : addPTLineStop(attrs);
76 : }
77 : break;
78 12 : case SUMO_TAG_ACCESS:
79 12 : addAccess(attrs);
80 12 : break;
81 18 : case SUMO_TAG_PT_LINE:
82 18 : addPTLine(attrs);
83 18 : break;
84 7 : case SUMO_TAG_ROUTE:
85 7 : if (myCurrentLine == nullptr) {
86 3 : addRoute(attrs);
87 : } else {
88 4 : addPTLineRoute(attrs);
89 : }
90 : break;
91 3 : case SUMO_TAG_FLOW:
92 : case SUMO_TAG_TRIP:
93 3 : addPTLineFromFlow(attrs);
94 3 : break;
95 7 : case SUMO_TAG_PARAM: {
96 7 : bool ok = true;
97 7 : const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
98 7 : if (myCurrentLine != nullptr) {
99 6 : if (key == "completeness") {
100 3 : myCurrentCompletion = attrs.get<double>(SUMO_ATTR_VALUE, nullptr, ok);
101 3 : } else if (key == "name") {
102 6 : myCurrentLine->setName(attrs.get<std::string>(SUMO_ATTR_VALUE, nullptr, ok));
103 0 : } else if (key == "missingBefore") {
104 0 : myMissingBefore = attrs.get<int>(SUMO_ATTR_VALUE, nullptr, ok);
105 0 : } else if (key == "missingAfter") {
106 0 : myMissingAfter = attrs.get<int>(SUMO_ATTR_VALUE, nullptr, ok);
107 : }
108 1 : } else if (myCurrentStop != nullptr) {
109 1 : const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
110 1 : myCurrentStop->setParameter(key, val);
111 : }
112 : }
113 7 : break;
114 : default:
115 : break;
116 : }
117 245 : }
118 :
119 : void
120 245 : NIXMLPTHandler::myEndElement(int element) {
121 245 : switch (element) {
122 : case SUMO_TAG_BUS_STOP:
123 : case SUMO_TAG_TRAIN_STOP:
124 : myCurrentStop = nullptr;
125 133 : myCurrentStopWasIgnored = false;
126 133 : break;
127 21 : case SUMO_TAG_PT_LINE:
128 : case SUMO_TAG_FLOW:
129 : case SUMO_TAG_TRIP:
130 21 : if (myCurrentLine != nullptr) {
131 21 : myCurrentLine->setNumOfStops((int)((double)myCurrentLine->getStops().size() / myCurrentCompletion), myMissingBefore, myMissingAfter);
132 : }
133 21 : myCurrentLine = nullptr;
134 21 : break;
135 7 : case SUMO_TAG_ROUTE:
136 7 : myCurrentRouteID = "";
137 : break;
138 : default:
139 : break;
140 : }
141 245 : }
142 :
143 :
144 : bool
145 9 : NIXMLPTHandler::reportError(const std::string& msg, const std::string& edgeID, const std::string& id) {
146 9 : const bool ignored = myEdgeCont.wasIgnored(edgeID);
147 9 : if (!ignored && !myWarnOnly) {
148 0 : WRITE_ERRORF(msg, edgeID, id);
149 0 : return true;
150 : } else if (!ignored) {
151 6 : WRITE_WARNINGF(msg, edgeID, id);
152 : }
153 : return false;
154 : }
155 :
156 :
157 : void
158 99 : NIXMLPTHandler::addPTStop(int element, const SUMOSAXAttributes& attrs) {
159 99 : bool ok = true;
160 99 : const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, "busStop", ok);
161 198 : const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
162 99 : const std::string laneID = attrs.get<std::string>(SUMO_ATTR_LANE, id.c_str(), ok);
163 99 : double startPos = attrs.get<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok);
164 99 : double endPos = attrs.get<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok);
165 99 : const double parkingLength = attrs.getOpt<double>(SUMO_ATTR_PARKING_LENGTH, id.c_str(), ok, 0);
166 99 : const RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor(false));
167 198 : const std::string lines = attrs.getOpt<std::string>(SUMO_ATTR_LINES, id.c_str(), ok, "");
168 198 : int laneIndex = NBEdge::getLaneIndexFromLaneID(laneID);
169 99 : std::string edgeID = SUMOXMLDefinitions::getEdgeIDFromLane(laneID);
170 99 : NBEdge* edge = myEdgeCont.retrieve(edgeID);
171 99 : if (edge == nullptr) {
172 7 : edge = myEdgeCont.retrieve(edgeID, true);
173 7 : if (edge != nullptr && myEdgeCont.getSplit(edge) == nullptr) {
174 : // splits are treated later
175 : edge = nullptr;
176 : }
177 : }
178 7 : if (edge == nullptr) {
179 12 : if (!reportError(TL("Edge '%' for stop '%' not found"), edgeID, id)) {
180 6 : myCurrentStopWasIgnored = true;
181 : NBPTStopCont::addIgnored(id);
182 : }
183 6 : return;
184 : }
185 93 : if (edge->getNumLanes() <= laneIndex) {
186 0 : if (myWarnOnly) {
187 0 : WRITE_WARNINGF(TL("Lane '%' for stop '%' not found. Using leftmost lane"), laneID, id);
188 0 : laneIndex = edge->getNumLanes() - 1;
189 : } else {
190 0 : WRITE_ERRORF(TL("Lane '%' for stop '%' not found"), laneID, id);
191 0 : return;
192 : }
193 : }
194 93 : SVCPermissions permissions = edge->getPermissions(laneIndex);
195 : // possibly the stops were written for a different network. If the lane is not a typical public transport stop lane, assume bus as the default
196 93 : if (!isRailway(permissions) && permissions != SVC_SHIP && permissions != SVC_TAXI) {
197 9 : permissions = SVC_BUS;
198 : }
199 93 : if (ok) {
200 93 : if (startPos < 0) {
201 1 : startPos += edge->getLoadedLength();
202 : }
203 93 : if (endPos < 0) {
204 8 : endPos += edge->getLoadedLength();
205 : }
206 186 : if (myEdgeCont.wasRemoved(edgeID) && (
207 4 : startPos >= endPos || startPos < 0 || endPos < 0
208 4 : || startPos >= edge->getLoadedLength()
209 1 : || endPos >= edge->getLoadedLength())) {
210 3 : NBEdge* longest = myEdgeCont.getSplitBase(edgeID);
211 3 : if (longest != nullptr) {
212 : edge = longest;
213 : }
214 : }
215 93 : Position pos = edge->geometryPositionAtOffset((startPos + endPos) / 2);
216 93 : myCurrentStop = std::make_shared<NBPTStop>((SumoXMLTag)element, id, pos, edgeID, edgeID, endPos - startPos, name, permissions, parkingLength, color, startPos);
217 98 : while (myEdgeCont.getSplit(edge) != nullptr) {
218 : myCurrentStop->resetLoaded();
219 5 : const std::pair<NBEdge*, NBEdge*> split = *myEdgeCont.getSplit(edge);
220 5 : if (myCurrentStop->replaceEdge(edgeID, {split.first, split.second})) {
221 5 : edge = split.first->getID() == myCurrentStop->getEdgeId() ? split.first : split.second;
222 5 : edgeID = edge->getID();
223 : }
224 : }
225 242 : for (const std::string& line : StringTokenizer(lines).getVector()) {
226 56 : myCurrentStop->addLine(line);
227 93 : }
228 186 : if (!myStopCont.insert(myCurrentStop)) {
229 0 : WRITE_ERRORF(TL("Could not add public transport stop '%' (already exists)"), id);
230 : }
231 : }
232 : }
233 :
234 : void
235 12 : NIXMLPTHandler::addAccess(const SUMOSAXAttributes& attrs) {
236 12 : if (myCurrentStop == nullptr) {
237 5 : if (myCurrentStopWasIgnored) {
238 6 : return;
239 : } else {
240 0 : throw InvalidArgument("Could not add access outside a stopping place.");
241 : }
242 : }
243 7 : bool ok = true;
244 7 : const std::string laneID = attrs.get<std::string>(SUMO_ATTR_LANE, "access", ok);
245 7 : const std::string edgeID = SUMOXMLDefinitions::getEdgeIDFromLane(laneID);
246 7 : if (myEdgeCont.retrieve(edgeID) == nullptr) {
247 2 : reportError(TL("Edge '%' for access to stop '%' not found"), edgeID, myCurrentStop->getID());
248 : return;
249 : }
250 6 : const double pos = attrs.get<double>(SUMO_ATTR_POSITION, "access", ok);
251 6 : const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "access", ok, -1);
252 6 : if (ok) {
253 12 : myCurrentStop->addAccess(laneID, pos, length);
254 : }
255 : }
256 :
257 :
258 : void
259 18 : NIXMLPTHandler::addPTLine(const SUMOSAXAttributes& attrs) {
260 18 : bool ok = true;
261 18 : const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, "ptLine", ok);
262 18 : const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
263 18 : const std::string line = attrs.getOpt<std::string>(SUMO_ATTR_LINE, id.c_str(), ok, "");
264 18 : const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
265 18 : SUMOVehicleClass vClass = NIImporter_OpenStreetMap::interpretTransportType(type);
266 18 : if (attrs.hasAttribute(SUMO_ATTR_VCLASS)) {
267 0 : vClass = getVehicleClassID(attrs.get<std::string>(SUMO_ATTR_VCLASS, id.c_str(), ok));
268 : }
269 18 : RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor(false));
270 18 : const int intervalS = attrs.getOpt<int>(SUMO_ATTR_PERIOD, id.c_str(), ok, -1);
271 36 : const std::string nightService = attrs.getStringSecure("nightService", "");
272 36 : myCurrentCompletion = StringUtils::toDouble(attrs.getStringSecure("completeness", "1"));
273 36 : myMissingBefore = StringUtils::toInt(attrs.getStringSecure("missingBefore", "0"));
274 36 : myMissingAfter = StringUtils::toInt(attrs.getStringSecure("missingAfter", "0"));
275 18 : if (ok) {
276 : // patching existing line?
277 18 : myCurrentLine = myLineCont.retrieve(id);
278 18 : if (myCurrentLine == nullptr) {
279 17 : myCurrentLine = new NBPTLine(id, name, type, line, intervalS / 60, nightService, vClass, color);
280 17 : myLineCont.insert(myCurrentLine);
281 : } else {
282 3 : WRITE_MESSAGEF(TL("Duplicate ptLine id occurred ('%'); assuming overwriting is wished."), id);
283 1 : if (name != "") {
284 0 : myCurrentLine->setName(name);
285 : }
286 1 : if (line != "") {
287 0 : myCurrentLine->setRef(line);
288 : }
289 1 : if (intervalS != -1) {
290 1 : myCurrentLine->setPeriod(intervalS);
291 : }
292 : }
293 : }
294 18 : }
295 :
296 :
297 : void
298 3 : NIXMLPTHandler::addPTLineFromFlow(const SUMOSAXAttributes& attrs) {
299 3 : bool ok = true;
300 3 : myMissingBefore = 0;
301 3 : myMissingAfter = 0;
302 3 : const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, "flow", ok);
303 3 : const std::string line = attrs.get<std::string>(SUMO_ATTR_LINE, id.c_str(), ok);
304 3 : const std::string type = attrs.get<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok);
305 3 : const std::string route = attrs.get<std::string>(SUMO_ATTR_ROUTE, id.c_str(), ok);
306 3 : SUMOVehicleClass vClass = NIImporter_OpenStreetMap::interpretTransportType(type);
307 3 : const int intervalS = attrs.getOpt<int>(SUMO_ATTR_PERIOD, id.c_str(), ok, -1);
308 3 : RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor(false));
309 3 : if (ok) {
310 3 : myCurrentLine = new NBPTLine(id, "", type, line, intervalS / 60, "", vClass, color);
311 3 : myCurrentLine->setEdges(myRouteEdges[route]);
312 9 : for (std::shared_ptr<NBPTStop> stop : myRouteStops[route]) {
313 12 : myCurrentLine->addPTStop(stop);
314 : }
315 3 : myLineCont.insert(myCurrentLine);
316 : }
317 3 : }
318 :
319 :
320 : void
321 4 : NIXMLPTHandler::addPTLineRoute(const SUMOSAXAttributes& attrs) {
322 4 : if (myCurrentLine == nullptr) {
323 0 : WRITE_ERROR(TL("Found route outside line definition"));
324 0 : return;
325 : }
326 4 : bool ok = true;
327 4 : const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
328 : EdgeVector edges;
329 13 : for (const std::string& edgeID : edgeIDs) {
330 9 : NBEdge* edge = myEdgeCont.retrieve(edgeID);
331 9 : if (edge == nullptr) {
332 4 : reportError(TL("Edge '%' in route of line '%' not found"), edgeID, myCurrentLine->getLineID());
333 : } else {
334 7 : edges.push_back(edge);
335 : }
336 : }
337 4 : myCurrentLine->setEdges(edges);
338 4 : }
339 :
340 :
341 : void
342 3 : NIXMLPTHandler::addRoute(const SUMOSAXAttributes& attrs) {
343 3 : bool ok = true;
344 6 : myCurrentRouteID = attrs.get<std::string>(SUMO_ATTR_ID, "route", ok);
345 3 : const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, myCurrentRouteID.c_str(), ok);
346 : EdgeVector edges;
347 9 : for (const std::string& edgeID : edgeIDs) {
348 6 : NBEdge* edge = myEdgeCont.retrieve(edgeID);
349 6 : if (edge == nullptr) {
350 0 : reportError(TL("Edge '%' in route of line '%' not found"), edgeID, myCurrentLine->getLineID());
351 : } else {
352 6 : edges.push_back(edge);
353 : }
354 : }
355 3 : myRouteEdges[myCurrentRouteID] = edges;
356 3 : }
357 :
358 :
359 : void
360 34 : NIXMLPTHandler::addPTLineStop(const SUMOSAXAttributes& attrs) {
361 34 : bool ok = true;
362 34 : const std::string id = attrs.hasAttribute(SUMO_ATTR_ID)
363 34 : ? attrs.get<std::string>(SUMO_ATTR_ID, "ptLine", ok)
364 34 : : attrs.get<std::string>(SUMO_ATTR_BUS_STOP, "ptline", ok);
365 102 : std::shared_ptr<NBPTStop> stop = myStopCont.get(id);
366 34 : if (stop == nullptr) {
367 2 : if (!NBPTStopCont::wasIgnored(id)) {
368 0 : WRITE_ERRORF(TL("Stop '%' within line '%' not found"), id, toString(myCurrentLine->getLineID()));
369 : }
370 : return;
371 : }
372 64 : myCurrentLine->addPTStop(stop);
373 : }
374 :
375 : void
376 6 : NIXMLPTHandler::addRouteStop(const SUMOSAXAttributes& attrs) {
377 : assert(myCurrentRouteID != "");
378 6 : bool ok = true;
379 6 : const std::string id = attrs.hasAttribute(SUMO_ATTR_ID)
380 6 : ? attrs.get<std::string>(SUMO_ATTR_ID, "ptLine", ok)
381 6 : : attrs.get<std::string>(SUMO_ATTR_BUS_STOP, "ptline", ok);
382 18 : std::shared_ptr<NBPTStop> stop = myStopCont.get(id);
383 6 : if (stop == nullptr) {
384 0 : WRITE_ERRORF(TL("Stop '%' within route '%' not found"), id, toString(myCurrentRouteID));
385 : return;
386 : }
387 6 : myRouteStops[myCurrentRouteID].push_back(stop);
388 : }
389 :
390 :
391 : /****************************************************************************/
|