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 2337 : NBPTStopCont::~NBPTStopCont() {
44 : myPTStops.clear();
45 2337 : }
46 :
47 :
48 : bool
49 2680 : NBPTStopCont::insert(std::shared_ptr<NBPTStop> ptStop, bool floating) {
50 2680 : std::string id = ptStop->getID();
51 : auto i = myPTStops.find(id);
52 2680 : if (i != myPTStops.end()) {
53 : return false;
54 : }
55 2605 : myPTStops[id] = ptStop;
56 2605 : if (floating) {
57 127 : myFloatingStops.push_back(ptStop);
58 : }
59 : return true;
60 : }
61 :
62 :
63 : std::shared_ptr<NBPTStop>
64 13749 : NBPTStopCont::get(std::string id) const {
65 13749 : if (myPTStops.find(id) != myPTStops.end()) {
66 : return myPTStops.find(id)->second;
67 : }
68 : return nullptr;
69 : }
70 :
71 :
72 : void
73 106 : 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 1678 : for (const auto& ptStopIt : myPTStops) {
77 : std::shared_ptr<NBPTStop> const stop = ptStopIt.second;
78 1572 : bool multipleStopPositions = stop->getIsMultipleStopPositions();
79 1572 : bool platformsDefined = !stop->getPlatformCands().empty();
80 1572 : if (!platformsDefined) {
81 : //create pt stop for reverse edge if edge exists
82 2258 : std::shared_ptr<NBPTStop> reverseStop = getReverseStop(stop, cont);
83 1129 : if (reverseStop != nullptr) {
84 255 : reverseStops.push_back(reverseStop);
85 : }
86 443 : } else if (multipleStopPositions) {
87 : //create pt stop for closest platform at corresponding edge
88 822 : 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 64 : std::shared_ptr<NBPTStop> additionalStop = assignAndCreatNewPTStopAsNeeded(stop, cont);
92 32 : if (additionalStop != nullptr) {
93 0 : reverseStops.push_back(additionalStop);
94 : }
95 : }
96 : }
97 : //insert new stops if any
98 361 : for (std::shared_ptr<NBPTStop>& reverseStop : reverseStops) {
99 510 : insert(reverseStop);
100 : }
101 106 : }
102 :
103 :
104 : void
105 293 : NBPTStopCont::assignLanes(NBEdgeCont& cont) {
106 : //scnd pass set correct lane
107 4977 : for (auto i = myPTStops.begin(); i != myPTStops.end();) {
108 : std::shared_ptr<NBPTStop> stop = i->second;
109 4684 : 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 293 : }
120 :
121 :
122 : int
123 138 : NBPTStopCont::generateBidiStops(NBEdgeCont& ec) {
124 : //scnd pass set correct lane
125 : int existingBidiStops = 0;
126 : std::vector<std::shared_ptr<NBPTStop> > toAdd;
127 2441 : for (auto i = myPTStops.begin(); i != myPTStops.end(); i++) {
128 : std::shared_ptr<NBPTStop> stop = i->second;
129 2303 : NBEdge* edge = ec.getByID(stop->getEdgeId());
130 2303 : if (edge != nullptr && edge->isBidiRail()) {
131 336 : NBEdge* bidiEdge = edge->getTurnDestination(true);
132 : assert(bidiEdge != 0);
133 672 : const std::string id = getReverseID(stop->getID());
134 176 : if (myPTStops.count(id) > 0) {
135 176 : 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 160 : bidiEdge->getID(),
144 320 : stop->getOrigEdgeId(),
145 320 : stop->getLength(),
146 320 : stop->getName(),
147 480 : stop->getPermissions());
148 160 : if (bidiStop->findLaneAndComputeBusStopExtent(ec)) {
149 160 : toAdd.push_back(bidiStop);
150 160 : stop->setBidiStop(bidiStop);
151 160 : bidiStop->setBidiStop(stop);
152 : } else {
153 : // should not happen
154 : assert(false);
155 : }
156 1967 : } else if (edge != nullptr) {
157 1967 : NBEdge* bidiEdge = edge->getTurnDestination(true);
158 1967 : if (bidiEdge != nullptr) {
159 1756 : const std::string id = getReverseID(stop->getID());
160 : if (myPTStops.count(id) > 0) {
161 389 : existingBidiStops++;
162 : }
163 : }
164 : }
165 : }
166 298 : for (std::shared_ptr<NBPTStop> newStop : toAdd) {
167 320 : myPTStops[newStop->getID()] = newStop;
168 : }
169 138 : if (toAdd.size() > 0) {
170 50 : WRITE_MESSAGEF(TL("Added % stops for superposed rail edges."), toString(toAdd.size()));
171 : }
172 138 : return (int)toAdd.size() + existingBidiStops;
173 138 : }
174 :
175 :
176 : std::shared_ptr<NBPTStop>
177 1219 : NBPTStopCont::getReverseStop(std::shared_ptr<NBPTStop> pStop, const NBEdgeCont& ec) {
178 1219 : std::string edgeId = pStop->getEdgeId();
179 1219 : NBEdge* edge = ec.getByID(edgeId);
180 1219 : NBEdge* reverse = NBPTStopCont::getReverseEdge(edge);
181 1219 : if (reverse != nullptr) {
182 688 : const std::string reverseID = getReverseID(pStop->getID());
183 : if (myPTStops.count(reverseID) == 0) {
184 269 : return std::make_shared<NBPTStop>(reverseID, pStop->getPosition(), reverse->getID(), reverse->getID(),
185 962 : pStop->getLength(), pStop->getName(), pStop->getPermissions());
186 : } else {
187 75 : return myPTStops[reverseID];
188 : }
189 : }
190 : return nullptr;
191 : }
192 :
193 :
194 : std::shared_ptr<NBPTStop>
195 32 : NBPTStopCont::assignAndCreatNewPTStopAsNeeded(std::shared_ptr<NBPTStop> pStop, NBEdgeCont& cont) {
196 32 : std::string edgeId = pStop->getEdgeId();
197 32 : NBEdge* edge = cont.getByID(edgeId);
198 32 : if (edge == nullptr) {
199 : return nullptr;
200 : }
201 : bool rightOfEdge = false;
202 : bool leftOfEdge = false;
203 : const NBPTPlatform* left = nullptr;
204 63 : for (const NBPTPlatform& platform : pStop->getPlatformCands()) {
205 32 : double crossProd = computeCrossProductEdgePosition(edge, platform.getPos());
206 : //TODO consider driving on the left!!! [GL May '17]
207 32 : if (crossProd > 0) {
208 : leftOfEdge = true;
209 : left = &platform;
210 : } else {
211 : rightOfEdge = true;
212 31 : pStop->setPTStopLength(platform.getLength());
213 : }
214 : }
215 :
216 31 : 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 30 : } 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 411 : NBPTStopCont::assignPTStopToEdgeOfClosestPlatform(std::shared_ptr<NBPTStop> pStop, NBEdgeCont& cont) {
236 411 : std::string edgeId = pStop->getEdgeId();
237 411 : NBEdge* edge = cont.getByID(edgeId);
238 411 : NBEdge* reverse = NBPTStopCont::getReverseEdge(edge);
239 822 : const NBPTPlatform* closestPlatform = getClosestPlatformToPTStopPosition(pStop);
240 411 : pStop->setPTStopLength(closestPlatform->getLength());
241 411 : 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 151 : double crossProd = computeCrossProductEdgePosition(edge, closestPlatform->getPos());
248 :
249 : //TODO consider driving on the left!!! [GL May '17]
250 151 : if (crossProd > 0) { //pt stop is on the left of the orig edge
251 189 : pStop->setEdgeId(reverse->getID(), cont);
252 : }
253 : }
254 411 : }
255 :
256 :
257 : double
258 183 : NBPTStopCont::computeCrossProductEdgePosition(const NBEdge* edge, const Position& closestPlatform) const {
259 : PositionVector geom = edge->getGeometry();
260 183 : int idxTmp = geom.indexOfClosest(closestPlatform);
261 183 : double offset = geom.nearest_offset_to_point2D(closestPlatform, true);
262 183 : double offset2 = geom.offsetAtIndex2D(idxTmp);
263 : int idx1, idx2;
264 183 : if (offset2 < offset) {
265 : idx1 = idxTmp;
266 71 : idx2 = idx1 + 1;
267 : } else {
268 : idx2 = idxTmp;
269 112 : idx1 = idxTmp - 1;
270 : }
271 183 : if (idx1 < 0 || idx1 >= (int) geom.size() || idx2 < 0 || idx2 >= (int) geom.size()) {
272 99 : WRITE_WARNINGF(TL("Could not determine cross product for edge '%'."), edge->getID());
273 33 : return 0;
274 : }
275 150 : Position p1 = geom[idx1];
276 150 : 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 150 : double crossProd = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
285 : return crossProd;
286 183 : }
287 :
288 :
289 : const NBPTPlatform*
290 411 : NBPTStopCont::getClosestPlatformToPTStopPosition(std::shared_ptr<NBPTStop> pStop) {
291 411 : Position stopPosition = pStop->getPosition();
292 : const NBPTPlatform* closest = nullptr;
293 : double minSqrDist = std::numeric_limits<double>::max();
294 1557 : for (const NBPTPlatform& platform : pStop->getPlatformCands()) {
295 1146 : double sqrDist = stopPosition.distanceSquaredTo2D(platform.getPos());
296 1146 : if (sqrDist < minSqrDist) {
297 : minSqrDist = sqrDist;
298 : closest = &platform;
299 : }
300 : }
301 411 : return closest;
302 : }
303 :
304 : //static functions
305 :
306 : NBEdge*
307 1950 : NBPTStopCont::getReverseEdge(NBEdge* edge) {
308 1950 : if (edge != nullptr) {
309 1765 : for (auto it = edge->getToNode()->getOutgoingEdges().begin();
310 3240 : it != edge->getToNode()->getOutgoingEdges().end();
311 : it++) {
312 2187 : if ((*it)->getToNode() == edge->getFromNode()) {
313 : return (*it);
314 : }
315 : }
316 : }
317 : return nullptr;
318 : }
319 :
320 :
321 : int
322 2007 : NBPTStopCont::cleanupDeleted(NBEdgeCont& cont) {
323 : int numDeleted = 0;
324 6356 : for (auto i = myPTStops.begin(); i != myPTStops.end();) {
325 4349 : if (cont.getByID(i->second->getEdgeId()) == nullptr) {
326 522 : WRITE_WARNINGF(TL("Removing pt stop '%' on non existing edge '%'."), i->first, i->second->getEdgeId());
327 : i = myPTStops.erase(i);
328 174 : numDeleted++;
329 : } else {
330 : i++;
331 : }
332 : }
333 2007 : return numDeleted;
334 : }
335 :
336 :
337 : void
338 1747 : NBPTStopCont::addEdges2Keep(const OptionsCont& /* oc */, std::set<std::string>& into) {
339 4062 : for (auto stop : myPTStops) {
340 2315 : into.insert(stop.second->getEdgeId());
341 2315 : }
342 1747 : }
343 :
344 :
345 : void
346 9409 : NBPTStopCont::replaceEdge(const std::string& edgeID, const EdgeVector& replacement) {
347 9409 : if (myPTStops.size() > 0 && myPTStopLookup.size() == 0) {
348 : // init lookup once
349 1675 : for (auto& item : myPTStops) {
350 1622 : myPTStopLookup[item.second->getEdgeId()].push_back(item.second);
351 : }
352 : }
353 : // make a copy because the vector gets modified
354 9409 : const std::vector<std::shared_ptr<NBPTStop> > stops = myPTStopLookup[edgeID];
355 10192 : for (std::shared_ptr<NBPTStop> stop : stops) {
356 783 : 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 783 : myPTStopLookup[stop->getEdgeId()].push_back(stop);
360 : }
361 : }
362 : myPTStopLookup.erase(edgeID);
363 9409 : }
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 1771 : NBPTStopCont::getReverseID(const std::string& id) {
379 1771 : return id.size() > 0 && id[0] == '-' ? id.substr(1) : "-" + id;
380 : }
381 :
382 : void
383 138 : NBPTStopCont::alignIdSigns() {
384 : PTStopsCont stops = myPTStops;
385 2420 : for (auto& i : stops) {
386 : std::shared_ptr<NBPTStop> s = i.second;
387 2282 : const std::string& stopId = s->getID();
388 4564 : if (s->getEdgeId() == "") {
389 : continue;
390 : }
391 4564 : const char edgeSign = s->getEdgeId().at(0);
392 2282 : const char stopSign = stopId.at(0);
393 2282 : if (edgeSign != stopSign && (edgeSign == '-' || stopSign == '-')) {
394 213 : const std::string reverseID = getReverseID(stopId);
395 426 : std::shared_ptr<NBPTStop> rs = get(reverseID);
396 213 : s->setPTStopId(reverseID);
397 : myPTStops.erase(stopId);
398 213 : myPTStops[reverseID] = s;
399 213 : if (rs != nullptr) {
400 0 : rs->setPTStopId(stopId);
401 73 : myPTStops[stopId] = rs;
402 : }
403 : }
404 : }
405 138 : }
406 :
407 : void
408 153 : NBPTStopCont::assignEdgeForFloatingStops(NBEdgeCont& cont, double maxRadius) {
409 : NamedRTree r;
410 : SVCPermissions publicPermissions = SVC_BUS | SVC_TRAM | SVC_RAIL | SVC_RAIL_URBAN | SVC_TAXI;
411 61507 : for (const auto& item : cont) {
412 61354 : NBEdge* edge = item.second;
413 61354 : if ((edge->getPermissions() & publicPermissions) == 0) {
414 31063 : continue;
415 : }
416 30291 : const Boundary& bound = edge->getGeometry().getBoxBoundary();
417 30291 : float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
418 30291 : float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
419 30291 : r.Insert(min, max, edge);
420 30291 : }
421 280 : for (std::shared_ptr<NBPTStop> ptStop : myFloatingStops) {
422 : std::set<const Named*> edges;
423 : Named::StoringVisitor visitor(edges);
424 127 : const Position& pos = ptStop->getPosition();
425 127 : float min[2] = {static_cast<float>(pos.x() - maxRadius), static_cast<float>(pos.y() - maxRadius)};
426 127 : 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 296 : for (const Named* namedEdge : edges) {
430 169 : NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
431 169 : if ((e->getPermissions() & ptStop->getPermissions()) != 0) {
432 81 : nearby.push_back(e);
433 : }
434 : }
435 127 : 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 127 : for (NBEdge* e : nearby) {
440 60 : ptStop->setEdgeId(e->getID(), cont);
441 20 : if (ptStop->getLaneId() != "") {
442 : break;
443 : }
444 : }
445 127 : if (ptStop->getLaneId() == "") {
446 214 : 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 214 : myPTStops.erase(ptStop->getID());
449 : }
450 : }
451 153 : }
452 :
453 : void
454 140 : NBPTStopCont::findAccessEdgesForRailStops(NBEdgeCont& cont, double maxRadius, int maxCount, double accessFactor) {
455 : NamedRTree r;
456 49321 : for (auto edge : cont) {
457 49181 : const Boundary& bound = edge.second->getGeometry().getBoxBoundary();
458 49181 : float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
459 49181 : float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
460 49181 : r.Insert(min, max, edge.second);
461 49181 : }
462 2607 : for (auto& ptStop : myPTStops) {
463 2467 : const std::string& stopEdgeID = ptStop.second->getEdgeId();
464 2467 : NBEdge* stopEdge = cont.getByID(stopEdgeID);
465 : //std::cout << "findAccessEdgesForRailStops edge=" << stopEdgeID << " exists=" << (stopEdge != 0) << "\n";
466 2467 : 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 1408 : const Position& pos = ptStop.second->getPosition();
471 1408 : float min[2] = {static_cast<float>(pos.x() - maxRadius), static_cast<float>(pos.y() - maxRadius)};
472 1408 : 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 187716 : for (const Named* namedEdge : edges) {
476 186308 : NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
477 186308 : edgCants.push_back(e);
478 : }
479 1408 : std::sort(edgCants.begin(), edgCants.end(), [pos](NBEdge * a, NBEdge * b) {
480 1762014 : return a->getLaneShape(0).distance2D(pos, false) < b->getLaneShape(0).distance2D(pos, false);
481 : });
482 : int cnt = 0;
483 15439 : for (auto edge : edgCants) {
484 : int laneIdx = 0;
485 27016 : for (auto lane : edge->getLanes()) {
486 16482 : if ((lane.permissions & SVC_PEDESTRIAN) != 0) {
487 4355 : double offset = lane.shape.nearest_offset_to_point2D(pos, false);
488 4355 : double finalLength = edge->getFinalLength();
489 4355 : double laneLength = lane.shape.length();
490 4355 : double accessLength = pos.distanceTo2D(lane.shape.positionAtOffset2D(offset)) * accessFactor;
491 4355 : ptStop.second->addAccess(edge->getLaneID(laneIdx), offset * finalLength / laneLength, accessLength);
492 4355 : cnt++;
493 : break;
494 : }
495 12127 : laneIdx++;
496 16482 : }
497 14889 : if (cnt == maxCount) {
498 : break;
499 : }
500 : }
501 : }
502 : }
503 140 : }
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 : /****************************************************************************/
|