1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    NBPTStopCont.cpp
11 /// @author  Gregor Laemmel
12 /// @date    Tue, 20 Mar 2017
13 /// @version $Id$
14 ///
15 // Container for pt stops during the netbuilding process
16 /****************************************************************************/
17 
18 
19 #include <utils/common/MsgHandler.h>
20 #include <utils/geom/Boundary.h>
21 #include <utils/options/OptionsCont.h>
22 #include <microsim/MSLane.h>
23 #include "NBPTStopCont.h"
24 #include "NBEdgeCont.h"
25 #include "NBEdge.h"
26 #include "NBNode.h"
27 #include <utils/geom/Position.h>
28 
29 
~NBPTStopCont()30 NBPTStopCont::~NBPTStopCont() {
31     for (auto& myPTStop : myPTStops) {
32         delete myPTStop.second;
33     }
34     myPTStops.clear();
35 }
36 
37 
38 bool
insert(NBPTStop * ptStop)39 NBPTStopCont::insert(NBPTStop* ptStop) {
40     std::string id = ptStop->getID();
41     auto i = myPTStops.find(id);
42     if (i != myPTStops.end()) {
43         return false;
44     }
45     myPTStops[id] = ptStop;
46     return true;
47 }
48 
49 
50 NBPTStop*
get(std::string id)51 NBPTStopCont::get(std::string id) {
52     if (myPTStops.find(id) != myPTStops.end()) {
53         return myPTStops.find(id)->second;
54     }
55     return nullptr;
56 }
57 
58 
59 void
localizePTStops(NBEdgeCont & cont)60 NBPTStopCont::localizePTStops(NBEdgeCont& cont) {
61     std::vector<NBPTStop*> reverseStops;
62     //first pass localize pt stop at correct side of the street; create stop for opposite side if needed
63     for (auto& myPTStop : myPTStops) {
64 
65         NBPTStop* stop = myPTStop.second;
66 
67         bool multipleStopPositions = stop->getIsMultipleStopPositions();
68         bool platformsDefined = !stop->getPlatformCands().empty();
69         if (!platformsDefined) {
70             //create pt stop for reverse edge if edge exists
71             NBPTStop* reverseStop = getReverseStop(stop, cont);
72             if (reverseStop != nullptr) {
73                 reverseStops.push_back(reverseStop);
74             }
75         } else if (multipleStopPositions) {
76             //create pt stop for closest platform at corresponding edge
77             assignPTStopToEdgeOfClosestPlatform(stop, cont);
78 
79         } else {
80             //create pt stop for each side of the street where a platform is defined (create additional pt stop as needed)
81             NBPTStop* additionalStop = assignAndCreatNewPTStopAsNeeded(stop, cont);
82             if (additionalStop != nullptr) {
83                 reverseStops.push_back(additionalStop);
84             }
85         }
86     }
87 
88     //insrt new stops if any
89     for (auto& reverseStop : reverseStops) {
90         insert(reverseStop);
91     }
92 }
93 
94 
assignLanes(NBEdgeCont & cont)95 void NBPTStopCont::assignLanes(NBEdgeCont& cont) {
96     //scnd pass set correct lane
97     for (auto i = myPTStops.begin(); i != myPTStops.end();) {
98         NBPTStop* stop = i->second;
99 
100         if (!stop->findLaneAndComputeBusStopExtent(cont)) {
101             WRITE_WARNING("Could not find corresponding edge or compatible lane for pt stop: " + i->second->getName()
102                           + ". Thus, it will be removed!");
103             EdgeVector edgeVector = cont.getGeneratedFrom((*i).second->getOrigEdgeId());
104             //std::cout << edgeVector.size() << std::endl;
105             myPTStops.erase(i++);
106         } else {
107             i++;
108         }
109     }
110 }
111 
112 
113 int
generateBidiStops(NBEdgeCont & ec)114 NBPTStopCont::generateBidiStops(NBEdgeCont& ec) {
115     //scnd pass set correct lane
116     std::vector<NBPTStop*> toAdd;
117     for (auto i = myPTStops.begin(); i != myPTStops.end(); i++) {
118         NBPTStop* stop = i->second;
119         NBEdge* edge = ec.getByID(stop->getEdgeId());
120         if (edge != nullptr && edge->isBidiRail()) {
121             NBEdge* bidiEdge = edge->getTurnDestination(true);
122             assert(bidiEdge != 0);
123             const std::string id = getReverseID(stop->getID());
124             if (myPTStops.count(id) > 0) {
125                 if (myPTStops[id]->getEdgeId() != bidiEdge->getID()) {
126                     WRITE_WARNING("Could not create reverse-direction stop for superposed edge '" + bidiEdge->getID()
127                                   + "' (origStop '" + i->first + "'). Stop id '" + id
128                                   + "' already in use by stop on edge '" + myPTStops[id]->getEdgeId() + "'.");
129                 }
130                 continue;
131             }
132             NBPTStop* bidiStop = new NBPTStop(id,
133                                               stop->getPosition(),
134                                               bidiEdge->getID(),
135                                               stop->getOrigEdgeId(),
136                                               stop->getLength(),
137                                               stop->getName(),
138                                               stop->getPermissions());
139             if (bidiStop->findLaneAndComputeBusStopExtent(ec)) {
140                 toAdd.push_back(bidiStop);
141                 stop->setBidiStop(bidiStop);
142                 bidiStop->setBidiStop(stop);
143             } else {
144                 // should not happen
145                 assert(false);
146             }
147         }
148     }
149     for (NBPTStop* newStop : toAdd) {
150         myPTStops[newStop->getID()] = newStop;
151     }
152     if (toAdd.size() > 0) {
153         WRITE_MESSAGE("Added " + toString(toAdd.size()) + " stops for superposed rail edges.");
154     }
155     return (int)toAdd.size();
156 }
157 
158 
159 NBPTStop*
getReverseStop(NBPTStop * pStop,NBEdgeCont & cont)160 NBPTStopCont::getReverseStop(NBPTStop* pStop, NBEdgeCont& cont) {
161     std::string edgeId = pStop->getEdgeId();
162     NBEdge* edge = cont.getByID(edgeId);
163     NBEdge* reverse = NBPTStopCont::getReverseEdge(edge);
164     if (reverse != nullptr) {
165         const std::string reverseID = getReverseID(pStop->getID());
166         if (myPTStops.count(reverseID) == 0) {
167             return new NBPTStop(reverseID, pStop->getPosition(), reverse->getID(), reverse->getID(),
168                                 pStop->getLength(), pStop->getName(), pStop->getPermissions());
169         } else {
170             return myPTStops[reverseID];
171         }
172     }
173     return nullptr;
174 }
175 
176 
177 NBPTStop*
assignAndCreatNewPTStopAsNeeded(NBPTStop * pStop,NBEdgeCont & cont)178 NBPTStopCont::assignAndCreatNewPTStopAsNeeded(NBPTStop* pStop, NBEdgeCont& cont) {
179     std::string edgeId = pStop->getEdgeId();
180     NBEdge* edge = cont.getByID(edgeId);
181     bool rightOfEdge = false;
182     bool leftOfEdge = false;
183     const NBPTPlatform* left = nullptr;
184     for (const NBPTPlatform& platform : pStop->getPlatformCands()) {
185         double crossProd = computeCrossProductEdgePosition(edge, platform.getPos());
186         //TODO consider driving on the left!!! [GL May '17]
187         if (crossProd > 0) {
188             leftOfEdge = true;
189             left = &platform;
190         } else {
191             rightOfEdge = true;
192             pStop->setMyPTStopLength(platform.getLength());
193         }
194     }
195 
196     if (leftOfEdge && rightOfEdge) {
197         NBPTStop* leftStop = getReverseStop(pStop, cont);
198         leftStop->setMyPTStopLength(left->getLength());
199         return leftStop;
200     } else if (leftOfEdge) {
201         NBEdge* reverse = getReverseEdge(edge);
202         if (reverse != nullptr) {
203             pStop->setEdgeId(reverse->getID(), cont);
204             pStop->setMyPTStopLength(left->getLength());
205         }
206     }
207 
208     return nullptr;
209 }
210 
211 
212 void
assignPTStopToEdgeOfClosestPlatform(NBPTStop * pStop,NBEdgeCont & cont)213 NBPTStopCont::assignPTStopToEdgeOfClosestPlatform(NBPTStop* pStop, NBEdgeCont& cont) {
214     std::string edgeId = pStop->getEdgeId();
215     NBEdge* edge = cont.getByID(edgeId);
216     NBEdge* reverse = NBPTStopCont::getReverseEdge(edge);
217     const NBPTPlatform* closestPlatform = getClosestPlatformToPTStopPosition(pStop);
218     pStop->setMyPTStopLength(closestPlatform->getLength());
219     if (reverse != nullptr) {
220 
221         //TODO make isLeft in PositionVector static [GL May '17]
222 //        if (PositionVector::isLeft(edge->getFromNode()->getPosition(),edge->getToNode()->getPosition(),closestPlatform)){
223 //
224 //        }
225         double crossProd = computeCrossProductEdgePosition(edge, closestPlatform->getPos());
226 
227         //TODO consider driving on the left!!! [GL May '17]
228         if (crossProd > 0) { //pt stop is on the left of the orig edge
229             pStop->setEdgeId(reverse->getID(), cont);
230         }
231     }
232 }
233 
234 
235 double
computeCrossProductEdgePosition(const NBEdge * edge,const Position & closestPlatform) const236 NBPTStopCont::computeCrossProductEdgePosition(const NBEdge* edge, const Position& closestPlatform) const {
237     PositionVector geom = edge->getGeometry();
238     int idxTmp = geom.indexOfClosest(closestPlatform);
239     double offset = geom.nearest_offset_to_point2D(closestPlatform, true);
240     double offset2 = geom.offsetAtIndex2D(idxTmp);
241     int idx1, idx2;
242     if (offset2 < offset) {
243         idx1 = idxTmp;
244         idx2 = idx1 + 1;
245     } else {
246         idx2 = idxTmp;
247         idx1 = idxTmp - 1;
248     }
249     if (idx1 < 0 || idx1 >= (int) geom.size() || idx2 < 0 || idx2 >= (int) geom.size()) {
250         WRITE_WARNING("Could not determine cross product");
251         return 0;
252     }
253     Position p1 = geom[idx1];
254     Position p2 = geom[idx2];
255 
256     double x0 = p1.x();
257     double y0 = p1.y();
258     double x1 = p2.x();
259     double y1 = p2.y();
260     double x2 = closestPlatform.x();
261     double y2 = closestPlatform.y();
262     double crossProd = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
263     return crossProd;
264 }
265 
266 
267 const NBPTPlatform*
getClosestPlatformToPTStopPosition(NBPTStop * pStop)268 NBPTStopCont::getClosestPlatformToPTStopPosition(NBPTStop* pStop) {
269     Position stopPosition = pStop->getPosition();
270     const NBPTPlatform* closest = nullptr;
271     double minSqrDist = std::numeric_limits<double>::max();
272     for (const NBPTPlatform& platform : pStop->getPlatformCands()) {
273         double sqrDist = stopPosition.distanceSquaredTo2D(platform.getPos());
274         if (sqrDist < minSqrDist) {
275             minSqrDist = sqrDist;
276             closest = &platform;
277         }
278     }
279     return closest;
280 }
281 
282 //static functions
283 
284 NBEdge*
getReverseEdge(NBEdge * edge)285 NBPTStopCont::getReverseEdge(NBEdge* edge) {
286     if (edge != nullptr) {
287         for (auto it = edge->getToNode()->getOutgoingEdges().begin();
288                 it != edge->getToNode()->getOutgoingEdges().end();
289                 it++) {
290             if ((*it)->getToNode() == edge->getFromNode()) {
291                 return (*it);
292             }
293         }
294     }
295     return nullptr;
296 }
297 
298 
299 void
cleanupDeleted(NBEdgeCont & cont)300 NBPTStopCont::cleanupDeleted(NBEdgeCont& cont) {
301     for (auto i = myPTStops.begin(); i != myPTStops.end();) {
302         if (cont.getByID((*i).second->getEdgeId()) == nullptr) {
303             WRITE_WARNING("Removing pt stop:" + (*i).first + " on non existing edge: " + (*i).second->getEdgeId());
304             myPTStops.erase(i++);
305         } else {
306             i++;
307         }
308     }
309 
310 }
311 
312 
313 void
addEdges2Keep(const OptionsCont & oc,std::set<std::string> & into)314 NBPTStopCont::addEdges2Keep(const OptionsCont& oc, std::set<std::string>& into) {
315     if (oc.isSet("ptstop-output")) {
316         for (auto stop : myPTStops) {
317             into.insert(stop.second->getEdgeId());
318         }
319     }
320 }
321 
322 
323 void
postprocess(std::set<std::string> & usedStops)324 NBPTStopCont::postprocess(std::set<std::string>& usedStops) {
325     for (auto i = myPTStops.begin(); i != myPTStops.end();) {
326         if (usedStops.find(i->second->getID()) == usedStops.end()) {
327             myPTStops.erase(i++);
328         } else {
329             i++;
330         }
331     }
332 }
333 
334 std::string
getReverseID(const std::string & id)335 NBPTStopCont::getReverseID(const std::string& id) {
336     return id.size() > 0 && id[0] == '-' ? id.substr(1) : "-" + id;
337 }
338 
339 void
alignIdSigns()340 NBPTStopCont::alignIdSigns() {
341     PTStopsCont stops = myPTStops;
342     for (auto& i : stops) {
343         const std::string& stopId = i.second->getID();
344         const char edgeSign = i.second->getEdgeId().at(0);
345         const char stopSign = stopId.at(0);
346         if (edgeSign != stopSign && (edgeSign == '-' || stopSign == '-')) {
347             i.second->setMyPTStopId(getReverseID(stopId));
348             myPTStops.erase(stopId);
349             myPTStops[i.second->getID()] = i.second;
350         }
351     }
352 }
353 
354 
355 void
findAccessEdgesForRailStops(NBEdgeCont & cont,double maxRadius,int maxCount,double accessFactor)356 NBPTStopCont::findAccessEdgesForRailStops(NBEdgeCont& cont, double maxRadius, int maxCount, double accessFactor) {
357     NamedRTree r;
358     for (auto edge : cont) {
359         const Boundary& bound = edge.second->getGeometry().getBoxBoundary();
360         float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
361         float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
362         r.Insert(min, max, edge.second);
363     }
364     for (auto& ptStop : myPTStops) {
365         const std::string& stopEdgeID = ptStop.second->getEdgeId();
366         NBEdge* stopEdge = cont.getByID(stopEdgeID);
367         //std::cout << "findAccessEdgesForRailStops edge=" << stopEdgeID << " exists=" << (stopEdge != 0) << "\n";
368         if (stopEdge != nullptr && (stopEdge->getPermissions() & SVC_PEDESTRIAN) == 0) {
369             //if (stopEdge != 0 && isRailway(stopEdge->getPermissions())) {
370             std::set<std::string> ids;
371             Named::StoringVisitor visitor(ids);
372             const Position& pos = ptStop.second->getPosition();
373             float min[2] = {static_cast<float>(pos.x() - maxRadius), static_cast<float>(pos.y() - maxRadius)};
374             float max[2] = {static_cast<float>(pos.x() + maxRadius), static_cast<float>(pos.y() + maxRadius)};
375             r.Search(min, max, visitor);
376             std::vector<NBEdge*> edgCants;
377             for (const auto& id : ids) {
378                 NBEdge* e = cont.getByID(id);
379                 edgCants.push_back(e);
380             }
381             std::sort(edgCants.begin(), edgCants.end(), [pos](NBEdge * a, NBEdge * b) {
382                 return a->getLaneShape(0).distance2D(pos, false) < b->getLaneShape(0).distance2D(pos, false);
383             });
384             int cnt = 0;
385             for (auto edge : edgCants) {
386                 int laneIdx = 0;
387                 for (auto lane : edge->getLanes()) {
388                     if ((lane.permissions & SVC_PEDESTRIAN) != 0) {
389                         double offset = lane.shape.nearest_offset_to_point2D(pos, false);
390                         double finalLength = edge->getFinalLength();
391                         double laneLength = lane.shape.length();
392                         double accessLength = pos.distanceTo2D(lane.shape.positionAtOffset2D(offset)) * accessFactor;
393                         ptStop.second->addAccess(edge->getLaneID(laneIdx), offset * finalLength / laneLength, accessLength);
394                         cnt++;
395                         break;
396                     }
397                     laneIdx++;
398                 }
399                 if (cnt == maxCount) {
400                     break;
401                 }
402             }
403         }
404     }
405 }
406 
407 
408 /****************************************************************************/
409