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 NBPTStopCont.cpp
15 : /// @author Gregor Laemmel
16 : /// @date Tue, 20 Mar 2017
17 : ///
18 : // Container for pt stops during the netbuilding process
19 : /****************************************************************************/
20 : #include <config.h>
21 : #include <utils/common/MsgHandler.h>
22 : #include <utils/geom/Boundary.h>
23 : #include <utils/geom/Position.h>
24 : #include <utils/options/OptionsCont.h>
25 : #include <microsim/MSLane.h>
26 : #include "NBEdgeCont.h"
27 : #include "NBEdge.h"
28 : #include "NBNode.h"
29 : #include "NBPTPlatform.h"
30 : #include "NBPTStop.h"
31 : #include "NBPTStopCont.h"
32 :
33 :
34 : // ===========================================================================
35 : // static members
36 : // ===========================================================================
37 : std::set<std::string> NBPTStopCont::myIgnoredStops;
38 :
39 :
40 : // ===========================================================================
41 : // method definitions
42 : // ===========================================================================
43 1993 : NBPTStopCont::~NBPTStopCont() {
44 : myPTStops.clear();
45 1993 : }
46 :
47 :
48 : bool
49 2043 : NBPTStopCont::insert(std::shared_ptr<NBPTStop> ptStop, bool floating) {
50 2043 : std::string id = ptStop->getID();
51 : auto i = myPTStops.find(id);
52 2043 : if (i != myPTStops.end()) {
53 : return false;
54 : }
55 1982 : myPTStops[id] = ptStop;
56 1982 : if (floating) {
57 116 : myFloatingStops.push_back(ptStop);
58 : }
59 : return true;
60 : }
61 :
62 :
63 : std::shared_ptr<NBPTStop>
64 9410 : NBPTStopCont::get(std::string id) const {
65 9410 : if (myPTStops.find(id) != myPTStops.end()) {
66 : return myPTStops.find(id)->second;
67 : }
68 : return nullptr;
69 : }
70 :
71 :
72 : void
73 103 : NBPTStopCont::localizePTStops(NBEdgeCont& cont) {
74 : std::vector<std::shared_ptr<NBPTStop> > reverseStops;
75 : //first pass localize pt stop at correct side of the street; create stop for opposite side if needed
76 1218 : for (const auto& ptStopIt : myPTStops) {
77 : std::shared_ptr<NBPTStop> const stop = ptStopIt.second;
78 1115 : bool multipleStopPositions = stop->getIsMultipleStopPositions();
79 1115 : bool platformsDefined = !stop->getPlatformCands().empty();
80 1115 : if (!platformsDefined) {
81 : //create pt stop for reverse edge if edge exists
82 1660 : std::shared_ptr<NBPTStop> reverseStop = getReverseStop(stop, cont);
83 830 : if (reverseStop != nullptr) {
84 206 : reverseStops.push_back(reverseStop);
85 : }
86 285 : } else if (multipleStopPositions) {
87 : //create pt stop for closest platform at corresponding edge
88 536 : assignPTStopToEdgeOfClosestPlatform(stop, cont);
89 : } else {
90 : //create pt stop for each side of the street where a platform is defined (create additional pt stop as needed)
91 34 : std::shared_ptr<NBPTStop> additionalStop = assignAndCreatNewPTStopAsNeeded(stop, cont);
92 17 : if (additionalStop != nullptr) {
93 0 : reverseStops.push_back(additionalStop);
94 : }
95 : }
96 : }
97 : //insert new stops if any
98 309 : for (std::shared_ptr<NBPTStop>& reverseStop : reverseStops) {
99 412 : insert(reverseStop);
100 : }
101 103 : }
102 :
103 :
104 : void
105 284 : NBPTStopCont::assignLanes(NBEdgeCont& cont) {
106 : //scnd pass set correct lane
107 3769 : for (auto i = myPTStops.begin(); i != myPTStops.end();) {
108 : std::shared_ptr<NBPTStop> stop = i->second;
109 3485 : if (!stop->findLaneAndComputeBusStopExtent(cont)) {
110 12 : WRITE_WARNINGF(TL("Could not find corresponding edge or compatible lane for pt stop '%' (%). Thus, it will be removed!"),
111 : i->first, i->second->getName());
112 : //EdgeVector edgeVector = cont.getGeneratedFrom((*i).second->getOrigEdgeId());
113 : //std::cout << edgeVector.size() << std::endl;
114 : myPTStops.erase(i++);
115 : } else {
116 : i++;
117 : }
118 : }
119 284 : }
120 :
121 :
122 : int
123 133 : NBPTStopCont::generateBidiStops(NBEdgeCont& ec) {
124 : //scnd pass set correct lane
125 : int existingBidiStops = 0;
126 : std::vector<std::shared_ptr<NBPTStop> > toAdd;
127 1837 : for (auto i = myPTStops.begin(); i != myPTStops.end(); i++) {
128 : std::shared_ptr<NBPTStop> stop = i->second;
129 1704 : NBEdge* edge = ec.getByID(stop->getEdgeId());
130 1704 : if (edge != nullptr && edge->isBidiRail()) {
131 284 : NBEdge* bidiEdge = edge->getTurnDestination(true);
132 : assert(bidiEdge != 0);
133 568 : const std::string id = getReverseID(stop->getID());
134 150 : if (myPTStops.count(id) > 0) {
135 150 : if (myPTStops[id]->getEdgeId() != bidiEdge->getID()) {
136 0 : WRITE_WARNINGF(TL("Could not create reverse-direction stop for superposed edge '%' (origStop '%'). Stop id '%' already in use by stop on edge '%'."),
137 : bidiEdge->getID(), i->first, id, myPTStops[id]->getEdgeId());
138 : }
139 : continue;
140 : }
141 : std::shared_ptr<NBPTStop> bidiStop = std::make_shared<NBPTStop>(id,
142 : stop->getPosition(),
143 134 : bidiEdge->getID(),
144 268 : stop->getOrigEdgeId(),
145 268 : stop->getLength(),
146 268 : stop->getName(),
147 402 : stop->getPermissions());
148 134 : if (bidiStop->findLaneAndComputeBusStopExtent(ec)) {
149 134 : toAdd.push_back(bidiStop);
150 134 : stop->setBidiStop(bidiStop);
151 134 : bidiStop->setBidiStop(stop);
152 : } else {
153 : // should not happen
154 : assert(false);
155 : }
156 1420 : } else if (edge != nullptr) {
157 1420 : NBEdge* bidiEdge = edge->getTurnDestination(true);
158 1420 : if (bidiEdge != nullptr) {
159 1346 : const std::string id = getReverseID(stop->getID());
160 : if (myPTStops.count(id) > 0) {
161 329 : existingBidiStops++;
162 : }
163 : }
164 : }
165 : }
166 267 : for (std::shared_ptr<NBPTStop> newStop : toAdd) {
167 268 : myPTStops[newStop->getID()] = newStop;
168 : }
169 133 : if (toAdd.size() > 0) {
170 48 : WRITE_MESSAGEF(TL("Added % stops for superposed rail edges."), toString(toAdd.size()));
171 : }
172 133 : return (int)toAdd.size() + existingBidiStops;
173 133 : }
174 :
175 :
176 : std::shared_ptr<NBPTStop>
177 919 : NBPTStopCont::getReverseStop(std::shared_ptr<NBPTStop> pStop, const NBEdgeCont& ec) {
178 919 : std::string edgeId = pStop->getEdgeId();
179 919 : NBEdge* edge = ec.getByID(edgeId);
180 919 : NBEdge* reverse = NBPTStopCont::getReverseEdge(edge);
181 919 : if (reverse != nullptr) {
182 588 : const std::string reverseID = getReverseID(pStop->getID());
183 : if (myPTStops.count(reverseID) == 0) {
184 233 : return std::make_shared<NBPTStop>(reverseID, pStop->getPosition(), reverse->getID(), reverse->getID(),
185 699 : pStop->getLength(), pStop->getName(), pStop->getPermissions());
186 : } else {
187 61 : return myPTStops[reverseID];
188 : }
189 : }
190 : return nullptr;
191 : }
192 :
193 :
194 : std::shared_ptr<NBPTStop>
195 17 : NBPTStopCont::assignAndCreatNewPTStopAsNeeded(std::shared_ptr<NBPTStop> pStop, NBEdgeCont& cont) {
196 17 : std::string edgeId = pStop->getEdgeId();
197 17 : NBEdge* edge = cont.getByID(edgeId);
198 17 : if (edge == nullptr) {
199 : return nullptr;
200 : }
201 : bool rightOfEdge = false;
202 : bool leftOfEdge = false;
203 : const NBPTPlatform* left = nullptr;
204 33 : for (const NBPTPlatform& platform : pStop->getPlatformCands()) {
205 17 : double crossProd = computeCrossProductEdgePosition(edge, platform.getPos());
206 : //TODO consider driving on the left!!! [GL May '17]
207 17 : if (crossProd > 0) {
208 : leftOfEdge = true;
209 : left = &platform;
210 : } else {
211 : rightOfEdge = true;
212 16 : pStop->setPTStopLength(platform.getLength());
213 : }
214 : }
215 :
216 16 : if (leftOfEdge && rightOfEdge) {
217 2 : std::shared_ptr<NBPTStop> leftStop = getReverseStop(pStop, cont);
218 1 : if (leftStop) {
219 0 : leftStop->setPTStopLength(left->getLength());
220 : }
221 : return leftStop;
222 15 : } else if (leftOfEdge) {
223 0 : NBEdge* reverse = getReverseEdge(edge);
224 0 : if (reverse != nullptr) {
225 0 : pStop->setEdgeId(reverse->getID(), cont);
226 0 : pStop->setPTStopLength(left->getLength());
227 : }
228 : }
229 :
230 : return nullptr;
231 : }
232 :
233 :
234 : void
235 268 : NBPTStopCont::assignPTStopToEdgeOfClosestPlatform(std::shared_ptr<NBPTStop> pStop, NBEdgeCont& cont) {
236 268 : std::string edgeId = pStop->getEdgeId();
237 268 : NBEdge* edge = cont.getByID(edgeId);
238 268 : NBEdge* reverse = NBPTStopCont::getReverseEdge(edge);
239 536 : const NBPTPlatform* closestPlatform = getClosestPlatformToPTStopPosition(pStop);
240 268 : pStop->setPTStopLength(closestPlatform->getLength());
241 268 : if (reverse != nullptr) {
242 :
243 : //TODO make isLeft in PositionVector static [GL May '17]
244 : // if (PositionVector::isLeft(edge->getFromNode()->getPosition(),edge->getToNode()->getPosition(),closestPlatform)){
245 : //
246 : // }
247 99 : double crossProd = computeCrossProductEdgePosition(edge, closestPlatform->getPos());
248 :
249 : //TODO consider driving on the left!!! [GL May '17]
250 99 : if (crossProd > 0) { //pt stop is on the left of the orig edge
251 123 : pStop->setEdgeId(reverse->getID(), cont);
252 : }
253 : }
254 268 : }
255 :
256 :
257 : double
258 116 : NBPTStopCont::computeCrossProductEdgePosition(const NBEdge* edge, const Position& closestPlatform) const {
259 : PositionVector geom = edge->getGeometry();
260 116 : int idxTmp = geom.indexOfClosest(closestPlatform);
261 116 : double offset = geom.nearest_offset_to_point2D(closestPlatform, true);
262 116 : double offset2 = geom.offsetAtIndex2D(idxTmp);
263 : int idx1, idx2;
264 116 : if (offset2 < offset) {
265 : idx1 = idxTmp;
266 41 : idx2 = idx1 + 1;
267 : } else {
268 : idx2 = idxTmp;
269 75 : idx1 = idxTmp - 1;
270 : }
271 116 : if (idx1 < 0 || idx1 >= (int) geom.size() || idx2 < 0 || idx2 >= (int) geom.size()) {
272 63 : WRITE_WARNINGF(TL("Could not determine cross product for edge '%'."), edge->getID());
273 21 : return 0;
274 : }
275 95 : Position p1 = geom[idx1];
276 95 : Position p2 = geom[idx2];
277 :
278 : double x0 = p1.x();
279 : double y0 = p1.y();
280 : double x1 = p2.x();
281 : double y1 = p2.y();
282 : double x2 = closestPlatform.x();
283 : double y2 = closestPlatform.y();
284 95 : double crossProd = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
285 : return crossProd;
286 116 : }
287 :
288 :
289 : const NBPTPlatform*
290 268 : NBPTStopCont::getClosestPlatformToPTStopPosition(std::shared_ptr<NBPTStop> pStop) {
291 268 : Position stopPosition = pStop->getPosition();
292 : const NBPTPlatform* closest = nullptr;
293 : double minSqrDist = std::numeric_limits<double>::max();
294 993 : for (const NBPTPlatform& platform : pStop->getPlatformCands()) {
295 725 : double sqrDist = stopPosition.distanceSquaredTo2D(platform.getPos());
296 725 : if (sqrDist < minSqrDist) {
297 : minSqrDist = sqrDist;
298 : closest = &platform;
299 : }
300 : }
301 268 : return closest;
302 : }
303 :
304 : //static functions
305 :
306 : NBEdge*
307 1429 : NBPTStopCont::getReverseEdge(NBEdge* edge) {
308 1429 : if (edge != nullptr) {
309 1259 : for (auto it = edge->getToNode()->getOutgoingEdges().begin();
310 2244 : it != edge->getToNode()->getOutgoingEdges().end();
311 : it++) {
312 1557 : if ((*it)->getToNode() == edge->getFromNode()) {
313 : return (*it);
314 : }
315 : }
316 : }
317 : return nullptr;
318 : }
319 :
320 :
321 : int
322 1861 : NBPTStopCont::cleanupDeleted(NBEdgeCont& cont) {
323 : int numDeleted = 0;
324 5054 : for (auto i = myPTStops.begin(); i != myPTStops.end();) {
325 3193 : if (cont.getByID(i->second->getEdgeId()) == nullptr) {
326 483 : WRITE_WARNINGF(TL("Removing pt stop '%' on non existing edge '%'."), i->first, i->second->getEdgeId());
327 : i = myPTStops.erase(i);
328 161 : numDeleted++;
329 : } else {
330 : i++;
331 : }
332 : }
333 1861 : return numDeleted;
334 : }
335 :
336 :
337 : void
338 1609 : NBPTStopCont::addEdges2Keep(const OptionsCont& /* oc */, std::set<std::string>& into) {
339 3325 : for (auto stop : myPTStops) {
340 1716 : into.insert(stop.second->getEdgeId());
341 1716 : }
342 1609 : }
343 :
344 :
345 : void
346 5471 : NBPTStopCont::replaceEdge(const std::string& edgeID, const EdgeVector& replacement) {
347 5471 : if (myPTStops.size() > 0 && myPTStopLookup.size() == 0) {
348 : // init lookup once
349 1068 : for (auto& item : myPTStops) {
350 1022 : myPTStopLookup[item.second->getEdgeId()].push_back(item.second);
351 : }
352 : }
353 : // make a copy because the vector gets modified
354 5471 : const std::vector<std::shared_ptr<NBPTStop> > stops = myPTStopLookup[edgeID];
355 5935 : for (std::shared_ptr<NBPTStop> stop : stops) {
356 464 : if (!stop->replaceEdge(edgeID, replacement)) {
357 0 : WRITE_WARNINGF(TL("Could not re-assign pt stop '%' after replacing edge '%'."), stop->getID(), edgeID);
358 : } else {
359 464 : myPTStopLookup[stop->getEdgeId()].push_back(stop);
360 : }
361 : }
362 : myPTStopLookup.erase(edgeID);
363 5471 : }
364 :
365 :
366 : void
367 2 : NBPTStopCont::postprocess(std::set<std::string>& usedStops) {
368 29 : for (auto i = myPTStops.begin(); i != myPTStops.end();) {
369 54 : if (usedStops.find(i->second->getID()) == usedStops.end()) {
370 : myPTStops.erase(i++);
371 : } else {
372 : i++;
373 : }
374 : }
375 2 : }
376 :
377 : std::string
378 1413 : NBPTStopCont::getReverseID(const std::string& id) {
379 1413 : return id.size() > 0 && id[0] == '-' ? id.substr(1) : "-" + id;
380 : }
381 :
382 : void
383 133 : NBPTStopCont::alignIdSigns() {
384 : PTStopsCont stops = myPTStops;
385 1817 : for (auto& i : stops) {
386 : std::shared_ptr<NBPTStop> s = i.second;
387 1684 : const std::string& stopId = s->getID();
388 1684 : if (s->getEdgeId() == "") {
389 : continue;
390 : }
391 3368 : const char edgeSign = s->getEdgeId().at(0);
392 1684 : const char stopSign = stopId.at(0);
393 1684 : if (edgeSign != stopSign && (edgeSign == '-' || stopSign == '-')) {
394 162 : const std::string reverseID = getReverseID(stopId);
395 324 : std::shared_ptr<NBPTStop> rs = get(reverseID);
396 162 : s->setPTStopId(reverseID);
397 : myPTStops.erase(stopId);
398 162 : myPTStops[reverseID] = s;
399 162 : if (rs != nullptr) {
400 0 : rs->setPTStopId(stopId);
401 57 : myPTStops[stopId] = rs;
402 : }
403 : }
404 : }
405 133 : }
406 :
407 : void
408 149 : NBPTStopCont::assignEdgeForFloatingStops(NBEdgeCont& cont, double maxRadius) {
409 : NamedRTree r;
410 : SVCPermissions publicPermissions = SVC_BUS | SVC_TRAM | SVC_RAIL | SVC_RAIL_URBAN | SVC_TAXI;
411 39949 : for (const auto& item : cont) {
412 39800 : NBEdge* edge = item.second;
413 39800 : if ((edge->getPermissions() & publicPermissions) == 0) {
414 18814 : continue;
415 : }
416 20986 : const Boundary& bound = edge->getGeometry().getBoxBoundary();
417 20986 : float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
418 20986 : float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
419 20986 : r.Insert(min, max, edge);
420 20986 : }
421 265 : for (std::shared_ptr<NBPTStop> ptStop : myFloatingStops) {
422 : std::set<const Named*> edges;
423 : Named::StoringVisitor visitor(edges);
424 116 : const Position& pos = ptStop->getPosition();
425 116 : float min[2] = {static_cast<float>(pos.x() - maxRadius), static_cast<float>(pos.y() - maxRadius)};
426 116 : float max[2] = {static_cast<float>(pos.x() + maxRadius), static_cast<float>(pos.y() + maxRadius)};
427 : r.Search(min, max, visitor);
428 : std::vector<NBEdge*> nearby;
429 271 : for (const Named* namedEdge : edges) {
430 155 : NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
431 155 : if ((e->getPermissions() & ptStop->getPermissions()) != 0) {
432 81 : nearby.push_back(e);
433 : }
434 : }
435 116 : std::sort(nearby.begin(), nearby.end(), [pos](NBEdge * a, NBEdge * b) {
436 147 : return a->getLaneShape(0).distance2D(pos, false) < b->getLaneShape(0).distance2D(pos, false);
437 : });
438 :
439 116 : for (NBEdge* e : nearby) {
440 60 : ptStop->setEdgeId(e->getID(), cont);
441 20 : if (ptStop->getLaneId() != "") {
442 : break;
443 : }
444 : }
445 116 : if (ptStop->getLaneId() == "") {
446 192 : WRITE_WARNINGF(TL("Could not find corresponding edge or compatible lane for free-floating pt stop '%' (%). Thus, it will be removed!"),
447 : ptStop->getID(), ptStop->getName());
448 192 : myPTStops.erase(ptStop->getID());
449 : }
450 116 : }
451 149 : }
452 :
453 : void
454 135 : NBPTStopCont::findAccessEdgesForRailStops(NBEdgeCont& cont, double maxRadius, int maxCount, double accessFactor) {
455 : NamedRTree r;
456 32883 : for (auto edge : cont) {
457 32748 : const Boundary& bound = edge.second->getGeometry().getBoxBoundary();
458 32748 : float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
459 32748 : float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
460 32748 : r.Insert(min, max, edge.second);
461 32748 : }
462 1977 : for (auto& ptStop : myPTStops) {
463 1842 : const std::string& stopEdgeID = ptStop.second->getEdgeId();
464 1842 : NBEdge* stopEdge = cont.getByID(stopEdgeID);
465 : //std::cout << "findAccessEdgesForRailStops edge=" << stopEdgeID << " exists=" << (stopEdge != 0) << "\n";
466 1842 : if (stopEdge != nullptr && (stopEdge->getPermissions() & SVC_PEDESTRIAN) == 0) {
467 : //if (stopEdge != 0 && isRailway(stopEdge->getPermissions())) {
468 : std::set<const Named*> edges;
469 : Named::StoringVisitor visitor(edges);
470 1064 : const Position& pos = ptStop.second->getPosition();
471 1064 : float min[2] = {static_cast<float>(pos.x() - maxRadius), static_cast<float>(pos.y() - maxRadius)};
472 1064 : float max[2] = {static_cast<float>(pos.x() + maxRadius), static_cast<float>(pos.y() + maxRadius)};
473 : r.Search(min, max, visitor);
474 : std::vector<NBEdge*> edgCants;
475 107033 : for (const Named* namedEdge : edges) {
476 105969 : NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
477 105969 : edgCants.push_back(e);
478 : }
479 1064 : std::sort(edgCants.begin(), edgCants.end(), [pos](NBEdge * a, NBEdge * b) {
480 976796 : return a->getLaneShape(0).distance2D(pos, false) < b->getLaneShape(0).distance2D(pos, false);
481 : });
482 : int cnt = 0;
483 12107 : for (auto edge : edgCants) {
484 : int laneIdx = 0;
485 21962 : for (auto lane : edge->getLanes()) {
486 13155 : if ((lane.permissions & SVC_PEDESTRIAN) != 0) {
487 2779 : double offset = lane.shape.nearest_offset_to_point2D(pos, false);
488 2779 : double finalLength = edge->getFinalLength();
489 2779 : double laneLength = lane.shape.length();
490 2779 : double accessLength = pos.distanceTo2D(lane.shape.positionAtOffset2D(offset)) * accessFactor;
491 2779 : ptStop.second->addAccess(edge->getLaneID(laneIdx), offset * finalLength / laneLength, accessLength);
492 2779 : cnt++;
493 : break;
494 : }
495 10376 : laneIdx++;
496 13155 : }
497 11586 : if (cnt == maxCount) {
498 : break;
499 : }
500 : }
501 1064 : }
502 : }
503 135 : }
504 :
505 :
506 : std::shared_ptr<NBPTStop>
507 5 : NBPTStopCont::findStop(const std::string& origEdgeID, Position pos, double threshold) const {
508 123 : for (auto& item : myPTStops) {
509 245 : if (item.second->getOrigEdgeId() == origEdgeID &&
510 5 : item.second->getPosition().distanceTo2D(pos) < threshold) {
511 : return item.second;
512 : }
513 : }
514 : return nullptr;
515 : }
516 :
517 :
518 : /****************************************************************************/
|