Eclipse SUMO - Simulation of Urban MObility
MSPModel_JuPedSim.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2014-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 /****************************************************************************/
21 // The pedestrian following model that can instantiate different pedestrian models
22 // that come with the JuPedSim third-party simulation framework.
23 /****************************************************************************/
24 
25 #include <config.h>
26 
27 #include <algorithm>
28 #include <fstream>
29 #include <geos_c.h>
30 #include <jupedsim/jupedsim.h>
31 #include <microsim/MSEdge.h>
32 #include <microsim/MSLane.h>
33 #include <microsim/MSLink.h>
34 #include <microsim/MSEdgeControl.h>
39 #include <microsim/MSTrainHelper.h>
40 #include <libsumo/Helper.h>
41 #include <utils/geom/Position.h>
45 #include "MSPerson.h"
46 #include "MSStageWalking.h"
47 #include "MSPModel_JuPedSim.h"
48 
49 
50 // #define DEBUG_GEOMETRY_GENERATION
51 
52 
54 const double MSPModel_JuPedSim::GEOS_MITRE_LIMIT = 5.0;
55 const double MSPModel_JuPedSim::GEOS_MIN_AREA = 1;
60 const std::string MSPModel_JuPedSim::PEDESTRIAN_NETWORK_ID = "jupedsim.pedestrian_network";
61 const std::string MSPModel_JuPedSim::PEDESTRIAN_NETWORK_CARRIAGES_AND_RAMPS_ID = "jupedsim.pedestrian_network.carriages_and_ramps";
62 
63 // ===========================================================================
64 // method definitions
65 // ===========================================================================
67  myNetwork(net), myShapeContainer(net->getShapeContainer()), myJPSDeltaT(string2time(oc.getString("pedestrian.jupedsim.step-length"))),
68  myExitTolerance(oc.getFloat("pedestrian.jupedsim.exit-tolerance")), myGEOSPedestrianNetworkLargestComponent(nullptr),
69  myHaveAdditionalWalkableAreas(false) {
70  initialize(oc);
72 }
73 
74 
76  clearState();
77  if (myPythonScript != nullptr) {
78  (*myPythonScript) << "while simulation.agent_count() > 0 and simulation.iteration_count() < 160 * 100: simulation.iterate()\n";
79  }
80 
81  JPS_Simulation_Free(myJPSSimulation);
82  JPS_OperationalModel_Free(myJPSModel);
83  JPS_Geometry_Free(myJPSGeometry);
84  if (myJPSGeometryWithTrainsAndRamps != nullptr) {
85  JPS_Geometry_Free(myJPSGeometryWithTrainsAndRamps);
86  }
87 
88  GEOSGeom_destroy(myGEOSPedestrianNetwork);
89  finishGEOS();
90 }
91 
92 
93 void
95  JPS_CollisionFreeSpeedModelAgentParameters agent_parameters{};
96  agent_parameters.journeyId = state->getJourneyId();
97  agent_parameters.stageId = state->getStageId();
98  agent_parameters.position = {p.x(), p.y()};
99  /*
100  const double angle = state->getAngle(*state->getStage(), 0);
101  JPS_Point orientation;
102  if (fabs(angle - M_PI / 2) < NUMERICAL_EPS) {
103  orientation = JPS_Point{0., 1.};
104  }
105  else if (fabs(angle + M_PI / 2) < NUMERICAL_EPS) {
106  orientation = JPS_Point{0., -1.};
107  }
108  else {
109  orientation = JPS_Point{1., tan(angle)};
110  }
111  agent_parameters.orientation = orientation;
112  */
113  const MSVehicleType& type = state->getPerson()->getVehicleType();
114  agent_parameters.radius = getRadius(type);
115  agent_parameters.v0 = state->getPerson()->getMaxSpeed();
116  JPS_ErrorMessage message = nullptr;
117  JPS_AgentId agentId = JPS_Simulation_AddCollisionFreeSpeedModelAgent(myJPSSimulation, agent_parameters, &message);
118  if (message != nullptr) {
119  WRITE_WARNINGF(TL("Error while adding person '%' as JuPedSim agent: %"), state->getPerson()->getID(), JPS_ErrorMessage_GetMessage(message));
120  JPS_ErrorMessage_Free(message);
121  } else {
122  state->setAgentId(agentId);
123  }
124 }
125 
126 
127 bool
128 MSPModel_JuPedSim::addStage(JPS_JourneyDescription journey, JPS_StageId& predecessor, const std::string& agentID, const JPS_StageId stage) {
129  JPS_ErrorMessage message = nullptr;
130  if (predecessor != 0) {
131  const JPS_Transition transition = JPS_Transition_CreateFixedTransition(stage, &message);
132  if (message != nullptr) {
133  WRITE_WARNINGF(TL("Error while creating fixed transition for person '%': %"), agentID, JPS_ErrorMessage_GetMessage(message));
134  JPS_ErrorMessage_Free(message);
135  return false;
136  }
137  JPS_JourneyDescription_SetTransitionForStage(journey, predecessor, transition, &message);
138  if (message != nullptr) {
139  WRITE_WARNINGF(TL("Error while setting transition for person '%': %"), agentID, JPS_ErrorMessage_GetMessage(message));
140  JPS_ErrorMessage_Free(message);
141  return false;
142  }
143  JPS_Transition_Free(transition);
144  }
145  JPS_JourneyDescription_AddStage(journey, stage);
146  predecessor = stage;
147  return true;
148 }
149 
150 
151 bool
152 MSPModel_JuPedSim::addWaypoint(JPS_JourneyDescription journey, JPS_StageId& predecessor, const std::string& agentID, const WaypointDesc& waypoint) {
153  JPS_ErrorMessage message = nullptr;
154  const Position& coords = std::get<1>(waypoint);
155  const JPS_StageId waypointId = JPS_Simulation_AddStageWaypoint(myJPSSimulation, {coords.x(), coords.y()},
156  std::get<2>(waypoint), &message);
157  if (message != nullptr) {
158  WRITE_WARNINGF(TL("Error while adding waypoint for person '%': %"), agentID, JPS_ErrorMessage_GetMessage(message));
159  JPS_ErrorMessage_Free(message);
160  return false;
161  }
162  const JPS_StageId waiting = std::get<0>(waypoint);
163  if (waiting != 0) {
164  if (myPythonScript != nullptr) {
165  (*myPythonScript) << "journey.add(" << waiting << ")\n";
166  if (predecessor != 0) {
167  (*myPythonScript) << "journey.set_transition_for_stage(" << predecessor << ", jps.Transition.create_fixed_transition(" << waiting << "))\n";
168  }
169  }
170  if (!addStage(journey, predecessor, agentID, waiting)) {
171  return false;
172  }
173  }
174  if (myPythonScript != nullptr) {
175  (*myPythonScript) << "ws = simulation.add_waypoint_stage((" << coords.x() << "," << coords.y() << "), " << std::get<2>(waypoint) << ")\n"
176  "journey.add(ws)\n";
177  if (predecessor != 0) {
178  (*myPythonScript) << "journey.set_transition_for_stage(" << predecessor << ", jps.Transition.create_fixed_transition(ws))\n";
179  }
180  }
181  if (!addStage(journey, predecessor, agentID, waypointId)) {
182  return false;
183  }
184  return true;
185 }
186 
187 
190  assert(person->getCurrentStageType() == MSStageType::WALKING);
191  const double radius = getRadius(person->getVehicleType());
192  Position departurePosition = Position::INVALID;
193  const MSLane* const departureLane = getSidewalk<MSEdge, MSLane>(stage->getRoute().front());
194  if (departureLane == nullptr) {
195  const char* error = TL("Person '%' could not find sidewalk on edge '%', time=%.");
196  if (OptionsCont::getOptions().getBool("ignore-route-errors")) {
197  WRITE_WARNINGF(error, person->getID(), person->getEdge()->getID(), time2string(SIMSTEP));
198  return nullptr;
199  } else {
200  throw ProcessError(TLF(error, person->getID(), person->getEdge()->getID(), time2string(SIMSTEP)));
201  }
202  }
203  // First real stage, stage 0 is waiting.
205  const MSEdge* const tripOrigin = person->getNextStage(-1)->getEdge();
206  if (tripOrigin->isTazConnector()) {
207  const SUMOPolygon* tazShape = myNetwork->getShapeContainer().getPolygons().get(tripOrigin->getParameter("taz"));
208  if (tazShape == nullptr) {
209  WRITE_WARNINGF(TL("FromTaz '%' for person '%' has no shape information."), tripOrigin->getParameter("taz"), person->getID());
210  } else {
211  const Boundary& bbox = tazShape->getShape().getBoxBoundary();
212  while (!tazShape->getShape().around(departurePosition)) {
213  // TODO: Optimize for speed if necessary or at least abort trying to find a point.
214  departurePosition.setx(RandHelper::rand(bbox.xmin(), bbox.xmax()));
215  departurePosition.sety(RandHelper::rand(bbox.ymin(), bbox.ymax()));
216  }
217  }
218  }
219  }
220  if (departurePosition == Position::INVALID) {
221  const double halfDepartureLaneWidth = departureLane->getWidth() / 2.0;
222  double departureRelativePositionY = stage->getDepartPosLat();
223  if (departureRelativePositionY == UNSPECIFIED_POS_LAT) {
224  departureRelativePositionY = 0.0;
225  }
226  if (departureRelativePositionY == MSPModel::RANDOM_POS_LAT) {
227  departureRelativePositionY = RandHelper::rand(-halfDepartureLaneWidth, halfDepartureLaneWidth);
228  }
229  departurePosition = departureLane->getShape().positionAtOffset(stage->getDepartPos() + radius + POSITION_EPS, -departureRelativePositionY); // Minus sign is here for legacy reasons.
230  }
231 
232  std::vector<WaypointDesc> waypoints;
233  const MSEdge* prev = nullptr;
234  for (const MSEdge* const e : stage->getEdges()) {
235  const MSLane* const lane = getSidewalk<MSEdge, MSLane>(e);
236  JPS_StageId waitingStage = 0;
237  if (prev != nullptr) {
238  int dir = UNDEFINED_DIRECTION;
239  if (prev->getToJunction() == e->getFromJunction() || prev->getToJunction() == e->getToJunction()) {
240  dir = FORWARD;
241  } else if (prev->getFromJunction() == e->getFromJunction() || prev->getFromJunction() == e->getToJunction()) {
242  dir = BACKWARD;
243  }
244  if (dir != UNDEFINED_DIRECTION) {
245  ConstMSEdgeVector crossingRoute;
246  MSNet::getInstance()->getPedestrianRouter(0).compute(prev, e, 0, 0, stage->getMaxSpeed(person), 0, dir == FORWARD ? prev->getToJunction() : prev->getFromJunction(), crossingRoute, true);
247  for (const MSEdge* const ce : crossingRoute) {
248  if (ce->isCrossing()) {
249  const MSLane* const crossing = getSidewalk<MSEdge, MSLane>(ce);
250  if (myCrossingWaits.count(crossing) > 0) {
251  const MSLane* const prevLane = getSidewalk<MSEdge, MSLane>(prev);
252  const Position& startPos = dir == FORWARD ? prevLane->getShape().back() : prevLane->getShape().front();
253  // choose the waiting set closer to the lane "end"
254  if (crossing->getShape().front().distanceSquaredTo(startPos) < crossing->getShape().back().distanceSquaredTo(startPos)) {
255  waitingStage = myCrossingWaits[crossing].first;
256  } else {
257  waitingStage = myCrossingWaits[crossing].second;
258  }
259  } else {
260  throw ProcessError(TLF("No waiting set for crossing at %.", person->getID(), person->getEdge()->getID(), time2string(SIMSTEP)));
261  }
262  }
263  }
264  }
265  }
266  waypoints.push_back({waitingStage, lane->getShape().positionAtOffset(e->getLength() / 2.), lane->getWidth() / 2.});
267  prev = e;
268  }
269  const JPS_StageId finalWait = std::get<0>(waypoints.back());
270  waypoints.pop_back(); // arrival waypoint comes later
271  if (!waypoints.empty()) {
272  waypoints.erase(waypoints.begin()); // departure edge also does not need a waypoint
273  }
274  const MSLane* const arrivalLane = getSidewalk<MSEdge, MSLane>(stage->getDestination());
275  const Position arrivalPosition = arrivalLane->getShape().positionAtOffset(stage->getArrivalPos());
276  waypoints.push_back({finalWait, arrivalPosition, stage->getDouble("jupedsim.waypoint.radius", myExitTolerance)});
277 
278  JPS_JourneyDescription journeyDesc = JPS_JourneyDescription_Create();
279  if (myPythonScript != nullptr) {
280  (*myPythonScript) << "\njourney = jps.JourneyDescription()\n";
281  }
282  JPS_StageId startingStage = 0;
283  JPS_StageId predecessor = 0;
284  for (const auto& p : waypoints) {
285  if (!addWaypoint(journeyDesc, predecessor, person->getID(), p)) {
286  JPS_JourneyDescription_Free(journeyDesc);
287  return nullptr;
288  }
289  if (startingStage == 0) {
290  startingStage = std::get<0>(waypoints.front()) == 0 ? predecessor : std::get<0>(waypoints.front());
291  }
292  }
293  JPS_ErrorMessage message = nullptr;
294  JPS_JourneyId journeyId = JPS_Simulation_AddJourney(myJPSSimulation, journeyDesc, &message);
295  JPS_JourneyDescription_Free(journeyDesc);
296  if (message != nullptr) {
297  WRITE_WARNINGF(TL("Error while adding a journey for person '%': %"), person->getID(), JPS_ErrorMessage_GetMessage(message));
298  JPS_ErrorMessage_Free(message);
299  return nullptr;
300  }
301 
302  PState* state = nullptr;
303  for (PState* const pstate : myPedestrianStates) { // TODO transform myPedestrianStates into a map for faster lookup
304  if (pstate->getPerson() == person) {
305  state = pstate;
306  break;
307  }
308  }
309  if (state == nullptr) {
310  state = new PState(static_cast<MSPerson*>(person), stage, journeyId, startingStage, waypoints);
311  state->setLanePosition(stage->getDepartPos() + radius + POSITION_EPS);
312  state->setPreviousPosition(departurePosition);
313  state->setPosition(departurePosition.x(), departurePosition.y());
314  state->setAngle(departureLane->getShape().rotationAtOffset(stage->getDepartPos()));
315  myPedestrianStates.push_back(state);
317  } else {
318  state->reinit(stage, journeyId, startingStage, waypoints);
319  }
320  if (state->isWaitingToEnter()) {
321  tryPedestrianInsertion(state, state->getPosition(*state->getStage(), now));
322  if (myPythonScript != nullptr) {
323  (*myPythonScript) << "journey_id = simulation.add_journey(journey)\n"
324  "simulation.add_agent(jps.CollisionFreeSpeedModelAgentParameters(journey_id=journey_id,stage_id="
325  << startingStage << ",position=(" << departurePosition.x() << "," << departurePosition.y()
326  << "),radius=" << getRadius(person->getVehicleType()) << ",v0=" << person->getMaxSpeed() << "))\n";
327  }
328  } else {
329  JPS_Simulation_SwitchAgentJourney(myJPSSimulation, state->getAgentId(), journeyId, startingStage, &message);
330  if (message != nullptr) {
331  WRITE_WARNINGF(TL("Error while switching to a new journey for person '%': %"), person->getID(), JPS_ErrorMessage_GetMessage(message));
332  JPS_ErrorMessage_Free(message);
333  return nullptr;
334  }
335  }
336  return state;
337 }
338 
339 
340 void
342  PState* pstate = static_cast<PState*>(state);
343  if (pstate->getStage() != nullptr) {
344  pstate->getStage()->setPState(nullptr); // we need to remove the old state reference to avoid double deletion
345  }
346  pstate->setStage(nullptr);
347 }
348 
349 
350 SUMOTime
352  const int nbrIterations = (int)(DELTA_T / myJPSDeltaT);
353  JPS_ErrorMessage message = nullptr;
354  for (int i = 0; i < nbrIterations; ++i) {
355  // Perform one JuPedSim iteration.
356  bool ok = JPS_Simulation_Iterate(myJPSSimulation, &message);
357  if (!ok) {
358  WRITE_ERRORF(TL("Error during iteration %: %"), i, JPS_ErrorMessage_GetMessage(message));
359  }
360  }
361 
362  // Update the state of all pedestrians.
363  // If necessary, this could be done more often in the loop above but the more precise positions are probably never visible.
364  // If it is needed for model correctness (precise stopping / arrivals) we should rather reduce SUMO's step-length.
365  for (auto stateIt = myPedestrianStates.begin(); stateIt != myPedestrianStates.end();) {
366  PState* const state = *stateIt;
367 
368  if (state->isWaitingToEnter()) {
369  // insertion failed at first try so we retry with some noise
370  Position p = state->getPosition(*state->getStage(), time);
371  p.setx(p.x() + RandHelper::rand(-.5, .5)); // we do this separately to avoid evaluation order problems
372  p.sety(p.y() + RandHelper::rand(-.5, .5));
373  tryPedestrianInsertion(state, p);
374  ++stateIt;
375  continue;
376  }
377 
378  MSPerson* person = state->getPerson();
379  MSStageWalking* stage = dynamic_cast<MSStageWalking*>(person->getCurrentStage());
380  if (stage == nullptr) {
381  // It seems we kept the state for another stage but the new stage is not a walk.
382  // So let's remove the state because after the new stage we will be elsewhere and need to be reinserted for JuPedSim anyway.
383  // We cannot check this earlier because when the old stage ends the next stage might not know yet whether it will be a walk.
384  registerArrived();
385  JPS_Simulation_MarkAgentForRemoval(myJPSSimulation, state->getAgentId(), nullptr);
386  stateIt = myPedestrianStates.erase(stateIt);
387  continue;
388  }
389 
390  // Updates the agent position.
391  const JPS_Agent agent = JPS_Simulation_GetAgent(myJPSSimulation, state->getAgentId(), nullptr);
392  state->setPreviousPosition(state->getPosition(*stage, DELTA_T));
393  const JPS_Point position = JPS_Agent_GetPosition(agent);
394  state->setPosition(position.x, position.y);
395 
396  // Updates the agent direction.
397  const JPS_Point orientation = JPS_Agent_GetOrientation(agent);
398  state->setAngle(atan2(orientation.y, orientation.x));
399 
400  // Find on which edge the pedestrian is, using route's forward-looking edges because of how moveToXY is written.
401  Position newPosition(position.x, position.y);
402  ConstMSEdgeVector route = stage->getEdges();
403  const int routeIndex = (int)(stage->getRouteStep() - stage->getRoute().begin());
404  ConstMSEdgeVector forwardRoute = ConstMSEdgeVector(route.begin() + routeIndex, route.end());
405  double bestDistance = std::numeric_limits<double>::max();
406  MSLane* candidateLane = nullptr;
407  double candidateLaneLongitudinalPosition = 0.0;
408  int routeOffset = 0;
409  const bool found = libsumo::Helper::moveToXYMap_matchingRoutePosition(newPosition, "",
410  forwardRoute, 0, person->getVClass(), true, bestDistance, &candidateLane, candidateLaneLongitudinalPosition, routeOffset);
411 
412  if (found) {
413  state->setLanePosition(candidateLaneLongitudinalPosition);
414  }
415 
416  const MSEdge* const expectedEdge = stage->getEdge();
417  if (found && expectedEdge->isNormal() && candidateLane->getEdge().isNormal() && &candidateLane->getEdge() != expectedEdge) {
418  const bool arrived = stage->moveToNextEdge(person, time, 1, nullptr);
419  UNUSED_PARAMETER(arrived);
420  assert(!arrived); // The person has not arrived yet.
421  stage->activateEntryReminders(person);
422  // Adapt speed to lane's speed limit.
423  JPS_CollisionFreeSpeedModelState modelState = JPS_Agent_GetCollisionFreeSpeedModelState(agent, nullptr);
424  const double newMaxSpeed = MIN2(candidateLane->getSpeedLimit(), person->getMaxSpeed());
425  if (newMaxSpeed != JPS_CollisionFreeSpeedModelState_GetV0(modelState)) {
426  JPS_CollisionFreeSpeedModelState_SetV0(modelState, newMaxSpeed);
427  }
428  }
429  for (int offset = 0; offset < 2; offset++) {
430  const WaypointDesc* const waypoint = state->getNextWaypoint(offset);
431  if (waypoint != nullptr && std::get<0>(*waypoint) != 0) {
432  const MSLane* const crossing = myCrossings[std::get<0>(*waypoint)];
433  const double speed = person->getSpeed();
434  for (const auto& ili : crossing->getIncomingLanes()) {
435  // compare to and maybe adapt for MSPModel_Striping.cpp:1264
436  const bool open = ili.viaLink->opened(time - DELTA_T, speed, speed, person->getVehicleType().getLength() + 2 * speed, person->getImpatience(), speed, 0, 0, nullptr, false, person);
437  const auto proxy = JPS_Simulation_GetWaitingSetProxy(myJPSSimulation, std::get<0>(*waypoint), &message);
438  JPS_WaitingSetProxy_SetWaitingSetState(proxy, open ? JPS_WaitingSet_Inactive : JPS_WaitingSet_Active);
439  }
440  // do the same for crossing->getLinkCont() ?
441  }
442  }
443  // In the worst case during one SUMO step the person touches the waypoint radius and walks immediately into a different direction,
444  // but at some simstep it should have a maximum distance of v * delta_t / 2 to the waypoint circle.
445  const double slack = person->getMaxSpeed() * TS / 2. + POSITION_EPS;
446  if (newPosition.distanceTo2D(std::get<1>(*state->getNextWaypoint())) < std::get<2>(*state->getNextWaypoint()) + slack) {
447  // If near the last waypoint, remove the agent.
448  if (state->advanceNextWaypoint()) {
449  // TODO this only works if the final stage is actually a walk
450  const bool finalStage = person->getNumRemainingStages() == 1;
451  const JPS_AgentId agentID = state->getAgentId();
452  while (!stage->moveToNextEdge(person, time, 1, nullptr));
453  if (finalStage) {
454  registerArrived();
455  JPS_Simulation_MarkAgentForRemoval(myJPSSimulation, agentID, nullptr);
456  stateIt = myPedestrianStates.erase(stateIt);
457  continue;
458  }
459  }
460  }
461  ++stateIt;
462  }
463 
464  // Remove pedestrians that are in a predefined area, at a predefined rate.
465  for (AreaData& area : myAreas) {
466  const std::vector<JPS_Point>& areaBoundary = area.areaBoundary;
467  JPS_AgentIdIterator agentsInArea = JPS_Simulation_AgentsInPolygon(myJPSSimulation, areaBoundary.data(), areaBoundary.size());
468  if (area.areaType == "vanishing_area") {
469  const SUMOTime period = area.params.count("period") > 0 ? string2time(area.params.at("period")) : 1000;
470  const int nbrPeriodsCoveringTimestep = (int)ceil(TS / STEPS2TIME(period));
471  if (time - area.lastRemovalTime >= nbrPeriodsCoveringTimestep * period) {
472  for (int k = 0; k < nbrPeriodsCoveringTimestep; k++) {
473  const JPS_AgentId agentID = JPS_AgentIdIterator_Next(agentsInArea);
474  if (agentID != 0) {
475  auto lambda = [agentID](const PState * const p) {
476  return p->getAgentId() == agentID;
477  };
478  std::vector<PState*>::const_iterator iterator = std::find_if(myPedestrianStates.begin(), myPedestrianStates.end(), lambda);
479  if (iterator != myPedestrianStates.end()) {
480  const PState* const state = *iterator;
481  MSPerson* const person = state->getPerson();
482  // Code below only works if the removal happens at the last stage.
483  const bool finalStage = person->getNumRemainingStages() == 1;
484  if (finalStage) {
485  WRITE_MESSAGEF(TL("Person '%' in vanishing area '%' was removed from the simulation."), person->getID(), area.id);
486  while (!state->getStage()->moveToNextEdge(person, time, 1, nullptr));
487  registerArrived();
488  JPS_Simulation_MarkAgentForRemoval(myJPSSimulation, agentID, nullptr);
489  myPedestrianStates.erase(iterator);
490  area.lastRemovalTime = time;
491  }
492  }
493  }
494  }
495  }
496  } else { // areaType == "influencer"
497  for (JPS_AgentId agentID = JPS_AgentIdIterator_Next(agentsInArea); agentID != 0; agentID = JPS_AgentIdIterator_Next(agentsInArea)) {
498  if (area.params.count("speed") > 0) {
499  const JPS_Agent agent = JPS_Simulation_GetAgent(myJPSSimulation, agentID, nullptr);
500  JPS_CollisionFreeSpeedModelState modelState = JPS_Agent_GetCollisionFreeSpeedModelState(agent, nullptr);
501  const double newMaxSpeed = StringUtils::toDouble(area.params.at("speed"));
502  if (newMaxSpeed != JPS_CollisionFreeSpeedModelState_GetV0(modelState)) {
503  JPS_CollisionFreeSpeedModelState_SetV0(modelState, newMaxSpeed);
504  }
505  }
506  }
507  }
508  JPS_AgentIdIterator_Free(agentsInArea);
509  }
510 
511  // Add dynamically additional geometry from train carriages that are stopped.
512  const auto& stoppingPlaces = myNetwork->getStoppingPlaces(SumoXMLTag::SUMO_TAG_BUS_STOP);
513  std::vector<SUMOTrafficObject::NumericalID> allStoppedTrainIDs;
514  std::vector<const MSVehicle*> allStoppedTrains;
515  for (const auto& stop : stoppingPlaces) {
516  std::vector<const SUMOVehicle*> stoppedTrains = stop.second->getStoppedVehicles();
517  for (const SUMOVehicle* train : stoppedTrains) {
518  allStoppedTrainIDs.push_back(train->getNumericalID());
519  allStoppedTrains.push_back(dynamic_cast<const MSVehicle*>(train));
520  }
521  }
522  if (allStoppedTrainIDs != myAllStoppedTrainIDs) {
524  if (!allStoppedTrainIDs.empty()) {
525  std::vector<GEOSGeometry*> carriagePolygons;
526  std::vector<GEOSGeometry*> rampPolygons;
527  for (const MSVehicle* train : allStoppedTrains) {
528  if (train->getLeavingPersonNumber() > 0) {
529  MSTrainHelper trainHelper = MSTrainHelper(train);
530  trainHelper.computeDoorPositions();
531  const std::vector<MSTrainHelper::Carriage*>& carriages = trainHelper.getCarriages();
532  for (const MSTrainHelper::Carriage* carriage : carriages) {
533  Position dir = carriage->front - carriage->back;
534  if (dir.length2D() == 0.0) {
535  continue;
536  }
537  dir.norm2D();
538  Position perp = Position(-dir.y(), dir.x());
539  // Create carriages geometry.
540  double p = trainHelper.getHalfWidth();
541  PositionVector carriageShape;
542  carriageShape.push_back(carriage->front + perp * p);
543  carriageShape.push_back(carriage->back + perp * p);
544  carriageShape.push_back(carriage->back - perp * p);
545  carriageShape.push_back(carriage->front - perp * p);
546  carriagePolygons.push_back(createGeometryFromShape(carriageShape));
547  // Create ramps geometry.
549  const double d = 0.5 * MSTrainHelper::CARRIAGE_DOOR_WIDTH;
550  for (const Position& door : carriage->doorPositions) {
551  PositionVector rampShape;
552  rampShape.push_back(door - perp * p + dir * d);
553  rampShape.push_back(door - perp * p - dir * d);
554  rampShape.push_back(door + perp * p - dir * d);
555  rampShape.push_back(door + perp * p + dir * d);
556  rampPolygons.push_back(createGeometryFromShape(rampShape));
557  }
558  }
559  }
560  }
561  if (!carriagePolygons.empty()) {
562  GEOSGeometry* carriagesCollection = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, carriagePolygons.data(), (unsigned int)carriagePolygons.size());
563  GEOSGeometry* rampsCollection = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, rampPolygons.data(), (unsigned int)rampPolygons.size());
564  GEOSGeometry* carriagesAndRampsUnion = GEOSUnion(carriagesCollection, rampsCollection);
565  if (carriagesAndRampsUnion == nullptr) {
566  WRITE_WARNING(TL("Error while generating geometry for carriages."));
567  } else {
568  GEOSGeometry* pedestrianNetworkWithTrainsAndRamps = GEOSUnion(carriagesAndRampsUnion, myGEOSPedestrianNetworkLargestComponent);
569 #ifdef DEBUG_GEOMETRY_GENERATION
570  //dumpGeometry(pedestrianNetworkWithTrainsAndRamps, "pedestrianNetworkWithTrainsAndRamps.wkt");
571 #endif
572  int nbrComponents = 0;
573  double maxArea = 0.0;
574  double totalArea = 0.0;
575  const GEOSGeometry* pedestrianNetworkWithTrainsAndRampsLargestComponent = getLargestComponent(pedestrianNetworkWithTrainsAndRamps, nbrComponents, maxArea, totalArea);
576  if (nbrComponents > 1) {
577  WRITE_WARNINGF(TL("While generating geometry % connected components were detected, %% of total pedestrian area is covered by the largest."),
578  nbrComponents, maxArea / totalArea * 100.0, "%");
579  }
580 #ifdef DEBUG_GEOMETRY_GENERATION
581  //dumpGeometry(pedestrianNetworkWithTrainsAndRampsLargestComponent, "pedestrianNetworkWithTrainsAndRamps.wkt");
582 #endif
583  myJPSGeometryWithTrainsAndRamps = buildJPSGeometryFromGEOSGeometry(pedestrianNetworkWithTrainsAndRampsLargestComponent);
584  JPS_Simulation_SwitchGeometry(myJPSSimulation, myJPSGeometryWithTrainsAndRamps, nullptr, nullptr);
587  GEOSGeom_destroy(pedestrianNetworkWithTrainsAndRamps);
588  }
589  GEOSGeom_destroy(rampsCollection);
590  GEOSGeom_destroy(carriagesCollection);
591  }
592  } else {
593  JPS_Simulation_SwitchGeometry(myJPSSimulation, myJPSGeometry, nullptr, nullptr);
595  }
596  myAllStoppedTrainIDs = allStoppedTrainIDs;
597  }
598 
599  JPS_ErrorMessage_Free(message);
600 
601  return DELTA_T;
602 }
603 
604 
605 bool
608 }
609 
610 
613 }
614 
615 
617  return myNumActivePedestrians;
618 }
619 
620 
622  myPedestrianStates.clear();
624 }
625 
626 
627 GEOSGeometry*
628 MSPModel_JuPedSim::createGeometryFromCenterLine(PositionVector centerLine, double width, int capStyle) {
629  GEOSCoordSequence* coordinateSequence = convertToGEOSPoints(centerLine);
630  GEOSGeometry* lineString = GEOSGeom_createLineString(coordinateSequence);
631  GEOSGeometry* dilatedLineString = GEOSBufferWithStyle(lineString, width, GEOS_QUADRANT_SEGMENTS, capStyle, GEOSBUF_JOIN_ROUND, GEOS_MITRE_LIMIT);
632  GEOSGeom_destroy(lineString);
633  return dilatedLineString;
634 }
635 
636 
637 GEOSGeometry*
638 MSPModel_JuPedSim::createGeometryFromShape(PositionVector shape, std::string junctionID, std::string shapeID, bool isInternalShape) {
639  // Corner case.
640  if (shape.size() == 1) {
641  WRITE_WARNINGF(TL("Polygon '%' at junction '%' will be skipped as it is just a point."), shapeID, junctionID);
642  return nullptr;
643  }
644  // Make sure the shape is closed.
645  if (shape.back() != shape.front()) {
646  shape.push_back(shape.front());
647  }
648  // Replace consecutive points that are equal with just one.
649  PositionVector cleanShape;
650  cleanShape.push_back(shape[0]);
651  PositionVector duplicates;
652  for (int i = 1; i < (int)shape.size(); i++) {
653  if (shape[i] != shape[i - 1]) {
654  cleanShape.push_back(shape[i]);
655  } else {
656  duplicates.push_back(shape[i]);
657  }
658  }
659  if (cleanShape.size() < shape.size()) {
660  WRITE_WARNINGF(TL("Polygon '%' at junction '%' had some equal consecutive points removed: %"), shapeID, junctionID, toString(duplicates, 9));
661  }
662  GEOSCoordSequence* coordinateSequence = convertToGEOSPoints(cleanShape);
663  GEOSGeometry* linearRing = GEOSGeom_createLinearRing(coordinateSequence);
664  GEOSGeometry* polygon = GEOSGeom_createPolygon(linearRing, nullptr, 0);
665 
666  if (!GEOSisValid(polygon)) {
667  if (cleanShape.size() == 3) {
668  if (isInternalShape) {
669  WRITE_WARNINGF(TL("Polygon '%' at junction '%' has been dilated as it is just a segment."), shapeID, junctionID);
670  } else {
671  WRITE_WARNINGF(TL("Polygon '%' has been dilated as it is just a segment."), shapeID);
672  }
673  GEOSGeometry* lineString = GEOSGeom_createLineString(GEOSCoordSeq_clone(coordinateSequence));
674  GEOSGeom_destroy(polygon);
675  polygon = GEOSBufferWithStyle(lineString, GEOS_BUFFERED_SEGMENT_WIDTH, GEOS_QUADRANT_SEGMENTS, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_BEVEL, GEOS_MITRE_LIMIT);
676  GEOSGeom_destroy(lineString);
677  } else {
678  if (isInternalShape) {
679  WRITE_WARNINGF(TL("Polygon '%' at junction '%' has been replaced by its convex hull as it is not simple."), shapeID, junctionID);
680  GEOSGeometry* hull = GEOSConvexHull(polygon);
681  GEOSGeom_destroy(polygon);
682  polygon = GEOSBufferWithStyle(hull, POSITION_EPS, GEOS_QUADRANT_SEGMENTS, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_ROUND, GEOS_MITRE_LIMIT);;
683  } else {
684  WRITE_WARNINGF(TL("Polygon '%' at junction '%' will be skipped as it is not simple."), shapeID, junctionID);
685  polygon = nullptr;
686  }
687  }
688 
689  return polygon;
690  }
691 
692  // Some junctions need to be buffered a little so that edges are properly connected.
693  if (isInternalShape) {
694  polygon = GEOSBufferWithStyle(polygon, POSITION_EPS, GEOS_QUADRANT_SEGMENTS, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_ROUND, GEOS_MITRE_LIMIT);
695  }
696 
697  return polygon;
698 }
699 
700 
701 GEOSGeometry*
703  std::vector<GEOSGeometry*> walkableAreas;
704  for (const MSEdge* const edge : network->getEdgeControl().getEdges()) {
705  const MSLane* const lane = getSidewalk<MSEdge, MSLane>(edge);
706  if (lane != nullptr) {
707  GEOSGeometry* dilatedLane = nullptr;
708  if (edge->isWalkingArea()) {
709  dilatedLane = createGeometryFromShape(lane->getShape(), edge->getFromJunction()->getID(), edge->getID(), true);
710  } else if (edge->isCrossing()) {
711  const PositionVector* outlineShape = lane->getOutlineShape();
712  if (outlineShape != nullptr) {
713  // this is mainly to ensure that we have at least the "normal" crossing, see #15037
714  dilatedLane = createGeometryFromCenterLine(lane->getShape(), lane->getWidth() / 2.0, GEOSBUF_CAP_FLAT);
715  if (dilatedLane != nullptr) {
716  walkableAreas.push_back(dilatedLane);
717  }
718  dilatedLane = createGeometryFromShape(*outlineShape, edge->getFromJunction()->getID(), edge->getID(), true);
719  } else {
720  dilatedLane = createGeometryFromCenterLine(lane->getShape(), lane->getWidth() / 2.0, GEOSBUF_CAP_ROUND);
721  }
722  myCrossingWaits[lane] = {0, 0};
723  } else { // regular sidewalk
724  dilatedLane = createGeometryFromCenterLine(lane->getShape(), lane->getWidth() / 2.0 + POSITION_EPS, GEOSBUF_CAP_FLAT);
725  }
726  if (dilatedLane != nullptr) {
727  walkableAreas.push_back(dilatedLane);
728  }
729  }
730  }
731  for (const auto& junctionWithID : network->getJunctionControl()) {
732  const MSJunction* const junction = junctionWithID.second;
733  int pedEdgeCount = 0;
734  bool hasWalkingArea = false;
735  for (const ConstMSEdgeVector& edges : {
736  junction->getIncoming(), junction->getOutgoing()
737  }) {
738  for (const MSEdge* const edge : edges) {
739  if (edge->isWalkingArea()) {
740  hasWalkingArea = true;
741  break;
742  }
743  if (getSidewalk<MSEdge, MSLane>(edge) != nullptr) {
744  pedEdgeCount++;
745  }
746  }
747  if (hasWalkingArea) {
748  break;
749  }
750  }
751  if (!hasWalkingArea && pedEdgeCount > 1) {
752  // there is something to connect but no walking area, let's assume peds are allowed everywhere
753  GEOSGeometry* walkingAreaGeom = createGeometryFromShape(junction->getShape(), junction->getID(), junction->getID(), true);
754  if (walkingAreaGeom != nullptr) {
755  walkableAreas.push_back(walkingAreaGeom);
756  }
757  }
758  }
759 
760  // Retrieve additional walkable areas and obstacles (walkable areas and obstacles in the sense of JuPedSim).
761  std::vector<GEOSGeometry*> additionalObstacles;
762  for (const auto& polygonWithID : myNetwork->getShapeContainer().getPolygons()) {
763  if (polygonWithID.second->getShapeType() == "jupedsim.walkable_area" || polygonWithID.second->getShapeType() == "taz") {
764  GEOSGeometry* walkableArea = createGeometryFromShape(polygonWithID.second->getShape(), std::string(""), polygonWithID.first);
765  if (walkableArea != nullptr) {
766  walkableAreas.push_back(walkableArea);
768  }
769  } else if (polygonWithID.second->getShapeType() == "jupedsim.obstacle") {
770  GEOSGeometry* additionalObstacle = createGeometryFromShape(polygonWithID.second->getShape(), std::string(""), polygonWithID.first);
771  if (additionalObstacle != nullptr) {
772  additionalObstacles.push_back(additionalObstacle);
773  }
774  }
775  }
776 
777  // Take the union of all walkable areas.
778  GEOSGeometry* disjointWalkableAreas = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, walkableAreas.data(), (unsigned int)walkableAreas.size());
779 #ifdef DEBUG_GEOMETRY_GENERATION
780  // dumpGeometry(disjointWalkableAreas, "disjointWalkableAreas.wkt");
781 #endif
782  GEOSGeometry* initialWalkableAreas = GEOSUnaryUnion(disjointWalkableAreas);
783 #ifdef DEBUG_GEOMETRY_GENERATION
784  dumpGeometry(initialWalkableAreas, "initialWalkableAreas.wkt");
785 #endif
786  GEOSGeom_destroy(disjointWalkableAreas);
787 
788  // At last, remove additional obstacles from the merged walkable areas.
789  GEOSGeometry* disjointAdditionalObstacles = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, additionalObstacles.data(), (unsigned int)additionalObstacles.size());
790 #ifdef DEBUG_GEOMETRY_GENERATION
791  // dumpGeometry(disjointAdditionalObstacles, "disjointAdditionalObstacles.wkt");
792 #endif
793  GEOSGeometry* additionalObstaclesUnion = GEOSUnaryUnion(disjointAdditionalObstacles); // Obstacles may overlap, e.g. if they were loaded from separate files.
794 #ifdef DEBUG_GEOMETRY_GENERATION
795  dumpGeometry(additionalObstaclesUnion, "additionalObstaclesUnion.wkt");
796 #endif
797  GEOSGeometry* finalWalkableAreas = GEOSDifference(initialWalkableAreas, additionalObstaclesUnion);
798 #ifdef DEBUG_GEOMETRY_GENERATION
799  dumpGeometry(finalWalkableAreas, "finalWalkableAreas.wkt");
800 #endif
801  GEOSGeom_destroy(initialWalkableAreas);
802  GEOSGeom_destroy(additionalObstaclesUnion);
803  GEOSGeom_destroy(disjointAdditionalObstacles);
804 
805  if (!GEOSisSimple(finalWalkableAreas)) {
806  throw ProcessError(TL("Union of walkable areas minus union of obstacles is not a simple polygon."));
807  }
808 
809  return finalWalkableAreas;
810 }
811 
812 
813 GEOSCoordSequence*
815  GEOSCoordSequence* coordinateSequence = GEOSCoordSeq_create((unsigned int)shape.size(), 2);
816  for (int i = 0; i < (int)shape.size(); i++) {
817  GEOSCoordSeq_setXY(coordinateSequence, i, shape[i].x(), shape[i].y());
818  }
819  return coordinateSequence;
820 }
821 
822 
824 MSPModel_JuPedSim::convertToSUMOPoints(const GEOSGeometry* geometry) {
825  PositionVector coordinateVector;
826  const GEOSCoordSequence* coordinateSequence = GEOSGeom_getCoordSeq(geometry);
827  unsigned int coordinateSequenceSize;
828  GEOSCoordSeq_getSize(coordinateSequence, &coordinateSequenceSize);
829  double x;
830  double y;
831  for (unsigned int i = 0; i < coordinateSequenceSize; i++) {
832  GEOSCoordSeq_getX(coordinateSequence, i, &x);
833  GEOSCoordSeq_getY(coordinateSequence, i, &y);
834  coordinateVector.push_back(Position(x, y));
835  }
836  return coordinateVector;
837 }
838 
839 
840 std::vector<JPS_Point>
841 MSPModel_JuPedSim::convertToJPSPoints(const GEOSGeometry* geometry) {
842  std::vector<JPS_Point> pointVector;
843  const GEOSCoordSequence* coordinateSequence = GEOSGeom_getCoordSeq(geometry);
844  unsigned int coordinateSequenceSize;
845  GEOSCoordSeq_getSize(coordinateSequence, &coordinateSequenceSize);
846  double x;
847  double y;
848  // Remove the last point so that CGAL doesn't complain about the simplicity of the polygon downstream.
849  for (unsigned int i = 0; i < coordinateSequenceSize - 1; i++) {
850  GEOSCoordSeq_getX(coordinateSequence, i, &x);
851  GEOSCoordSeq_getY(coordinateSequence, i, &y);
852  pointVector.push_back({x, y});
853  }
854  return pointVector;
855 }
856 
857 
858 double
859 MSPModel_JuPedSim::getLinearRingArea(const GEOSGeometry* linearRing) {
860  double area;
861  GEOSGeometry* linearRingAsPolygon = GEOSGeom_createPolygon(GEOSGeom_clone(linearRing), nullptr, 0);
862  GEOSArea(linearRingAsPolygon, &area);
863  GEOSGeom_destroy(linearRingAsPolygon);
864  return area;
865 }
866 
867 
868 void
869 MSPModel_JuPedSim::removePolygonFromDrawing(const std::string& polygonId) {
870  myShapeContainer.removePolygon(polygonId);
871 }
872 
873 
874 void
875 MSPModel_JuPedSim::preparePolygonForDrawing(const GEOSGeometry* polygon, const std::string& polygonId, const RGBColor& color) {
876  const GEOSGeometry* exterior = GEOSGetExteriorRing(polygon);
877  bool added = myShapeContainer.addPolygon(polygonId, std::string("jupedsim.pedestrian_network"), color, 10.0, 0.0,
878  std::string(), false, convertToSUMOPoints(exterior), false, true, 1.0);
879  if (added) {
880  std::vector<PositionVector> holes;
881  int nbrInteriorRings = GEOSGetNumInteriorRings(polygon);
882  if (nbrInteriorRings != -1) {
883  for (int k = 0; k < nbrInteriorRings; k++) {
884  const GEOSGeometry* linearRing = GEOSGetInteriorRingN(polygon, k);
885  double area = getLinearRingArea(linearRing);
886  if (area > GEOS_MIN_AREA) {
887  holes.push_back(convertToSUMOPoints(linearRing));
888  }
889  }
890  myShapeContainer.getPolygons().get(polygonId)->setHoles(holes);
891  }
892  }
893 }
894 
895 
896 const GEOSGeometry*
897 MSPModel_JuPedSim::getLargestComponent(const GEOSGeometry* polygon, int& nbrComponents, double& maxArea, double& totalArea) {
898  nbrComponents = GEOSGetNumGeometries(polygon);
899  const GEOSGeometry* largestComponent = nullptr;
900  maxArea = 0.0;
901  totalArea = 0.0;
902  for (int i = 0; i < nbrComponents; i++) {
903  const GEOSGeometry* componentPolygon = GEOSGetGeometryN(polygon, i);
904  double area;
905  GEOSArea(componentPolygon, &area);
906  totalArea += area;
907  if (area > maxArea) {
908  maxArea = area;
909  largestComponent = componentPolygon;
910  }
911  }
912  return largestComponent;
913 }
914 
915 
916 JPS_Geometry
918  JPS_GeometryBuilder geometryBuilder = JPS_GeometryBuilder_Create();
919 
920  // Handle the exterior polygon.
921  const GEOSGeometry* exterior = GEOSGetExteriorRing(polygon);
922  std::vector<JPS_Point> exteriorCoordinates = convertToJPSPoints(exterior);
923  JPS_GeometryBuilder_AddAccessibleArea(geometryBuilder, exteriorCoordinates.data(), exteriorCoordinates.size());
924 
925  // Handle the interior polygons (holes).
926  int nbrInteriorRings = GEOSGetNumInteriorRings(polygon);
927  if (nbrInteriorRings != -1) {
928  for (int k = 0; k < nbrInteriorRings; k++) {
929  const GEOSGeometry* linearRing = GEOSGetInteriorRingN(polygon, k);
930  double area = getLinearRingArea(linearRing);
931  if (area > GEOS_MIN_AREA) {
932  std::vector<JPS_Point> holeCoordinates = convertToJPSPoints(linearRing);
933  JPS_GeometryBuilder_ExcludeFromAccessibleArea(geometryBuilder, holeCoordinates.data(), holeCoordinates.size());
934  }
935  }
936  }
937 
938  JPS_ErrorMessage message = nullptr;
939  JPS_Geometry geometry = JPS_GeometryBuilder_Build(geometryBuilder, &message);
940  if (geometry == nullptr) {
941  const std::string error = TLF("Error while generating geometry: %", JPS_ErrorMessage_GetMessage(message));
942  JPS_ErrorMessage_Free(message);
943  throw ProcessError(error);
944  }
945  JPS_GeometryBuilder_Free(geometryBuilder);
946  return geometry;
947 }
948 
949 
950 void
951 MSPModel_JuPedSim::dumpGeometry(const GEOSGeometry* polygon, const std::string& filename, bool useGeoCoordinates) {
952  GEOSGeometry* polygonGeoCoordinates = nullptr;
953  if (useGeoCoordinates) {
954  const GEOSGeometry* exterior = GEOSGetExteriorRing(polygon);
955  PositionVector exteriorPoints = convertToSUMOPoints(exterior);
956  for (Position& position : exteriorPoints) {
958  }
959  const int nbrInteriorRings = GEOSGetNumInteriorRings(polygon);
960  std::vector<GEOSGeometry*> holeRings;
961  if (nbrInteriorRings != -1) {
962  for (int k = 0; k < nbrInteriorRings; k++) {
963  const GEOSGeometry* linearRing = GEOSGetInteriorRingN(polygon, k);
964  PositionVector holePoints = convertToSUMOPoints(linearRing);
965  for (Position& position : holePoints) {
967  }
968  GEOSCoordSequence* holeRingCoords = convertToGEOSPoints(holePoints);
969  GEOSGeometry* holeRing = GEOSGeom_createLinearRing(holeRingCoords);
970  holeRings.push_back(holeRing);
971  }
972  }
973  GEOSCoordSequence* exteriorRingCoords = convertToGEOSPoints(exteriorPoints);
974  GEOSGeometry* exteriorRing = GEOSGeom_createLinearRing(exteriorRingCoords);
975  polygonGeoCoordinates = GEOSGeom_createPolygon(exteriorRing, holeRings.data(), nbrInteriorRings);
976  }
977  std::ofstream dumpFile;
978  dumpFile.open(filename);
979  GEOSWKTWriter* writer = GEOSWKTWriter_create();
980  char* wkt = GEOSWKTWriter_write(writer, polygonGeoCoordinates == nullptr ? polygon : polygonGeoCoordinates);
981  dumpFile << wkt << std::endl;
982  dumpFile.close();
983  GEOSFree(wkt);
984  GEOSWKTWriter_destroy(writer);
985  GEOSGeom_destroy(polygonGeoCoordinates);
986 }
987 
988 
989 double
991  return 0.5 * MAX2(vehType.getLength(), vehType.getWidth());
992 }
993 
994 
995 JPS_StageId
996 MSPModel_JuPedSim::addWaitingSet(const MSLane* const crossing, const bool entry) {
997  JPS_ErrorMessage message = nullptr;
998  JPS_StageId waitingStage = 0;
999  const PositionVector& shape = crossing->getShape();
1000  const double radius = getRadius(*MSNet::getInstance()->getVehicleControl().getVType(DEFAULT_PEDTYPE_ID, nullptr, true));
1001  const double offset = 2 * radius + POSITION_EPS;
1002  const double lonOffset = entry ? radius + NUMERICAL_EPS : shape.length() - radius - NUMERICAL_EPS;
1003  const Position wPos = shape.positionAtOffset(lonOffset);
1004  std::vector<JPS_Point> points{{wPos.x(), wPos.y()}};
1005  for (double latOff = offset; latOff < crossing->getWidth() / 2. - offset; latOff += offset) {
1006  PositionVector moved(shape);
1007  moved.move2side(latOff);
1008  const Position wPosOff = moved.positionAtOffset(lonOffset);
1009  points.push_back({wPosOff.x(), wPosOff.y()});
1010  moved.move2side(-2. * latOff);
1011  const Position wPosOff2 = moved.positionAtOffset(lonOffset);
1012  points.push_back({wPosOff2.x(), wPosOff2.y()});
1013  }
1014  Position center = Position::INVALID;
1015  if (entry && crossing->getIncomingLanes().size() == 1 && crossing->getIncomingLanes().front().lane->isWalkingArea()) {
1016  center = crossing->getIncomingLanes().front().lane->getShape().getCentroid();
1017  }
1018  if (!entry && crossing->getLinkCont().size() == 1 && crossing->getLinkCont().front()->getLane()->isWalkingArea()) {
1019  center = crossing->getLinkCont().front()->getLane()->getShape().getCentroid();
1020  }
1021  if (center != Position::INVALID) {
1022  GEOSGeometry* point = GEOSGeom_createPointFromXY(center.x(), center.y());
1023  if (GEOSContains(myGEOSPedestrianNetworkLargestComponent, point)) {
1024  points.push_back({center.x(), center.y()});
1025  }
1026  GEOSGeom_destroy(point);
1027  }
1028  waitingStage = JPS_Simulation_AddStageWaitingSet(myJPSSimulation, points.data(), points.size(), &message);
1029  if (message != nullptr) {
1030  const std::string error = TLF("Error while adding waiting set for % on '%': %",
1031  entry ? "entry" : "exit", crossing->getID(), JPS_ErrorMessage_GetMessage(message));
1032  JPS_ErrorMessage_Free(message);
1033  throw ProcessError(error);
1034  }
1035  const auto proxy = JPS_Simulation_GetWaitingSetProxy(myJPSSimulation, waitingStage, &message);
1036  JPS_WaitingSetProxy_SetWaitingSetState(proxy, JPS_WaitingSet_Inactive);
1037  myCrossings[waitingStage] = crossing;
1038  if (myPythonScript != nullptr) {
1039  (*myPythonScript) << "ws = simulation.add_waiting_set_stage([";
1040  for (const JPS_Point& p : points) {
1041  (*myPythonScript) << "(" << p.x << "," << p.y << "),";
1042  }
1043  (*myPythonScript) << "])\nsimulation.get_stage(ws).state = jps.stages.WaitingSetState.INACTIVE\n";
1044  }
1045  return waitingStage;
1046 }
1047 
1048 
1049 void
1051  initGEOS(nullptr, nullptr);
1052  PROGRESS_BEGIN_MESSAGE("Generating initial JuPedSim geometry for pedestrian network");
1054  int nbrComponents = 0;
1055  double maxArea = 0.0;
1056  double totalArea = 0.0;
1058  if (nbrComponents > 1) {
1059  WRITE_WARNINGF(TL("While generating geometry % connected components were detected, %% of total pedestrian area is covered by the largest."),
1060  nbrComponents, maxArea / totalArea * 100.0, "%");
1061  }
1062 #ifdef DEBUG_GEOMETRY_GENERATION
1063  dumpGeometry(myGEOSPedestrianNetworkLargestComponent, "pedestrianNetwork.wkt");
1064 #endif
1065  const std::string filename = oc.getString("pedestrian.jupedsim.wkt");
1066  if (!filename.empty()) {
1067  dumpGeometry(myGEOSPedestrianNetworkLargestComponent, filename, oc.getBool("pedestrian.jupedsim.wkt.geo"));
1068  }
1069  // For the moment, only one connected component is supported.
1074  JPS_ErrorMessage message = nullptr;
1075 
1076  double strengthGeometryRepulsion = oc.getFloat("pedestrian.jupedsim.strength-geometry-repulsion");
1077  double rangeGeometryRepulsion = oc.getFloat("pedestrian.jupedsim.range-geometry-repulsion");
1078  if (myJPSDeltaT == 20) {
1079  if (oc.isDefault("pedestrian.jupedsim.strength-geometry-repulsion") && oc.isDefault("pedestrian.jupedsim.range-geometry-repulsion")) {
1080  WRITE_MESSAGE(TL("Adapting geometry repulsion default values for jupedsim timestep of 0.02."));
1081  strengthGeometryRepulsion = 35.;
1082  rangeGeometryRepulsion = 0.019;
1083  }
1084  }
1085  if (oc.getString("pedestrian.jupedsim.model") == "CollisionFreeSpeed") {
1086  JPS_CollisionFreeSpeedModelBuilder modelBuilder = JPS_CollisionFreeSpeedModelBuilder_Create(oc.getFloat("pedestrian.jupedsim.strength-neighbor-repulsion"),
1087  oc.getFloat("pedestrian.jupedsim.range-neighbor-repulsion"),
1088  strengthGeometryRepulsion, rangeGeometryRepulsion);
1089  myJPSModel = JPS_CollisionFreeSpeedModelBuilder_Build(modelBuilder, &message);
1090  JPS_CollisionFreeSpeedModelBuilder_Free(modelBuilder);
1091  } else {
1092  throw ProcessError(TLF("Unknown JuPedSim model: %", oc.getString("pedestrian.jupedsim.model")));
1093  }
1094 
1095  if (myJPSModel == nullptr) {
1096  const std::string error = TLF("Error creating the pedestrian model: %", JPS_ErrorMessage_GetMessage(message));
1097  JPS_ErrorMessage_Free(message);
1098  throw ProcessError(error);
1099  }
1100  myJPSSimulation = JPS_Simulation_Create(myJPSModel, myJPSGeometry, STEPS2TIME(myJPSDeltaT), &message);
1101  if (myJPSSimulation == nullptr) {
1102  const std::string error = TLF("Error creating the simulation: %", JPS_ErrorMessage_GetMessage(message));
1103  JPS_ErrorMessage_Free(message);
1104  throw ProcessError(error);
1105  }
1106  // Polygons that define vanishing areas aren't part of the regular JuPedSim geometry.
1107  for (const auto& polygonWithID : myNetwork->getShapeContainer().getPolygons()) {
1108  const SUMOPolygon* const poly = polygonWithID.second;
1109  if (poly->getShapeType() == "jupedsim.vanishing_area" || poly->getShapeType() == "jupedsim.influencer") {
1110  std::vector<JPS_Point> areaBoundary;
1111  for (const Position& p : poly->getShape()) {
1112  areaBoundary.push_back({p.x(), p.y()});
1113  }
1114  // Make sure the shape is not repeating the first point.
1115  if (areaBoundary.back().x == areaBoundary.front().x && areaBoundary.back().y == areaBoundary.front().y) {
1116  areaBoundary.pop_back();
1117  }
1118  const std::string type = StringTokenizer(poly->getShapeType(), ".").getVector()[1];
1119  myAreas.push_back({poly->getID(), type, areaBoundary, poly->getParametersMap(), 0});
1120  }
1121  }
1122  if (OutputDevice::createDeviceByOption("pedestrian.jupedsim.py")) {
1123  myPythonScript = &OutputDevice::getDeviceByOption("pedestrian.jupedsim.py");
1125  (*myPythonScript) << "import jupedsim as jps\nimport shapely\n\n"
1126  "with open('" << filename << "') as f: geom = shapely.from_wkt(f.read())\n"
1127  "simulation = jps.Simulation(dt=" << STEPS2TIME(myJPSDeltaT) << ", model=jps.CollisionFreeSpeedModel(), geometry=geom,\n"
1128  "trajectory_writer=jps.SqliteTrajectoryWriter(output_file='out.sql'))\n";
1129  }
1130  // add waiting sets at crossings
1131  for (auto& crossing : myCrossingWaits) {
1132  crossing.second.first = addWaitingSet(crossing.first, true);
1133  crossing.second.second = addWaitingSet(crossing.first, false);
1134  }
1135 }
1136 
1137 
1138 // ===========================================================================
1139 // MSPModel_Remote::PState method definitions
1140 // ===========================================================================
1142  JPS_JourneyId journeyId, JPS_StageId stageId,
1143  const std::vector<WaypointDesc>& waypoints)
1144  : myPerson(person), myStage(stage), myJourneyId(journeyId), myStageId(stageId), myWaypoints(waypoints),
1145  myAgentId(0), myPosition(0, 0), myAngle(0), myWaitingToEnter(true) {
1146 }
1147 
1148 
1149 void
1150 MSPModel_JuPedSim::PState::reinit(MSStageMoving* stage, JPS_JourneyId journeyId, JPS_StageId stageId,
1151  const std::vector<WaypointDesc>& waypoints) {
1152  if (myStage != nullptr) {
1153  myStage->setPState(nullptr); // we need to remove the old state reference to avoid double deletion
1154  }
1155  myStage = stage;
1156  myJourneyId = journeyId;
1157  myStageId = stageId;
1158  myWaypoints = waypoints;
1159 }
1160 
1161 
1163 }
1164 
1165 
1167  return myPosition;
1168 }
1169 
1170 
1171 void MSPModel_JuPedSim::PState::setPosition(double x, double y) {
1172  myPosition.set(x, y);
1173 }
1174 
1175 
1177  myPreviousPosition = previousPosition;
1178 }
1179 
1180 
1181 double MSPModel_JuPedSim::PState::getAngle(const MSStageMoving& /* stage */, SUMOTime /* now */) const {
1182  return myAngle;
1183 }
1184 
1185 
1187  myAngle = angle;
1188 }
1189 
1190 
1192  return myStage;
1193 }
1194 
1195 
1196 void
1198  myStage = stage;
1199 }
1200 
1201 
1203  return myPerson;
1204 }
1205 
1206 
1208  myLanePosition = lanePosition;
1209 }
1210 
1211 
1212 double MSPModel_JuPedSim::PState::getEdgePos(const MSStageMoving& /* stage */, SUMOTime /* now */) const {
1213  return myLanePosition;
1214 }
1215 
1216 
1217 int MSPModel_JuPedSim::PState::getDirection(const MSStageMoving& /* stage */, SUMOTime /* now */) const {
1218  return UNDEFINED_DIRECTION;
1219 }
1220 
1221 
1223  return 0;
1224 }
1225 
1226 
1227 double MSPModel_JuPedSim::PState::getSpeed(const MSStageMoving& /* stage */) const {
1228  return myPosition.distanceTo2D(myPreviousPosition) / STEPS2TIME(DELTA_T);
1229 }
1230 
1231 
1233  return stage.getNextRouteEdge();
1234 }
1235 
1236 
1239  return offset < (int)myWaypoints.size() ? &myWaypoints[offset] : nullptr;
1240 }
1241 
1242 
1244  return myAgentId;
1245 }
long long int SUMOTime
Definition: GUI.h:35
std::vector< const MSEdge * > ConstMSEdgeVector
Definition: MSEdge.h:74
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define WRITE_MESSAGEF(...)
Definition: MsgHandler.h:298
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:305
#define WRITE_MESSAGE(msg)
Definition: MsgHandler.h:297
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:295
#define TL(string)
Definition: MsgHandler.h:315
#define PROGRESS_DONE_MESSAGE()
Definition: MsgHandler.h:300
#define TLF(string,...)
Definition: MsgHandler.h:317
#define PROGRESS_BEGIN_MESSAGE(msg)
Definition: MsgHandler.h:299
SUMOTime DELTA_T
Definition: SUMOTime.cpp:38
SUMOTime string2time(const std::string &r)
convert string to SUMOTime
Definition: SUMOTime.cpp:46
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition: SUMOTime.cpp:69
#define STEPS2TIME(x)
Definition: SUMOTime.h:55
#define SIMSTEP
Definition: SUMOTime.h:61
#define TS
Definition: SUMOTime.h:42
const std::string DEFAULT_PEDTYPE_ID
@ RANDOM_LOCATION
The position may be chosen freely in a polygon defined by a taz.
@ SUMO_TAG_BUS_STOP
A bus stop.
const double SUMO_const_laneWidth
Definition: StdDefs.h:48
#define UNUSED_PARAMETER(x)
Definition: StdDefs.h:30
T MIN2(T a, T b)
Definition: StdDefs.h:76
T MAX2(T a, T b)
Definition: StdDefs.h:82
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
A class that stores a 2D geometrical boundary.
Definition: Boundary.h:39
double ymin() const
Returns minimum y-coordinate.
Definition: Boundary.cpp:130
double xmin() const
Returns minimum x-coordinate.
Definition: Boundary.cpp:118
double ymax() const
Returns maximum y-coordinate.
Definition: Boundary.cpp:136
double xmax() const
Returns maximum x-coordinate.
Definition: Boundary.cpp:124
void cartesian2geo(Position &cartesian) const
Converts the given cartesian (shifted) position to its geo (lat/long) representation.
static const GeoConvHelper & getFinal()
the coordinate transformation for writing the location element and for tracking the original coordina...
const MSEdgeVector & getEdges() const
Returns loaded edges.
A road/street connecting two junctions.
Definition: MSEdge.h:77
bool isNormal() const
return whether this edge is an internal edge
Definition: MSEdge.h:260
const MSJunction * getFromJunction() const
Definition: MSEdge.h:411
bool isTazConnector() const
Definition: MSEdge.h:288
const MSJunction * getToJunction() const
Definition: MSEdge.h:415
virtual void addEvent(Command *operation, SUMOTime execTimeStep=-1)
Adds an Event.
static bool gUsingInternalLanes
Information whether the simulation regards internal lanes.
Definition: MSGlobals.h:78
The base class for an intersection.
Definition: MSJunction.h:58
const ConstMSEdgeVector & getIncoming() const
Definition: MSJunction.h:108
const PositionVector & getShape() const
Returns this junction's shape.
Definition: MSJunction.h:91
const ConstMSEdgeVector & getOutgoing() const
Definition: MSJunction.h:114
Representation of a lane in the micro simulation.
Definition: MSLane.h:84
const std::vector< MSLink * > & getLinkCont() const
returns the container with all links !!!
Definition: MSLane.h:716
double getSpeedLimit() const
Returns the lane's maximum allowed speed.
Definition: MSLane.h:584
const PositionVector * getOutlineShape() const
Definition: MSLane.h:1342
const std::vector< IncomingLaneInfo > & getIncomingLanes() const
Definition: MSLane.h:942
MSEdge & getEdge() const
Returns the lane's edge.
Definition: MSLane.h:756
double getWidth() const
Returns the lane's width.
Definition: MSLane.h:627
virtual const PositionVector & getShape(bool) const
Definition: MSLane.h:294
The simulated network and simulation perfomer.
Definition: MSNet.h:89
MSPedestrianRouter & getPedestrianRouter(const int rngIndex, const MSEdgeVector &prohibited=MSEdgeVector()) const
Definition: MSNet.cpp:1494
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:182
ShapeContainer & getShapeContainer()
Returns the shapes container.
Definition: MSNet.h:501
MSJunctionControl & getJunctionControl()
Returns the junctions control.
Definition: MSNet.h:461
SUMOTime getCurrentTimeStep() const
Returns the current simulation step.
Definition: MSNet.h:320
MSEdgeControl & getEdgeControl()
Returns the edge control.
Definition: MSNet.h:421
MSEventControl * getBeginOfTimestepEvents()
Returns the event control for events executed at the begin of a time step.
Definition: MSNet.h:471
bool hasInternalLinks() const
return whether the network contains internal links
Definition: MSNet.h:774
const NamedObjectCont< MSStoppingPlace * > & getStoppingPlaces(SumoXMLTag category) const
Definition: MSNet.cpp:1387
Holds pedestrian state and performs updates.
JPS_StageId getStageId() const
first stage of the journey
void setPosition(double x, double y)
JPS_AgentId getAgentId() const
void setStage(MSStageMoving *const stage)
SUMOTime getWaitingTime(const MSStageMoving &stage, SUMOTime now) const override
return the time the transportable spent standing
double getEdgePos(const MSStageMoving &stage, SUMOTime now) const override
return the offset from the start of the current edge measured in its natural direction
double getSpeed(const MSStageMoving &stage) const override
return the current speed of the transportable
void setAgentId(JPS_AgentId id)
const MSPModel_JuPedSim::WaypointDesc * getNextWaypoint(const int offset=0) const
void setPreviousPosition(Position previousPosition)
void setLanePosition(double lanePosition)
const MSEdge * getNextEdge(const MSStageMoving &stage) const override
return the list of internal edges if the transportable is on an intersection
int getDirection(const MSStageMoving &stage, SUMOTime now) const override
return the walking direction (FORWARD, BACKWARD)
JPS_JourneyId getJourneyId() const
PState(MSPerson *person, MSStageMoving *stage, JPS_JourneyId journeyId, JPS_StageId stageId, const std::vector< WaypointDesc > &waypoints)
void reinit(MSStageMoving *stage, JPS_JourneyId journeyId, JPS_StageId stageId, const std::vector< WaypointDesc > &waypoints)
Position getPosition(const MSStageMoving &stage, SUMOTime now) const override
return the network coordinate of the transportable
double getAngle(const MSStageMoving &stage, SUMOTime now) const override
return the direction in which the transportable faces in degrees
MSStageMoving * getStage() const
JPS_StageId addWaitingSet(const MSLane *const crossing, const bool entry)
OutputDevice * myPythonScript
MSTransportableStateAdapter * add(MSTransportable *person, MSStageMoving *stage, SUMOTime now) override
register the given person as a pedestrian
void removePolygonFromDrawing(const std::string &polygonId)
JPS_OperationalModel myJPSModel
const GEOSGeometry * myGEOSPedestrianNetworkLargestComponent
The GEOS polygon representing the largest (by area) connected component of the pedestrian network.
void preparePolygonForDrawing(const GEOSGeometry *polygon, const std::string &polygonId, const RGBColor &color)
static const RGBColor PEDESTRIAN_NETWORK_CARRIAGES_AND_RAMPS_COLOR
MSPModel_JuPedSim(const OptionsCont &oc, MSNet *net)
static const double GEOS_MITRE_LIMIT
const double myExitTolerance
Threshold to decide if a pedestrian has ended its journey or not.
std::vector< SUMOTrafficObject::NumericalID > myAllStoppedTrainIDs
Array of stopped trains, used to detect whether to add carriages and ramps to the geometry.
bool addStage(JPS_JourneyDescription journey, JPS_StageId &predecessor, const std::string &agentID, const JPS_StageId stage)
static GEOSGeometry * createGeometryFromCenterLine(PositionVector centerLine, double width, int capStyle)
static PositionVector convertToSUMOPoints(const GEOSGeometry *geometry)
static const std::string PEDESTRIAN_NETWORK_CARRIAGES_AND_RAMPS_ID
static const RGBColor PEDESTRIAN_NETWORK_COLOR
JPS_Geometry myJPSGeometry
The JPS polygon representing the largest connected component of the pedestrian network.
static const std::string PEDESTRIAN_NETWORK_ID
bool addWaypoint(JPS_JourneyDescription journey, JPS_StageId &predecessor, const std::string &agentID, const WaypointDesc &waypoint)
static GEOSGeometry * createGeometryFromShape(PositionVector shape, std::string junctionID=std::string(""), std::string shapeID=std::string(""), bool isInternalShape=false)
static GEOSCoordSequence * convertToGEOSPoints(PositionVector shape)
JPS_Simulation myJPSSimulation
GEOSGeometry * myGEOSPedestrianNetwork
The GEOS polygon containing all computed connected components of the pedestrian network.
static void dumpGeometry(const GEOSGeometry *polygon, const std::string &filename, bool useGeoCoordinates=false)
std::vector< AreaData > myAreas
Array of special areas.
static std::vector< JPS_Point > convertToJPSPoints(const GEOSGeometry *geometry)
static JPS_Geometry buildJPSGeometryFromGEOSGeometry(const GEOSGeometry *polygon)
const SUMOTime myJPSDeltaT
Timestep used in the JuPedSim simulator.
MSNet *const myNetwork
The network on which the simulation runs.
std::map< JPS_StageId, const MSLane * > myCrossings
JPS_Geometry myJPSGeometryWithTrainsAndRamps
The JPS polygon representing the largest connected component plus carriages and ramps.
static const double CARRIAGE_RAMP_LENGTH
static const double GEOS_MIN_AREA
std::tuple< JPS_StageId, Position, double > WaypointDesc
void initialize(const OptionsCont &oc)
GEOSGeometry * buildPedestrianNetwork(MSNet *network)
void remove(MSTransportableStateAdapter *state) override
remove the specified person from the pedestrian simulation
static double getLinearRingArea(const GEOSGeometry *linearRing)
std::map< const MSLane *, std::pair< JPS_StageId, JPS_StageId > > myCrossingWaits
void clearState() override
Resets pedestrians when quick-loading state.
ShapeContainer & myShapeContainer
The shape container used to add polygons to the rendering pipeline.
std::vector< PState * > myPedestrianStates
static const int GEOS_QUADRANT_SEGMENTS
static const GEOSGeometry * getLargestComponent(const GEOSGeometry *polygon, int &nbrComponents, double &maxArea, double &totalArea)
static double getRadius(const MSVehicleType &vehType)
void tryPedestrianInsertion(PState *state, const Position &p)
SUMOTime execute(SUMOTime time)
static const double GEOS_BUFFERED_SEGMENT_WIDTH
int getActiveNumber() override
return the number of active objects
bool usingInternalLanes() override
whether movements on intersections are modelled
static const int BACKWARD
Definition: MSPModel.h:120
static const int FORWARD
Definition: MSPModel.h:119
static const double RANDOM_POS_LAT
magic value to encode randomized lateral offset for persons when starting a walk
Definition: MSPModel.h:133
static const int UNDEFINED_DIRECTION
Definition: MSPModel.h:121
static const double UNSPECIFIED_POS_LAT
the default lateral offset for persons when starting a walk
Definition: MSPModel.h:130
double getImpatience() const
Definition: MSPerson.cpp:209
const MSEdge * getDestination() const
returns the destination edge
Definition: MSStage.cpp:65
virtual double getArrivalPos() const
Definition: MSStage.h:93
virtual const MSEdge * getEdge() const
Returns the current edge.
Definition: MSStage.cpp:71
ConstMSEdgeVector getEdges() const
the edges of the current stage
void setPState(MSTransportableStateAdapter *pstate)
Definition: MSStageMoving.h:53
const std::vector< const MSEdge * >::iterator getRouteStep() const
double getDepartPosLat() const
const MSEdge * getEdge() const
Returns the current edge.
virtual const MSEdge * getNextRouteEdge() const =0
const std::vector< const MSEdge * > & getRoute() const
double getDepartPos() const
virtual bool moveToNextEdge(MSTransportable *transportable, SUMOTime currentTime, int prevDir, MSEdge *nextInternal=nullptr, const bool isReplay=false)=0
move forward and return whether the transportable arrived
virtual double getMaxSpeed(const MSTransportable *const transportable=nullptr) const =0
the maximum speed of the transportable
bool moveToNextEdge(MSTransportable *person, SUMOTime currentTime, int prevDir, MSEdge *nextInternal=nullptr, const bool isReplay=false)
move forward and return whether the person arrived
void activateEntryReminders(MSTransportable *person, const bool isDepart=false)
add the move reminders for the current lane on entry
A class that helps computing positions of a train's carriages and additional structures.
Definition: MSTrainHelper.h:42
const std::vector< Carriage * > & getCarriages() const
Definition: MSTrainHelper.h:99
void computeDoorPositions()
compute door positions on demand and fills the carriage structures
static const double CARRIAGE_DOOR_WIDTH
average door width used to compute doors positions
double getHalfWidth() const
Definition: MSTrainHelper.h:67
SUMOVehicleClass getVClass() const
Returns the object's access class.
MSStage * getNextStage(int offset) const
Return the next (or previous) stage denoted by the offset.
int getNumRemainingStages() const
Return the number of remaining stages (including the current)
virtual double getSpeed() const
the current speed of the transportable
const SUMOVehicleParameter & getParameter() const
Returns the vehicle's parameter (including departure definition)
const MSVehicleType & getVehicleType() const
Returns the object's "vehicle" type.
int getCurrentStageIndex() const
Return the index of the current stage.
MSStageType getCurrentStageType() const
the current stage type of the transportable
const MSEdge * getEdge() const
Returns the current edge.
MSStage * getCurrentStage() const
Return the current stage.
double getMaxSpeed() const
Returns the maximum speed (the minimum of desired and physical maximum speed)
abstract base class for managing callbacks to retrieve various state information from the model
Definition: MSPModel.h:156
Representation of a vehicle in the micro simulation.
Definition: MSVehicle.h:77
The car-following model and parameter.
Definition: MSVehicleType.h:63
double getWidth() const
Get the width which vehicles of this class shall have when being drawn.
double getLength() const
Get vehicle's length [m].
const std::string & getID() const
Returns the id.
Definition: Named.h:74
T get(const std::string &id) const
Retrieves an item.
A storage for options typed value containers)
Definition: OptionsCont.h:89
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
static bool createDeviceByOption(const std::string &optionName, const std::string &rootElement="", const std::string &schemaFile="")
Creates the device using the output definition stored in the named option.
static OutputDevice & getDeviceByOption(const std::string &name)
Returns the device described by the option.
void setPrecision(int precision=gPrecision)
Sets the precision or resets it to default.
double getDouble(const std::string &key, const double defaultValue) const
Returns the value for a given key converted to a double.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
const Parameterised::Map & getParametersMap() const
Returns the inner key/value map.
double compute(const E *from, const E *to, double departPos, double arrivalPos, double speed, SUMOTime msTime, const N *onlyNode, std::vector< const E * > &into, bool allEdges=false)
Builds the route between the given edges using the minimum effort at the given time The definition of...
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
void setx(double x)
set position x
Definition: Position.h:70
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:317
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:271
void norm2D()
Normalizes the given vector.
Definition: Position.h:177
double x() const
Returns the x-position.
Definition: Position.h:55
double length2D() const
Computes the length of the given vector.
Definition: Position.h:172
void sety(double y)
set position y
Definition: Position.h:75
double y() const
Returns the y-position.
Definition: Position.h:60
A list of positions.
double length() const
Returns the length.
double rotationAtOffset(double pos) const
Returns the rotation at the given length.
Position positionAtOffset(double pos, double lateralOffset=0) const
Returns the position at the given length.
void move2side(double amount, double maxExtension=100)
move position vector to side using certain amount
Boundary getBoxBoundary() const
Returns a boundary enclosing this list of lines.
bool around(const Position &p, double offset=0) const
Returns the information whether the position vector describes a polygon lying around the given point.
static double rand(SumoRNG *rng=nullptr)
Returns a random real number in [0, 1)
Definition: RandHelper.cpp:94
const PositionVector & getShape() const
Returns the shape of the polygon.
Definition: SUMOPolygon.cpp:51
virtual void setHoles(const std::vector< PositionVector > &holes)
Sets the holes of the polygon.
Definition: SUMOPolygon.cpp:92
Representation of a vehicle.
Definition: SUMOVehicle.h:60
DepartPosDefinition departPosProcedure
Information how the vehicle shall choose the departure position.
const Polygons & getPolygons() const
Returns all polygons.
virtual bool removePolygon(const std::string &id, bool useLock=true)
Removes a polygon from the container.
virtual bool addPolygon(const std::string &id, const std::string &type, const RGBColor &color, double layer, double angle, const std::string &imgFile, bool relativePath, const PositionVector &shape, bool geo, bool fill, double lineWidth, bool ignorePruning=false, const std::string &name=Shape::DEFAULT_NAME)
Builds a polygon using the given values and adds it to the container.
const std::string & getShapeType() const
Returns the (abstract) type of the Shape.
Definition: Shape.h:77
std::vector< std::string > getVector()
return vector of strings
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter
static bool moveToXYMap_matchingRoutePosition(const Position &pos, const std::string &origID, const ConstMSEdgeVector &currentRoute, int routeIndex, SUMOVehicleClass vClass, bool setLateralPos, double &bestDistance, MSLane **lane, double &lanePos, int &routeOffset)
Definition: Helper.cpp:1731
Structure that keeps data related to vanishing areas (and other types of areas).