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