Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2025 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 2113 : NBPTStopCont::~NBPTStopCont() {
44 : myPTStops.clear();
45 2113 : }
46 :
47 :
48 : bool
49 2094 : NBPTStopCont::insert(std::shared_ptr<NBPTStop> ptStop, bool floating) {
50 2094 : std::string id = ptStop->getID();
51 : auto i = myPTStops.find(id);
52 2094 : if (i != myPTStops.end()) {
53 : return false;
54 : }
55 2031 : myPTStops[id] = ptStop;
56 2031 : if (floating) {
57 124 : myFloatingStops.push_back(ptStop);
58 : }
59 : return true;
60 : }
61 :
62 :
63 : std::shared_ptr<NBPTStop>
64 9520 : NBPTStopCont::get(std::string id) const {
65 9520 : if (myPTStops.find(id) != myPTStops.end()) {
66 : return myPTStops.find(id)->second;
67 : }
68 : return nullptr;
69 : }
70 :
71 :
72 : void
73 120 : 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 1268 : for (const auto& ptStopIt : myPTStops) {
77 : std::shared_ptr<NBPTStop> const stop = ptStopIt.second;
78 1148 : bool multipleStopPositions = stop->getIsMultipleStopPositions();
79 1148 : bool platformsDefined = !stop->getPlatformCands().empty();
80 1148 : if (!platformsDefined) {
81 : //create pt stop for reverse edge if edge exists
82 1722 : std::shared_ptr<NBPTStop> reverseStop = getReverseStop(stop, cont);
83 861 : if (reverseStop != nullptr) {
84 212 : reverseStops.push_back(reverseStop);
85 : }
86 287 : } else if (multipleStopPositions) {
87 : //create pt stop for closest platform at corresponding edge
88 540 : 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 332 : for (std::shared_ptr<NBPTStop>& reverseStop : reverseStops) {
99 424 : insert(reverseStop);
100 : }
101 120 : }
102 :
103 :
104 : void
105 325 : NBPTStopCont::assignLanes(NBEdgeCont& cont) {
106 : //scnd pass set correct lane
107 3983 : for (auto i = myPTStops.begin(); i != myPTStops.end();) {
108 : std::shared_ptr<NBPTStop> stop = i->second;
109 3658 : if (!stop->findLaneAndComputeBusStopExtent(cont)) {
110 6 : 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 325 : }
120 :
121 :
122 : int
123 156 : NBPTStopCont::generateBidiStops(NBEdgeCont& ec) {
124 : int existingBidiStops = 0;
125 : std::vector<std::shared_ptr<NBPTStop> > toAdd;
126 2003 : for (auto i = myPTStops.begin(); i != myPTStops.end(); i++) {
127 : std::shared_ptr<NBPTStop> stop = i->second;
128 1847 : NBEdge* edge = ec.getByID(stop->getEdgeId());
129 1847 : if (edge != nullptr && edge->isBidiRail()) {
130 287 : NBEdge* bidiEdge = edge->getTurnDestination(true);
131 : assert(bidiEdge != 0);
132 574 : const std::string id = getReverseID(stop->getID());
133 154 : if (myPTStops.count(id) > 0) {
134 154 : if (myPTStops[id]->getEdgeId() != bidiEdge->getID()) {
135 0 : WRITE_WARNINGF(TL("Could not create reverse-direction stop for superposed edge '%' (origStop '%'). Stop id '%' already in use by stop on edge '%'."),
136 : bidiEdge->getID(), i->first, id, myPTStops[id]->getEdgeId());
137 : }
138 : continue;
139 : }
140 : std::shared_ptr<NBPTStop> bidiStop = std::make_shared<NBPTStop>(id,
141 : stop->getPosition(),
142 133 : bidiEdge->getID(),
143 266 : stop->getOrigEdgeId(),
144 266 : stop->getLength(),
145 266 : stop->getName(),
146 399 : stop->getPermissions());
147 133 : if (bidiStop->findLaneAndComputeBusStopExtent(ec)) {
148 133 : toAdd.push_back(bidiStop);
149 133 : stop->setBidiStop(bidiStop);
150 133 : bidiStop->setBidiStop(stop);
151 : } else {
152 : // should not happen
153 : assert(false);
154 : }
155 1560 : } else if (edge != nullptr) {
156 1457 : NBEdge* bidiEdge = edge->getTurnDestination(true);
157 1457 : if (bidiEdge != nullptr) {
158 1366 : const std::string id = getReverseID(stop->getID());
159 : if (myPTStops.count(id) > 0) {
160 335 : existingBidiStops++;
161 : }
162 : }
163 : }
164 : }
165 289 : for (std::shared_ptr<NBPTStop> newStop : toAdd) {
166 266 : myPTStops[newStop->getID()] = newStop;
167 : }
168 156 : if (toAdd.size() > 0) {
169 48 : WRITE_MESSAGEF(TL("Added % stops for superposed rail edges."), toString(toAdd.size()));
170 : }
171 156 : return (int)toAdd.size() + existingBidiStops;
172 156 : }
173 :
174 :
175 : int
176 2 : NBPTStopCont::countBidiStops(NBEdgeCont& ec) const {
177 : int existingBidiStops = 0;
178 6 : for (auto item : myPTStops) {
179 : auto stop = item.second;
180 4 : NBEdge* edge = ec.getByID(stop->getEdgeId());
181 4 : if (edge != nullptr && edge->isBidiRail()) {
182 4 : NBEdge* bidiEdge = edge->getTurnDestination(true);
183 : assert(bidiEdge != 0);
184 8 : const std::string id = getReverseID(stop->getID());
185 : // @note loaded pairs of bidi-stops might have arbitrary ids and we should rather search through all stops on bidiEdge
186 : auto it = myPTStops.find(id);
187 4 : if (it != myPTStops.end() && it->second->getEdgeId() == bidiEdge->getID()) {
188 0 : existingBidiStops++;
189 : }
190 : }
191 4 : }
192 2 : return existingBidiStops;
193 : }
194 :
195 :
196 : std::shared_ptr<NBPTStop>
197 950 : NBPTStopCont::getReverseStop(std::shared_ptr<NBPTStop> pStop, const NBEdgeCont& ec) {
198 950 : std::string edgeId = pStop->getEdgeId();
199 950 : NBEdge* edge = ec.getByID(edgeId);
200 950 : NBEdge* reverse = NBPTStopCont::getReverseEdge(edge);
201 950 : if (reverse != nullptr) {
202 600 : const std::string reverseID = getReverseID(pStop->getID());
203 : if (myPTStops.count(reverseID) == 0) {
204 237 : return std::make_shared<NBPTStop>(reverseID, pStop->getPosition(), reverse->getID(), reverse->getID(),
205 711 : pStop->getLength(), pStop->getName(), pStop->getPermissions());
206 : } else {
207 63 : return myPTStops[reverseID];
208 : }
209 : }
210 : return nullptr;
211 : }
212 :
213 :
214 : std::shared_ptr<NBPTStop>
215 17 : NBPTStopCont::assignAndCreatNewPTStopAsNeeded(std::shared_ptr<NBPTStop> pStop, NBEdgeCont& cont) {
216 17 : std::string edgeId = pStop->getEdgeId();
217 17 : NBEdge* edge = cont.getByID(edgeId);
218 17 : if (edge == nullptr) {
219 : return nullptr;
220 : }
221 : bool rightOfEdge = false;
222 : bool leftOfEdge = false;
223 : const NBPTPlatform* left = nullptr;
224 33 : for (const NBPTPlatform& platform : pStop->getPlatformCands()) {
225 17 : double crossProd = computeCrossProductEdgePosition(edge, platform.getPos());
226 : //TODO consider driving on the left!!! [GL May '17]
227 17 : if (crossProd > 0) {
228 : leftOfEdge = true;
229 : left = &platform;
230 : } else {
231 : rightOfEdge = true;
232 16 : pStop->setPTStopLength(platform.getLength());
233 : }
234 : }
235 :
236 16 : if (leftOfEdge && rightOfEdge) {
237 2 : std::shared_ptr<NBPTStop> leftStop = getReverseStop(pStop, cont);
238 1 : if (leftStop) {
239 0 : leftStop->setPTStopLength(left->getLength());
240 : }
241 : return leftStop;
242 15 : } else if (leftOfEdge) {
243 0 : NBEdge* reverse = getReverseEdge(edge);
244 0 : if (reverse != nullptr) {
245 0 : pStop->setEdgeId(reverse->getID(), cont);
246 0 : pStop->setPTStopLength(left->getLength());
247 : }
248 : }
249 :
250 : return nullptr;
251 : }
252 :
253 :
254 : void
255 270 : NBPTStopCont::assignPTStopToEdgeOfClosestPlatform(std::shared_ptr<NBPTStop> pStop, NBEdgeCont& cont) {
256 270 : std::string edgeId = pStop->getEdgeId();
257 270 : NBEdge* edge = cont.getByID(edgeId);
258 270 : NBEdge* reverse = NBPTStopCont::getReverseEdge(edge);
259 540 : const NBPTPlatform* closestPlatform = getClosestPlatformToPTStopPosition(pStop);
260 270 : pStop->setPTStopLength(closestPlatform->getLength());
261 270 : if (reverse != nullptr) {
262 :
263 : //TODO make isLeft in PositionVector static [GL May '17]
264 : // if (PositionVector::isLeft(edge->getFromNode()->getPosition(),edge->getToNode()->getPosition(),closestPlatform)){
265 : //
266 : // }
267 99 : double crossProd = computeCrossProductEdgePosition(edge, closestPlatform->getPos());
268 :
269 : //TODO consider driving on the left!!! [GL May '17]
270 99 : if (crossProd > 0) { //pt stop is on the left of the orig edge
271 123 : pStop->setEdgeId(reverse->getID(), cont);
272 : }
273 : }
274 270 : }
275 :
276 :
277 : double
278 116 : NBPTStopCont::computeCrossProductEdgePosition(const NBEdge* edge, const Position& closestPlatform) const {
279 : PositionVector geom = edge->getGeometry();
280 116 : int idxTmp = geom.indexOfClosest(closestPlatform);
281 116 : double offset = geom.nearest_offset_to_point2D(closestPlatform, true);
282 116 : double offset2 = geom.offsetAtIndex2D(idxTmp);
283 : int idx1, idx2;
284 116 : if (offset2 < offset) {
285 : idx1 = idxTmp;
286 41 : idx2 = idx1 + 1;
287 : } else {
288 : idx2 = idxTmp;
289 75 : idx1 = idxTmp - 1;
290 : }
291 116 : if (idx1 < 0 || idx1 >= (int) geom.size() || idx2 < 0 || idx2 >= (int) geom.size()) {
292 63 : WRITE_WARNINGF(TL("Could not determine cross product for edge '%'."), edge->getID());
293 21 : return 0;
294 : }
295 95 : Position p1 = geom[idx1];
296 95 : Position p2 = geom[idx2];
297 :
298 : double x0 = p1.x();
299 : double y0 = p1.y();
300 : double x1 = p2.x();
301 : double y1 = p2.y();
302 : double x2 = closestPlatform.x();
303 : double y2 = closestPlatform.y();
304 95 : double crossProd = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
305 95 : return crossProd;
306 116 : }
307 :
308 :
309 : const NBPTPlatform*
310 270 : NBPTStopCont::getClosestPlatformToPTStopPosition(std::shared_ptr<NBPTStop> pStop) {
311 270 : Position stopPosition = pStop->getPosition();
312 : const NBPTPlatform* closest = nullptr;
313 : double minSqrDist = std::numeric_limits<double>::max();
314 999 : for (const NBPTPlatform& platform : pStop->getPlatformCands()) {
315 729 : double sqrDist = stopPosition.distanceSquaredTo2D(platform.getPos());
316 729 : if (sqrDist < minSqrDist) {
317 : minSqrDist = sqrDist;
318 : closest = &platform;
319 : }
320 : }
321 270 : return closest;
322 : }
323 :
324 : //static functions
325 :
326 : NBEdge*
327 1462 : NBPTStopCont::getReverseEdge(NBEdge* edge) {
328 1462 : if (edge != nullptr) {
329 1284 : for (auto it = edge->getToNode()->getOutgoingEdges().begin();
330 2278 : it != edge->getToNode()->getOutgoingEdges().end();
331 : it++) {
332 1572 : if ((*it)->getToNode() == edge->getFromNode()) {
333 : return (*it);
334 : }
335 : }
336 : }
337 : return nullptr;
338 : }
339 :
340 :
341 : int
342 1991 : NBPTStopCont::cleanupDeleted(NBEdgeCont& cont) {
343 : int numDeleted = 0;
344 5240 : for (auto i = myPTStops.begin(); i != myPTStops.end();) {
345 3249 : if (cont.getByID(i->second->getEdgeId()) == nullptr) {
346 501 : WRITE_WARNINGF(TL("Removing pt stop '%' on non existing edge '%'."), i->first, i->second->getEdgeId());
347 : i = myPTStops.erase(i);
348 167 : numDeleted++;
349 : } else {
350 : i++;
351 : }
352 : }
353 1991 : return numDeleted;
354 : }
355 :
356 :
357 : void
358 1726 : NBPTStopCont::addEdges2Keep(const OptionsCont& /* oc */, std::set<std::string>& into) {
359 3589 : for (auto stop : myPTStops) {
360 1863 : into.insert(stop.second->getEdgeId());
361 1863 : }
362 1726 : }
363 :
364 :
365 : void
366 5567 : NBPTStopCont::replaceEdge(const std::string& edgeID, const EdgeVector& replacement) {
367 5567 : if (myPTStops.size() > 0 && myPTStopLookup.size() == 0) {
368 : // init lookup once
369 1163 : for (auto& item : myPTStops) {
370 1108 : myPTStopLookup[item.second->getEdgeId()].push_back(item.second);
371 : }
372 : }
373 : // make a copy because the vector gets modified
374 5567 : const std::vector<std::shared_ptr<NBPTStop> > stops = myPTStopLookup[edgeID];
375 6033 : for (std::shared_ptr<NBPTStop> stop : stops) {
376 466 : if (!stop->replaceEdge(edgeID, replacement)) {
377 0 : WRITE_WARNINGF(TL("Could not re-assign pt stop '%' after replacing edge '%'."), stop->getID(), edgeID);
378 : } else {
379 466 : myPTStopLookup[stop->getEdgeId()].push_back(stop);
380 : }
381 : }
382 : myPTStopLookup.erase(edgeID);
383 5567 : }
384 :
385 :
386 : void
387 4 : NBPTStopCont::postprocess(std::set<std::string>& usedStops) {
388 192 : for (auto i = myPTStops.begin(); i != myPTStops.end();) {
389 376 : if (usedStops.find(i->second->getID()) == usedStops.end()) {
390 : myPTStops.erase(i++);
391 : } else {
392 : i++;
393 : }
394 : }
395 4 : }
396 :
397 : std::string
398 1436 : NBPTStopCont::getReverseID(const std::string& id) {
399 1436 : return id.size() > 0 && id[0] == '-' ? id.substr(1) : "-" + id;
400 : }
401 :
402 : void
403 156 : NBPTStopCont::alignIdSigns() {
404 : PTStopsCont stops = myPTStops;
405 1983 : for (auto& i : stops) {
406 : std::shared_ptr<NBPTStop> s = i.second;
407 1827 : const std::string& stopId = s->getID();
408 1827 : if (s->getEdgeId() == "" || s->wasLoaded()) {
409 175 : continue;
410 : }
411 3304 : const char edgeSign = s->getEdgeId().at(0);
412 1652 : const char stopSign = stopId.at(0);
413 1652 : if (edgeSign != stopSign && (edgeSign == '-' || stopSign == '-')) {
414 162 : const std::string reverseID = getReverseID(stopId);
415 324 : std::shared_ptr<NBPTStop> rs = get(reverseID);
416 162 : if (rs != nullptr && rs->wasLoaded()) {
417 : continue;
418 : }
419 160 : s->setPTStopId(reverseID);
420 : myPTStops.erase(stopId);
421 160 : myPTStops[reverseID] = s;
422 160 : if (rs != nullptr) {
423 0 : rs->setPTStopId(stopId);
424 55 : myPTStops[stopId] = rs;
425 : }
426 : }
427 : }
428 156 : }
429 :
430 : void
431 167 : NBPTStopCont::assignEdgeForFloatingStops(NBEdgeCont& cont, double maxRadius) {
432 : NamedRTree r;
433 : SVCPermissions publicPermissions = SVC_BUS | SVC_TRAM | SVC_RAIL | SVC_RAIL_URBAN | SVC_TAXI;
434 40291 : for (const auto& item : cont) {
435 40124 : NBEdge* edge = item.second;
436 40124 : if ((edge->getPermissions() & publicPermissions) == 0) {
437 18806 : continue;
438 : }
439 21318 : const Boundary& bound = edge->getGeometry().getBoxBoundary();
440 21318 : float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
441 21318 : float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
442 42636 : r.Insert(min, max, edge);
443 : }
444 291 : for (std::shared_ptr<NBPTStop> ptStop : myFloatingStops) {
445 : std::set<const Named*> edges;
446 : Named::StoringVisitor visitor(edges);
447 124 : const Position& pos = ptStop->getPosition();
448 124 : float min[2] = {static_cast<float>(pos.x() - maxRadius), static_cast<float>(pos.y() - maxRadius)};
449 124 : float max[2] = {static_cast<float>(pos.x() + maxRadius), static_cast<float>(pos.y() + maxRadius)};
450 : r.Search(min, max, visitor);
451 : std::vector<NBEdge*> nearby;
452 289 : for (const Named* namedEdge : edges) {
453 165 : NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
454 165 : if ((e->getPermissions() & ptStop->getPermissions()) != 0) {
455 81 : nearby.push_back(e);
456 : }
457 : }
458 248 : std::sort(nearby.begin(), nearby.end(), [pos](NBEdge * a, NBEdge * b) {
459 148 : return a->getLaneShape(0).distance2D(pos, false) < b->getLaneShape(0).distance2D(pos, false);
460 : });
461 :
462 124 : for (NBEdge* e : nearby) {
463 60 : ptStop->setEdgeId(e->getID(), cont);
464 20 : if (ptStop->getLaneId() != "") {
465 : break;
466 : }
467 : }
468 124 : if (ptStop->getLaneId() == "") {
469 208 : WRITE_WARNINGF(TL("Could not find corresponding edge or compatible lane for free-floating pt stop '%' (%). Thus, it will be removed!"),
470 : ptStop->getID(), ptStop->getName());
471 208 : myPTStops.erase(ptStop->getID());
472 : }
473 124 : }
474 167 : }
475 :
476 : void
477 158 : NBPTStopCont::findAccessEdgesForRailStops(NBEdgeCont& cont, double maxRadius, int maxCount, double accessFactor) {
478 : NamedRTree r;
479 33225 : for (auto edge : cont) {
480 33067 : const Boundary& bound = edge.second->getGeometry().getBoxBoundary();
481 33067 : float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
482 33067 : float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
483 66134 : r.Insert(min, max, edge.second);
484 : }
485 2142 : for (auto& ptStop : myPTStops) {
486 1984 : const std::string& stopEdgeID = ptStop.second->getEdgeId();
487 1984 : NBEdge* stopEdge = cont.getByID(stopEdgeID);
488 : //std::cout << "findAccessEdgesForRailStops edge=" << stopEdgeID << " exists=" << (stopEdge != 0) << "\n";
489 1984 : if (stopEdge != nullptr && (stopEdge->getPermissions() & SVC_PEDESTRIAN) == 0) {
490 : //if (stopEdge != 0 && isRailway(stopEdge->getPermissions())) {
491 : std::set<const Named*> edges;
492 : Named::StoringVisitor visitor(edges);
493 1080 : const Position& pos = ptStop.second->getPosition();
494 1080 : float min[2] = {static_cast<float>(pos.x() - maxRadius), static_cast<float>(pos.y() - maxRadius)};
495 1080 : float max[2] = {static_cast<float>(pos.x() + maxRadius), static_cast<float>(pos.y() + maxRadius)};
496 : r.Search(min, max, visitor);
497 : std::vector<NBEdge*> edgCants;
498 107048 : for (const Named* namedEdge : edges) {
499 105968 : NBEdge* e = const_cast<NBEdge*>(dynamic_cast<const NBEdge*>(namedEdge));
500 105968 : edgCants.push_back(e);
501 : }
502 2160 : std::sort(edgCants.begin(), edgCants.end(), [pos](NBEdge * a, NBEdge * b) {
503 971361 : return a->getLaneShape(0).distance2D(pos, false) < b->getLaneShape(0).distance2D(pos, false);
504 : });
505 : int cnt = 0;
506 12228 : for (auto edge : edgCants) {
507 : int laneIdx = 0;
508 22227 : for (auto lane : edge->getLanes()) {
509 13331 : if ((lane.permissions & SVC_PEDESTRIAN) != 0) {
510 2798 : double offset = lane.shape.nearest_offset_to_point2D(pos, false);
511 2798 : double finalLength = edge->getFinalLength();
512 2798 : double laneLength = lane.shape.length();
513 2798 : double accessLength = pos.distanceTo2D(lane.shape.positionAtOffset2D(offset)) * accessFactor;
514 2798 : ptStop.second->addAccess(edge->getLaneID(laneIdx), offset * finalLength / laneLength, accessLength);
515 2798 : cnt++;
516 : break;
517 : }
518 10533 : laneIdx++;
519 13331 : }
520 11694 : if (cnt == maxCount) {
521 : break;
522 : }
523 : }
524 1080 : }
525 : }
526 158 : }
527 :
528 :
529 : std::shared_ptr<NBPTStop>
530 5 : NBPTStopCont::findStop(const std::string& origEdgeID, Position pos, double threshold) const {
531 123 : for (auto& item : myPTStops) {
532 245 : if (item.second->getOrigEdgeId() == origEdgeID &&
533 5 : item.second->getPosition().distanceTo2D(pos) < threshold) {
534 : return item.second;
535 : }
536 : }
537 : return nullptr;
538 : }
539 :
540 :
541 : /****************************************************************************/
|