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