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 const std::vector<MSPModel_JuPedSim::PState*> MSPModel_JuPedSim::noPedestrians;
64 
65 // ===========================================================================
66 // method definitions
67 // ===========================================================================
69  myNetwork(net), myShapeContainer(net->getShapeContainer()), myJPSDeltaT(string2time(oc.getString("pedestrian.jupedsim.step-length"))),
70  myExitTolerance(oc.getFloat("pedestrian.jupedsim.exit-tolerance")), myGEOSPedestrianNetworkLargestComponent(nullptr),
71  myHaveAdditionalWalkableAreas(false) {
72  std::string model = oc.getString("pedestrian.jupedsim.model");
73  if (model == "CollisionFreeSpeed") {
75  } else if (model == "CollisionFreeSpeedV2") {
77  } else if (model == "GeneralizedCentrifugalForce") {
79  } else if (model == "SocialForce") {
81  }
82  initialize(oc);
84 }
85 
86 
88  clearState();
89  if (myPythonScript != nullptr) {
90  (*myPythonScript) << "while simulation.agent_count() > 0 and simulation.iteration_count() < 160 * 100: simulation.iterate()\n";
91  }
92 
93  JPS_Simulation_Free(myJPSSimulation);
94  JPS_OperationalModel_Free(myJPSOperationalModel);
95  JPS_Geometry_Free(myJPSGeometry);
96  if (myJPSGeometryWithTrainsAndRamps != nullptr) {
97  JPS_Geometry_Free(myJPSGeometryWithTrainsAndRamps);
98  }
99 
100  GEOSGeom_destroy(myGEOSPedestrianNetwork);
101  finishGEOS();
102 }
103 
104 
105 void
107  const MSVehicleType& type = state->getPerson()->getVehicleType();
108 
109  const double angle = state->getAngle(*state->getStage(), 0);
110  JPS_Point orientation;
111  if (fabs(angle - M_PI / 2) < NUMERICAL_EPS) {
112  orientation = JPS_Point{0., 1.};
113  } else if (fabs(angle + M_PI / 2) < NUMERICAL_EPS) {
114  orientation = JPS_Point{0., -1.};
115  } else {
116  orientation = JPS_Point{1., tan(angle)};
117  }
118 
119  JPS_AgentId agentId = 0;
120  JPS_ErrorMessage message = nullptr;
121 
122  switch (myJPSModel) {
124  JPS_CollisionFreeSpeedModelAgentParameters agentParameters{};
125  agentParameters.position = {p.x(), p.y()};
126  agentParameters.journeyId = state->getJourneyId();
127  agentParameters.stageId = state->getStageId();
128  agentParameters.v0 = state->getPerson()->getMaxSpeed();
129  agentParameters.radius = getRadius(type);
130  agentId = JPS_Simulation_AddCollisionFreeSpeedModelAgent(myJPSSimulation, agentParameters, &message);
131  break;
132  }
134  JPS_CollisionFreeSpeedModelV2AgentParameters agentParameters{};
135  agentParameters.position = {p.x(), p.y()};
136  agentParameters.journeyId = state->getJourneyId();
137  agentParameters.stageId = state->getStageId();
138  agentParameters.v0 = state->getPerson()->getMaxSpeed();
139  agentParameters.radius = getRadius(type);
140  agentId = JPS_Simulation_AddCollisionFreeSpeedModelV2Agent(myJPSSimulation, agentParameters, &message);
141  break;
142  }
144  JPS_GeneralizedCentrifugalForceModelAgentParameters agentParameters{};
145  agentParameters.position = {p.x(), p.y()};
146  agentParameters.orientation = orientation;
147  agentParameters.journeyId = state->getJourneyId();
148  agentParameters.stageId = state->getStageId();
149  agentParameters.v0 = state->getPerson()->getMaxSpeed();
150  agentId = JPS_Simulation_AddGeneralizedCentrifugalForceModelAgent(myJPSSimulation, agentParameters, &message);
151  break;
152  }
153  case JPS_Model::SocialForce: {
154  JPS_SocialForceModelAgentParameters agentParameters{};
155  agentParameters.position = {p.x(), p.y()};
156  agentParameters.orientation = orientation;
157  agentParameters.journeyId = state->getJourneyId();
158  agentParameters.stageId = state->getStageId();
159  agentParameters.desiredSpeed = state->getPerson()->getMaxSpeed();
160  agentParameters.radius = getRadius(type);
161  agentId = JPS_Simulation_AddSocialForceModelAgent(myJPSSimulation, agentParameters, &message);
162  break;
163  }
164  }
165  if (message != nullptr) {
166  WRITE_WARNINGF(TL("Error while adding person '%' as JuPedSim agent: %"), state->getPerson()->getID(), JPS_ErrorMessage_GetMessage(message));
167  JPS_ErrorMessage_Free(message);
168  } else {
169  state->setAgentId(agentId);
170  }
171 }
172 
173 
174 bool
175 MSPModel_JuPedSim::addStage(JPS_JourneyDescription journey, JPS_StageId& predecessor, const std::string& agentID, const JPS_StageId stage) {
176  JPS_ErrorMessage message = nullptr;
177  if (predecessor != 0) {
178  const JPS_Transition transition = JPS_Transition_CreateFixedTransition(stage, &message);
179  if (message != nullptr) {
180  WRITE_WARNINGF(TL("Error while creating fixed transition for person '%': %"), agentID, JPS_ErrorMessage_GetMessage(message));
181  JPS_ErrorMessage_Free(message);
182  return false;
183  }
184  JPS_JourneyDescription_SetTransitionForStage(journey, predecessor, transition, &message);
185  if (message != nullptr) {
186  WRITE_WARNINGF(TL("Error while setting transition for person '%': %"), agentID, JPS_ErrorMessage_GetMessage(message));
187  JPS_ErrorMessage_Free(message);
188  return false;
189  }
190  JPS_Transition_Free(transition);
191  }
192  JPS_JourneyDescription_AddStage(journey, stage);
193  predecessor = stage;
194  return true;
195 }
196 
197 
198 bool
199 MSPModel_JuPedSim::addWaypoint(JPS_JourneyDescription journey, JPS_StageId& predecessor, const std::string& agentID, const WaypointDesc& waypoint) {
200  JPS_ErrorMessage message = nullptr;
201  const Position& coords = std::get<1>(waypoint);
202  const JPS_StageId waypointId = JPS_Simulation_AddStageWaypoint(myJPSSimulation, {coords.x(), coords.y()},
203  std::get<2>(waypoint), &message);
204  if (message != nullptr) {
205  WRITE_WARNINGF(TL("Error while adding waypoint for person '%': %"), agentID, JPS_ErrorMessage_GetMessage(message));
206  JPS_ErrorMessage_Free(message);
207  return false;
208  }
209  const JPS_StageId waiting = std::get<0>(waypoint);
210  if (waiting != 0) {
211  if (myPythonScript != nullptr) {
212  (*myPythonScript) << "journey.add(" << waiting << ")\n";
213  if (predecessor != 0) {
214  (*myPythonScript) << "journey.set_transition_for_stage(" << predecessor << ", jps.Transition.create_fixed_transition(" << waiting << "))\n";
215  }
216  }
217  if (!addStage(journey, predecessor, agentID, waiting)) {
218  return false;
219  }
220  }
221  if (myPythonScript != nullptr) {
222  (*myPythonScript) << "ws = simulation.add_waypoint_stage((" << coords.x() << "," << coords.y() << "), " << std::get<2>(waypoint) << ")\n"
223  "journey.add(ws)\n";
224  if (predecessor != 0) {
225  (*myPythonScript) << "journey.set_transition_for_stage(" << predecessor << ", jps.Transition.create_fixed_transition(ws))\n";
226  }
227  }
228  if (!addStage(journey, predecessor, agentID, waypointId)) {
229  return false;
230  }
231  return true;
232 }
233 
234 
237  assert(person->getCurrentStageType() == MSStageType::WALKING);
238  const double radius = getRadius(person->getVehicleType());
239  Position departurePosition = Position::INVALID;
240  const MSLane* const departureLane = getSidewalk<MSEdge, MSLane>(stage->getRoute().front());
241  if (departureLane == nullptr) {
242  const char* error = TL("Person '%' could not find sidewalk on edge '%', time=%.");
243  if (OptionsCont::getOptions().getBool("ignore-route-errors")) {
244  WRITE_WARNINGF(error, person->getID(), person->getEdge()->getID(), time2string(SIMSTEP));
245  return nullptr;
246  } else {
247  throw ProcessError(TLF(error, person->getID(), person->getEdge()->getID(), time2string(SIMSTEP)));
248  }
249  }
250  // First real stage, stage 0 is waiting.
252  const MSEdge* const tripOrigin = person->getNextStage(-1)->getEdge();
253  if (tripOrigin->isTazConnector()) {
254  const SUMOPolygon* tazShape = myNetwork->getShapeContainer().getPolygons().get(tripOrigin->getParameter("taz"));
255  if (tazShape == nullptr) {
256  WRITE_WARNINGF(TL("FromTaz '%' for person '%' has no shape information."), tripOrigin->getParameter("taz"), person->getID());
257  } else {
258  const Boundary& bbox = tazShape->getShape().getBoxBoundary();
259  while (!tazShape->getShape().around(departurePosition)) {
260  // TODO: Optimize for speed if necessary or at least abort trying to find a point.
261  departurePosition.setx(RandHelper::rand(bbox.xmin(), bbox.xmax()));
262  departurePosition.sety(RandHelper::rand(bbox.ymin(), bbox.ymax()));
263  }
264  }
265  }
266  }
267  if (departurePosition == Position::INVALID) {
268  const double halfDepartureLaneWidth = departureLane->getWidth() / 2.0;
269  double departureRelativePositionY = stage->getDepartPosLat();
270  if (departureRelativePositionY == UNSPECIFIED_POS_LAT) {
271  departureRelativePositionY = 0.0;
272  }
273  if (departureRelativePositionY == MSPModel::RANDOM_POS_LAT) {
274  departureRelativePositionY = RandHelper::rand(-halfDepartureLaneWidth, halfDepartureLaneWidth);
275  }
276  departurePosition = departureLane->getShape().positionAtOffset(stage->getDepartPos() + radius + POSITION_EPS, -departureRelativePositionY); // Minus sign is here for legacy reasons.
277  }
278 
279  std::vector<WaypointDesc> waypoints;
280  const MSEdge* prev = nullptr;
281  for (const MSEdge* const e : stage->getEdges()) {
282  const MSLane* const lane = getSidewalk<MSEdge, MSLane>(e);
283  JPS_StageId waitingStage = 0;
284  if (prev != nullptr) {
285  int dir = UNDEFINED_DIRECTION;
286  if (prev->getToJunction() == e->getFromJunction() || prev->getToJunction() == e->getToJunction()) {
287  dir = FORWARD;
288  } else if (prev->getFromJunction() == e->getFromJunction() || prev->getFromJunction() == e->getToJunction()) {
289  dir = BACKWARD;
290  }
291  if (dir != UNDEFINED_DIRECTION) {
292  ConstMSEdgeVector crossingRoute;
293  MSNet::getInstance()->getPedestrianRouter(0).compute(prev, e, 0, 0, stage->getMaxSpeed(person), 0, dir == FORWARD ? prev->getToJunction() : prev->getFromJunction(), crossingRoute, true);
294  for (const MSEdge* const ce : crossingRoute) {
295  if (ce->isCrossing()) {
296  const MSLane* const crossing = getSidewalk<MSEdge, MSLane>(ce);
297  if (myCrossingWaits.count(crossing) > 0) {
298  const MSLane* const prevLane = getSidewalk<MSEdge, MSLane>(prev);
299  const Position& startPos = dir == FORWARD ? prevLane->getShape().back() : prevLane->getShape().front();
300  // choose the waiting set closer to the lane "end"
301  if (crossing->getShape().front().distanceSquaredTo(startPos) < crossing->getShape().back().distanceSquaredTo(startPos)) {
302  waitingStage = myCrossingWaits[crossing].first;
303  } else {
304  waitingStage = myCrossingWaits[crossing].second;
305  }
306  } else {
307  throw ProcessError(TLF("No waiting set for crossing at %.", person->getID(), person->getEdge()->getID(), time2string(SIMSTEP)));
308  }
309  }
310  }
311  }
312  }
313  waypoints.push_back({waitingStage, lane->getShape().positionAtOffset(e->getLength() / 2.), lane->getWidth() / 2.});
314  prev = e;
315  }
316  const JPS_StageId finalWait = std::get<0>(waypoints.back());
317  waypoints.pop_back(); // arrival waypoint comes later
318  if (!waypoints.empty()) {
319  waypoints.erase(waypoints.begin()); // departure edge also does not need a waypoint
320  }
321  const MSLane* const arrivalLane = getSidewalk<MSEdge, MSLane>(stage->getDestination());
322  const Position arrivalPosition = arrivalLane->getShape().positionAtOffset(stage->getArrivalPos());
323  waypoints.push_back({finalWait, arrivalPosition, stage->getDouble("jupedsim.waypoint.radius", myExitTolerance)});
324 
325  JPS_JourneyDescription journeyDesc = JPS_JourneyDescription_Create();
326  if (myPythonScript != nullptr) {
327  (*myPythonScript) << "\njourney = jps.JourneyDescription()\n";
328  }
329  JPS_StageId startingStage = 0;
330  JPS_StageId predecessor = 0;
331  for (const auto& p : waypoints) {
332  if (!addWaypoint(journeyDesc, predecessor, person->getID(), p)) {
333  JPS_JourneyDescription_Free(journeyDesc);
334  return nullptr;
335  }
336  if (startingStage == 0) {
337  startingStage = std::get<0>(waypoints.front()) == 0 ? predecessor : std::get<0>(waypoints.front());
338  }
339  }
340  JPS_ErrorMessage message = nullptr;
341  JPS_JourneyId journeyId = JPS_Simulation_AddJourney(myJPSSimulation, journeyDesc, &message);
342  JPS_JourneyDescription_Free(journeyDesc);
343  if (message != nullptr) {
344  WRITE_WARNINGF(TL("Error while adding a journey for person '%': %"), person->getID(), JPS_ErrorMessage_GetMessage(message));
345  JPS_ErrorMessage_Free(message);
346  return nullptr;
347  }
348 
349  PState* state = nullptr;
350  for (PState* const pstate : myPedestrianStates) { // TODO transform myPedestrianStates into a map for faster lookup
351  if (pstate->getPerson() == person) {
352  state = pstate;
353  break;
354  }
355  }
356  if (state == nullptr) {
357  state = new PState(static_cast<MSPerson*>(person), stage, journeyId, startingStage, waypoints);
358  state->setLanePosition(stage->getDepartPos() + radius + POSITION_EPS);
359  state->setPosition(departurePosition.x(), departurePosition.y());
360  state->setAngle(departureLane->getShape().rotationAtOffset(stage->getDepartPos()));
361  myPedestrianStates.push_back(state);
363  } else {
364  state->reinit(stage, journeyId, startingStage, waypoints);
365  }
366  if (state->isWaitingToEnter()) {
367  const Position p = state->getPosition(*state->getStage(), now);
368  tryPedestrianInsertion(state, p);
369  if (myPythonScript != nullptr) {
370  (*myPythonScript) << "journey_id = simulation.add_journey(journey)\n"
371  "simulation.add_agent(jps.CollisionFreeSpeedModelAgentParameters(journey_id=journey_id,stage_id="
372  << startingStage << ",position=(" << departurePosition.x() << "," << departurePosition.y()
373  << "),radius=" << getRadius(person->getVehicleType()) << ",v0=" << person->getMaxSpeed() << "))\n";
374  }
375  } else {
376  JPS_Simulation_SwitchAgentJourney(myJPSSimulation, state->getAgentId(), journeyId, startingStage, &message);
377  if (message != nullptr) {
378  WRITE_WARNINGF(TL("Error while switching to a new journey for person '%': %"), person->getID(), JPS_ErrorMessage_GetMessage(message));
379  JPS_ErrorMessage_Free(message);
380  return nullptr;
381  }
382  }
383  return state;
384 }
385 
386 
387 void
389  PState* pstate = static_cast<PState*>(state);
390  if (pstate->getLane() != nullptr) {
391  auto& peds = myActiveLanes[pstate->getLane()];
392  peds.erase(std::find(peds.begin(), peds.end(), pstate));
393  }
394  if (pstate->getStage() != nullptr) {
395  pstate->getStage()->setPState(nullptr); // we need to remove the old state reference to avoid double deletion
396  }
397  pstate->setStage(nullptr);
398 }
399 
400 
401 SUMOTime
403  const int nbrIterations = (int)(DELTA_T / myJPSDeltaT);
404  JPS_ErrorMessage message = nullptr;
405  for (int i = 0; i < nbrIterations; ++i) {
406  // Perform one JuPedSim iteration.
407  bool ok = JPS_Simulation_Iterate(myJPSSimulation, &message);
408  if (!ok) {
409  WRITE_ERRORF(TL("Error during iteration %: %"), i, JPS_ErrorMessage_GetMessage(message));
410  }
411  }
412 
413  // Update the state of all pedestrians.
414  // If necessary, this could be done more often in the loop above but the more precise positions are probably never visible.
415  // If it is needed for model correctness (precise stopping / arrivals) we should rather reduce SUMO's step-length.
416  for (auto stateIt = myPedestrianStates.begin(); stateIt != myPedestrianStates.end();) {
417  PState* const state = *stateIt;
418 
419  if (state->isWaitingToEnter()) {
420  // insertion failed at first try so we retry with some noise
421  Position p = state->getPosition(*state->getStage(), time);
422  p.setx(p.x() + RandHelper::rand(-.5, .5)); // we do this separately to avoid evaluation order problems
423  p.sety(p.y() + RandHelper::rand(-.5, .5));
424  tryPedestrianInsertion(state, p);
425  ++stateIt;
426  continue;
427  }
428 
429  MSPerson* person = state->getPerson();
430  MSStageWalking* stage = dynamic_cast<MSStageWalking*>(person->getCurrentStage());
431  if (stage == nullptr) {
432  // It seems we kept the state for another stage but the new stage is not a walk.
433  // So let's remove the state because after the new stage we will be elsewhere and need to be reinserted for JuPedSim anyway.
434  // We cannot check this earlier because when the old stage ends the next stage might not know yet whether it will be a walk.
435  registerArrived(state->getAgentId());
436  stateIt = myPedestrianStates.erase(stateIt);
437  continue;
438  }
439 
440  // Updates the agent position.
441  const JPS_Agent agent = JPS_Simulation_GetAgent(myJPSSimulation, state->getAgentId(), nullptr);
442  const JPS_Point position = JPS_Agent_GetPosition(agent);
443  state->setPosition(position.x, position.y);
444 
445  // Updates the agent direction.
446  const JPS_Point orientation = JPS_Agent_GetOrientation(agent);
447  state->setAngle(atan2(orientation.y, orientation.x));
448 
449  // Find on which edge the pedestrian is, using route's forward-looking edges because of how moveToXY is written.
450  Position newPosition(position.x, position.y);
451  ConstMSEdgeVector route = stage->getEdges();
452  const int routeIndex = (int)(stage->getRouteStep() - stage->getRoute().begin());
453  ConstMSEdgeVector forwardRoute = ConstMSEdgeVector(route.begin() + routeIndex, route.end());
454  double bestDistance = std::numeric_limits<double>::max();
455  MSLane* candidateLane = nullptr;
456  double candidateLaneLongitudinalPosition = 0.0;
457  int routeOffset = 0;
458  const bool found = libsumo::Helper::moveToXYMap_matchingRoutePosition(newPosition, "",
459  forwardRoute, 0, person->getVClass(), true, bestDistance, &candidateLane, candidateLaneLongitudinalPosition, routeOffset);
460 
461  if (found) {
462  if (candidateLane != state->getLane()) {
463  if (state->getLane() != nullptr) {
464  auto& peds = myActiveLanes[state->getLane()];
465  if (!peds.empty()) {
466  peds.erase(std::find(peds.begin(), peds.end(), state));
467  }
468  }
469  myActiveLanes[candidateLane].push_back(state);
470  state->setLane(candidateLane);
471  }
472  state->setLanePosition(candidateLaneLongitudinalPosition);
473  }
474 
475  const MSEdge* const expectedEdge = stage->getEdge();
476  if (found && expectedEdge->isNormal() && candidateLane->getEdge().isNormal() && &candidateLane->getEdge() != expectedEdge) {
477  const bool arrived = stage->moveToNextEdge(person, time, 1, nullptr);
478  UNUSED_PARAMETER(arrived);
479  assert(!arrived); // The person has not arrived yet.
480  stage->activateEntryReminders(person);
481  // Adapt speed to lane's speed limit.
482  const double newMaxSpeed = MIN2(candidateLane->getSpeedLimit(), person->getMaxSpeed());
483  switch (myJPSModel) {
485  JPS_CollisionFreeSpeedModelState modelState = JPS_Agent_GetCollisionFreeSpeedModelState(agent, nullptr);
486  if (newMaxSpeed != JPS_CollisionFreeSpeedModelState_GetV0(modelState)) {
487  JPS_CollisionFreeSpeedModelState_SetV0(modelState, newMaxSpeed);
488  }
489  break;
490  }
492  JPS_CollisionFreeSpeedModelV2State modelState = JPS_Agent_GetCollisionFreeSpeedModelV2State(agent, nullptr);
493  if (newMaxSpeed != JPS_CollisionFreeSpeedModelV2State_GetV0(modelState)) {
494  JPS_CollisionFreeSpeedModelV2State_SetV0(modelState, newMaxSpeed);
495  }
496  break;
497  }
499  JPS_GeneralizedCentrifugalForceModelState modelState = JPS_Agent_GetGeneralizedCentrifugalForceModelState(agent, nullptr);
500  if (newMaxSpeed != JPS_GeneralizedCentrifugalForceModelState_GetV0(modelState)) {
501  JPS_GeneralizedCentrifugalForceModelState_SetV0(modelState, newMaxSpeed);
502  }
503  break;
504  }
505  case JPS_Model::SocialForce: {
506  JPS_SocialForceModelState modelState = JPS_Agent_GetSocialForceModelState(agent, nullptr);
507  if (newMaxSpeed != JPS_SocialForceModelState_GetDesiredSpeed(modelState)) {
508  JPS_SocialForceModelState_SetDesiredSpeed(modelState, newMaxSpeed);
509  }
510  break;
511  }
512  }
513  }
514  const double speed = person->getSpeed();
515  for (int offset = 0; offset < 2; offset++) {
516  const WaypointDesc* const waypoint = state->getNextWaypoint(offset);
517  if (waypoint != nullptr && std::get<0>(*waypoint) != 0) {
518  const JPS_StageId waitingStage = std::get<0>(*waypoint);
519  const MSLane* const crossing = myCrossings[waitingStage];
520  const MSLink* link = crossing->getIncomingLanes().front().viaLink;
521  if (waitingStage == myCrossingWaits[crossing].second && link->getTLLogic() != nullptr) {
522  // we are walking backwards on a traffic light, there is a different link to check
523  link = crossing->getLinkCont().front();
524  }
525  // compare to and maybe adapt for MSPModel_Striping.cpp:1264
526  const double passingClearanceTime = person->getFloatParam("pedestrian.timegap-crossing");
527  const bool open = link->opened(time - DELTA_T, speed, speed, person->getVehicleType().getLength() + passingClearanceTime * speed,
528  person->getImpatience(), speed, 0, 0, nullptr, false, person);
529  const auto proxy = JPS_Simulation_GetWaitingSetProxy(myJPSSimulation, waitingStage, &message);
530  JPS_WaitingSetProxy_SetWaitingSetState(proxy, open ? JPS_WaitingSet_Inactive : JPS_WaitingSet_Active);
531  }
532  }
533  // In the worst case during one SUMO step the person touches the waypoint radius and walks immediately into a different direction,
534  // but at some simstep it should have a maximum distance of v * delta_t / 2 to the waypoint circle.
535  const double slack = person->getMaxSpeed() * TS / 2. + POSITION_EPS;
536  if (newPosition.distanceTo2D(std::get<1>(*state->getNextWaypoint())) < std::get<2>(*state->getNextWaypoint()) + slack) {
537  // If near the last waypoint, remove the agent.
538  if (state->advanceNextWaypoint()) {
539  // TODO this only works if the final stage is actually a walk
540  const bool finalStage = person->getNumRemainingStages() == 1;
541  const JPS_AgentId agentID = state->getAgentId();
542  while (!stage->moveToNextEdge(person, time, 1, nullptr));
543  if (finalStage) {
544  registerArrived(agentID);
545  stateIt = myPedestrianStates.erase(stateIt);
546  continue;
547  }
548  }
549  }
550  ++stateIt;
551  }
552 
553  // Remove pedestrians that are in a predefined area, at a predefined rate.
554  for (AreaData& area : myAreas) {
555  const std::vector<JPS_Point>& areaBoundary = area.areaBoundary;
556  JPS_AgentIdIterator agentsInArea = JPS_Simulation_AgentsInPolygon(myJPSSimulation, areaBoundary.data(), areaBoundary.size());
557  if (area.areaType == "vanishing_area") {
558  const SUMOTime period = area.params.count("period") > 0 ? string2time(area.params.at("period")) : 1000;
559  const int nbrPeriodsCoveringTimestep = (int)ceil(TS / STEPS2TIME(period));
560  if (time - area.lastRemovalTime >= nbrPeriodsCoveringTimestep * period) {
561  for (int k = 0; k < nbrPeriodsCoveringTimestep; k++) {
562  const JPS_AgentId agentID = JPS_AgentIdIterator_Next(agentsInArea);
563  if (agentID != 0) {
564  auto lambda = [agentID](const PState * const p) {
565  return p->getAgentId() == agentID;
566  };
567  std::vector<PState*>::const_iterator iterator = std::find_if(myPedestrianStates.begin(), myPedestrianStates.end(), lambda);
568  if (iterator != myPedestrianStates.end()) {
569  const PState* const state = *iterator;
570  MSPerson* const person = state->getPerson();
571  // Code below only works if the removal happens at the last stage.
572  const bool finalStage = person->getNumRemainingStages() == 1;
573  if (finalStage) {
574  WRITE_MESSAGEF(TL("Person '%' in vanishing area '%' was removed from the simulation."), person->getID(), area.id);
575  while (!state->getStage()->moveToNextEdge(person, time, 1, nullptr));
576  registerArrived(agentID);
577  myPedestrianStates.erase(iterator);
578  area.lastRemovalTime = time;
579  }
580  }
581  }
582  }
583  }
584  } else { // areaType == "influencer"
585  for (JPS_AgentId agentID = JPS_AgentIdIterator_Next(agentsInArea); agentID != 0; agentID = JPS_AgentIdIterator_Next(agentsInArea)) {
586  if (area.params.count("speed") > 0) {
587  const JPS_Agent agent = JPS_Simulation_GetAgent(myJPSSimulation, agentID, nullptr);
588  const double newMaxSpeed = StringUtils::toDouble(area.params.at("speed"));
589  switch (myJPSModel) {
591  JPS_CollisionFreeSpeedModelState modelState = JPS_Agent_GetCollisionFreeSpeedModelState(agent, nullptr);
592  if (newMaxSpeed != JPS_CollisionFreeSpeedModelState_GetV0(modelState)) {
593  JPS_CollisionFreeSpeedModelState_SetV0(modelState, newMaxSpeed);
594  }
595  break;
596  }
598  JPS_CollisionFreeSpeedModelV2State modelState = JPS_Agent_GetCollisionFreeSpeedModelV2State(agent, nullptr);
599  if (newMaxSpeed != JPS_CollisionFreeSpeedModelV2State_GetV0(modelState)) {
600  JPS_CollisionFreeSpeedModelV2State_SetV0(modelState, newMaxSpeed);
601  }
602  break;
603  }
605  JPS_GeneralizedCentrifugalForceModelState modelState = JPS_Agent_GetGeneralizedCentrifugalForceModelState(agent, nullptr);
606  if (newMaxSpeed != JPS_GeneralizedCentrifugalForceModelState_GetV0(modelState)) {
607  JPS_GeneralizedCentrifugalForceModelState_SetV0(modelState, newMaxSpeed);
608  }
609  break;
610  }
611  case JPS_Model::SocialForce: {
612  JPS_SocialForceModelState modelState = JPS_Agent_GetSocialForceModelState(agent, nullptr);
613  if (newMaxSpeed != JPS_SocialForceModelState_GetDesiredSpeed(modelState)) {
614  JPS_SocialForceModelState_SetDesiredSpeed(modelState, newMaxSpeed);
615  }
616  break;
617  }
618  }
619  }
620  }
621  }
622  JPS_AgentIdIterator_Free(agentsInArea);
623  }
624 
625  // Add dynamically additional geometry from train carriages that are stopped.
626  const auto& stoppingPlaces = myNetwork->getStoppingPlaces(SumoXMLTag::SUMO_TAG_BUS_STOP);
627  std::vector<SUMOTrafficObject::NumericalID> allStoppedTrainIDs;
628  std::vector<const MSVehicle*> allStoppedTrains;
629  for (const auto& stop : stoppingPlaces) {
630  std::vector<const SUMOVehicle*> stoppedTrains = stop.second->getStoppedVehicles();
631  for (const SUMOVehicle* train : stoppedTrains) {
632  allStoppedTrainIDs.push_back(train->getNumericalID());
633  allStoppedTrains.push_back(dynamic_cast<const MSVehicle*>(train));
634  }
635  }
636  if (allStoppedTrainIDs != myAllStoppedTrainIDs) {
638  if (!allStoppedTrainIDs.empty()) {
639  std::vector<GEOSGeometry*> carriagePolygons;
640  std::vector<GEOSGeometry*> rampPolygons;
641  for (const MSVehicle* train : allStoppedTrains) {
642  if (train->getLeavingPersonNumber() > 0) {
643  MSTrainHelper trainHelper = MSTrainHelper(train);
644  trainHelper.computeDoorPositions();
645  const std::vector<MSTrainHelper::Carriage*>& carriages = trainHelper.getCarriages();
646  for (const MSTrainHelper::Carriage* carriage : carriages) {
647  Position dir = carriage->front - carriage->back;
648  if (dir.length2D() == 0.0) {
649  continue;
650  }
651  dir.norm2D();
652  Position perp = Position(-dir.y(), dir.x());
653  // Create carriages geometry.
654  double p = trainHelper.getHalfWidth();
655  PositionVector carriageShape;
656  carriageShape.push_back(carriage->front + perp * p);
657  carriageShape.push_back(carriage->back + perp * p);
658  carriageShape.push_back(carriage->back - perp * p);
659  carriageShape.push_back(carriage->front - perp * p);
660  carriagePolygons.push_back(createGeometryFromShape(carriageShape));
661  // Create ramps geometry.
663  const double d = 0.5 * MSTrainHelper::CARRIAGE_DOOR_WIDTH;
664  for (const Position& door : carriage->doorPositions) {
665  PositionVector rampShape;
666  rampShape.push_back(door - perp * p + dir * d);
667  rampShape.push_back(door - perp * p - dir * d);
668  rampShape.push_back(door + perp * p - dir * d);
669  rampShape.push_back(door + perp * p + dir * d);
670  rampPolygons.push_back(createGeometryFromShape(rampShape));
671  }
672  }
673  }
674  }
675  if (!carriagePolygons.empty()) {
676  GEOSGeometry* carriagesCollection = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, carriagePolygons.data(), (unsigned int)carriagePolygons.size());
677  GEOSGeometry* rampsCollection = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, rampPolygons.data(), (unsigned int)rampPolygons.size());
678  GEOSGeometry* carriagesAndRampsUnion = GEOSUnion(carriagesCollection, rampsCollection);
679  if (carriagesAndRampsUnion == nullptr) {
680  WRITE_WARNING(TL("Error while generating geometry for carriages."));
681  } else {
682  GEOSGeometry* pedestrianNetworkWithTrainsAndRamps = GEOSUnion(carriagesAndRampsUnion, myGEOSPedestrianNetworkLargestComponent);
683 #ifdef DEBUG_GEOMETRY_GENERATION
684  //dumpGeometry(pedestrianNetworkWithTrainsAndRamps, "pedestrianNetworkWithTrainsAndRamps.wkt");
685 #endif
686  int nbrComponents = 0;
687  double maxArea = 0.0;
688  double totalArea = 0.0;
689  const GEOSGeometry* pedestrianNetworkWithTrainsAndRampsLargestComponent = getLargestComponent(pedestrianNetworkWithTrainsAndRamps, nbrComponents, maxArea, totalArea);
690  if (nbrComponents > 1) {
691  WRITE_WARNINGF(TL("While generating geometry % connected components were detected, %% of total pedestrian area is covered by the largest."),
692  nbrComponents, maxArea / totalArea * 100.0, "%");
693  }
694 #ifdef DEBUG_GEOMETRY_GENERATION
695  //dumpGeometry(pedestrianNetworkWithTrainsAndRampsLargestComponent, "pedestrianNetworkWithTrainsAndRamps.wkt");
696 #endif
697  myJPSGeometryWithTrainsAndRamps = buildJPSGeometryFromGEOSGeometry(pedestrianNetworkWithTrainsAndRampsLargestComponent);
698  JPS_Simulation_SwitchGeometry(myJPSSimulation, myJPSGeometryWithTrainsAndRamps, nullptr, nullptr);
701  GEOSGeom_destroy(pedestrianNetworkWithTrainsAndRamps);
702  }
703  GEOSGeom_destroy(rampsCollection);
704  GEOSGeom_destroy(carriagesCollection);
705  }
706  } else {
707  JPS_Simulation_SwitchGeometry(myJPSSimulation, myJPSGeometry, nullptr, nullptr);
709  }
710  myAllStoppedTrainIDs = allStoppedTrainIDs;
711  }
712 
713  JPS_ErrorMessage_Free(message);
714 
715  return DELTA_T;
716 }
717 
718 
719 void MSPModel_JuPedSim::registerArrived(const JPS_AgentId agentID) {
721  JPS_Simulation_MarkAgentForRemoval(myJPSSimulation, agentID, nullptr);
722 }
723 
724 
726  myPedestrianStates.clear();
728 }
729 
730 
731 GEOSGeometry*
732 MSPModel_JuPedSim::createGeometryFromCenterLine(PositionVector centerLine, double width, int capStyle) {
733  GEOSCoordSequence* coordinateSequence = convertToGEOSPoints(centerLine);
734  GEOSGeometry* lineString = GEOSGeom_createLineString(coordinateSequence);
735  GEOSGeometry* dilatedLineString = GEOSBufferWithStyle(lineString, width, GEOS_QUADRANT_SEGMENTS, capStyle, GEOSBUF_JOIN_ROUND, GEOS_MITRE_LIMIT);
736  GEOSGeom_destroy(lineString);
737  return dilatedLineString;
738 }
739 
740 
741 GEOSGeometry*
742 MSPModel_JuPedSim::createGeometryFromShape(PositionVector shape, std::string junctionID, std::string shapeID, bool isInternalShape) {
743  // Corner case.
744  if (shape.size() == 1) {
745  WRITE_WARNINGF(TL("Polygon '%' at junction '%' will be skipped as it is just a point."), shapeID, junctionID);
746  return nullptr;
747  }
748  // Make sure the shape is closed.
749  if (shape.back() != shape.front()) {
750  shape.push_back(shape.front());
751  }
752  // Replace consecutive points that are equal with just one.
753  PositionVector cleanShape;
754  cleanShape.push_back(shape[0]);
755  PositionVector duplicates;
756  for (int i = 1; i < (int)shape.size(); i++) {
757  if (shape[i] != shape[i - 1]) {
758  cleanShape.push_back(shape[i]);
759  } else {
760  duplicates.push_back(shape[i]);
761  }
762  }
763  if (cleanShape.size() < shape.size()) {
764  WRITE_WARNINGF(TL("Polygon '%' at junction '%' had some equal consecutive points removed: %"), shapeID, junctionID, toString(duplicates, 9));
765  }
766  GEOSCoordSequence* coordinateSequence = convertToGEOSPoints(cleanShape);
767  GEOSGeometry* linearRing = GEOSGeom_createLinearRing(coordinateSequence);
768  GEOSGeometry* polygon = GEOSGeom_createPolygon(linearRing, nullptr, 0);
769 
770  if (!GEOSisValid(polygon)) {
771  if (cleanShape.size() == 3) {
772  if (isInternalShape) {
773  WRITE_WARNINGF(TL("Polygon '%' at junction '%' has been dilated as it is just a segment."), shapeID, junctionID);
774  } else {
775  WRITE_WARNINGF(TL("Polygon '%' has been dilated as it is just a segment."), shapeID);
776  }
777  GEOSGeometry* lineString = GEOSGeom_createLineString(GEOSCoordSeq_clone(coordinateSequence));
778  GEOSGeom_destroy(polygon);
779  polygon = GEOSBufferWithStyle(lineString, GEOS_BUFFERED_SEGMENT_WIDTH, GEOS_QUADRANT_SEGMENTS, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_BEVEL, GEOS_MITRE_LIMIT);
780  GEOSGeom_destroy(lineString);
781  } else {
782  if (isInternalShape) {
783  WRITE_WARNINGF(TL("Polygon '%' at junction '%' has been replaced by its convex hull as it is not simple."), shapeID, junctionID);
784  GEOSGeometry* hull = GEOSConvexHull(polygon);
785  GEOSGeom_destroy(polygon);
786  polygon = GEOSBufferWithStyle(hull, POSITION_EPS, GEOS_QUADRANT_SEGMENTS, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_ROUND, GEOS_MITRE_LIMIT);;
787  } else {
788  WRITE_WARNINGF(TL("Polygon '%' at junction '%' will be skipped as it is not simple."), shapeID, junctionID);
789  polygon = nullptr;
790  }
791  }
792 
793  return polygon;
794  }
795 
796  // Some junctions need to be buffered a little so that edges are properly connected.
797  if (isInternalShape) {
798  polygon = GEOSBufferWithStyle(polygon, POSITION_EPS, GEOS_QUADRANT_SEGMENTS, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_ROUND, GEOS_MITRE_LIMIT);
799  }
800 
801  return polygon;
802 }
803 
804 
805 GEOSGeometry*
807  std::vector<GEOSGeometry*> walkableAreas;
808  for (const MSEdge* const edge : network->getEdgeControl().getEdges()) {
809  const MSLane* const lane = getSidewalk<MSEdge, MSLane>(edge);
810  if (lane != nullptr) {
811  GEOSGeometry* dilatedLane = nullptr;
812  if (edge->isWalkingArea()) {
813  dilatedLane = createGeometryFromShape(lane->getShape(), edge->getFromJunction()->getID(), edge->getID(), true);
814  } else if (edge->isCrossing()) {
815  const PositionVector* outlineShape = lane->getOutlineShape();
816  if (outlineShape != nullptr) {
817  // this is mainly to ensure that we have at least the "normal" crossing, see #15037
818  dilatedLane = createGeometryFromCenterLine(lane->getShape(), lane->getWidth() / 2.0, GEOSBUF_CAP_FLAT);
819  if (dilatedLane != nullptr) {
820  walkableAreas.push_back(dilatedLane);
821  }
822  dilatedLane = createGeometryFromShape(*outlineShape, edge->getFromJunction()->getID(), edge->getID(), true);
823  } else {
824  dilatedLane = createGeometryFromCenterLine(lane->getShape(), lane->getWidth() / 2.0, GEOSBUF_CAP_ROUND);
825  }
826  myCrossingWaits[lane] = {0, 0};
827  } else { // regular sidewalk
828  dilatedLane = createGeometryFromCenterLine(lane->getShape(), lane->getWidth() / 2.0 + POSITION_EPS, GEOSBUF_CAP_FLAT);
829  }
830  if (dilatedLane != nullptr) {
831  walkableAreas.push_back(dilatedLane);
832  }
833  }
834  }
835  for (const auto& junctionWithID : network->getJunctionControl()) {
836  const MSJunction* const junction = junctionWithID.second;
837  int pedEdgeCount = 0;
838  bool hasWalkingArea = false;
839  for (const ConstMSEdgeVector& edges : {
840  junction->getIncoming(), junction->getOutgoing()
841  }) {
842  for (const MSEdge* const edge : edges) {
843  if (edge->isWalkingArea()) {
844  hasWalkingArea = true;
845  break;
846  }
847  if (getSidewalk<MSEdge, MSLane>(edge) != nullptr) {
848  pedEdgeCount++;
849  }
850  }
851  if (hasWalkingArea) {
852  break;
853  }
854  }
855  if (!hasWalkingArea && pedEdgeCount > 1) {
856  // there is something to connect but no walking area, let's assume peds are allowed everywhere
857  GEOSGeometry* walkingAreaGeom = createGeometryFromShape(junction->getShape(), junction->getID(), junction->getID(), true);
858  if (walkingAreaGeom != nullptr) {
859  walkableAreas.push_back(walkingAreaGeom);
860  }
861  }
862  }
863 
864  // Retrieve additional walkable areas and obstacles (walkable areas and obstacles in the sense of JuPedSim).
865  std::vector<GEOSGeometry*> additionalObstacles;
866  for (const auto& polygonWithID : myNetwork->getShapeContainer().getPolygons()) {
867  if (polygonWithID.second->getShapeType() == "jupedsim.walkable_area" || polygonWithID.second->getShapeType() == "taz") {
868  GEOSGeometry* walkableArea = createGeometryFromShape(polygonWithID.second->getShape(), std::string(""), polygonWithID.first);
869  if (walkableArea != nullptr) {
870  walkableAreas.push_back(walkableArea);
872  }
873  } else if (polygonWithID.second->getShapeType() == "jupedsim.obstacle") {
874  GEOSGeometry* additionalObstacle = createGeometryFromShape(polygonWithID.second->getShape(), std::string(""), polygonWithID.first);
875  if (additionalObstacle != nullptr) {
876  additionalObstacles.push_back(additionalObstacle);
877  }
878  }
879  }
880 
881  // Take the union of all walkable areas.
882  GEOSGeometry* disjointWalkableAreas = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, walkableAreas.data(), (unsigned int)walkableAreas.size());
883 #ifdef DEBUG_GEOMETRY_GENERATION
884  // dumpGeometry(disjointWalkableAreas, "disjointWalkableAreas.wkt");
885 #endif
886  GEOSGeometry* initialWalkableAreas = GEOSUnaryUnion(disjointWalkableAreas);
887 #ifdef DEBUG_GEOMETRY_GENERATION
888  dumpGeometry(initialWalkableAreas, "initialWalkableAreas.wkt");
889 #endif
890  GEOSGeom_destroy(disjointWalkableAreas);
891 
892  // At last, remove additional obstacles from the merged walkable areas.
893  GEOSGeometry* disjointAdditionalObstacles = GEOSGeom_createCollection(GEOS_MULTIPOLYGON, additionalObstacles.data(), (unsigned int)additionalObstacles.size());
894 #ifdef DEBUG_GEOMETRY_GENERATION
895  // dumpGeometry(disjointAdditionalObstacles, "disjointAdditionalObstacles.wkt");
896 #endif
897  GEOSGeometry* additionalObstaclesUnion = GEOSUnaryUnion(disjointAdditionalObstacles); // Obstacles may overlap, e.g. if they were loaded from separate files.
898 #ifdef DEBUG_GEOMETRY_GENERATION
899  dumpGeometry(additionalObstaclesUnion, "additionalObstaclesUnion.wkt");
900 #endif
901  GEOSGeometry* finalWalkableAreas = GEOSDifference(initialWalkableAreas, additionalObstaclesUnion);
902 #ifdef DEBUG_GEOMETRY_GENERATION
903  dumpGeometry(finalWalkableAreas, "finalWalkableAreas.wkt");
904 #endif
905  GEOSGeom_destroy(initialWalkableAreas);
906  GEOSGeom_destroy(additionalObstaclesUnion);
907  GEOSGeom_destroy(disjointAdditionalObstacles);
908 
909  if (!GEOSisSimple(finalWalkableAreas)) {
910  throw ProcessError(TL("Union of walkable areas minus union of obstacles is not a simple polygon."));
911  }
912 
913  return finalWalkableAreas;
914 }
915 
916 
917 GEOSCoordSequence*
919  GEOSCoordSequence* coordinateSequence = GEOSCoordSeq_create((unsigned int)shape.size(), 2);
920  for (int i = 0; i < (int)shape.size(); i++) {
921  GEOSCoordSeq_setXY(coordinateSequence, i, shape[i].x(), shape[i].y());
922  }
923  return coordinateSequence;
924 }
925 
926 
928 MSPModel_JuPedSim::convertToSUMOPoints(const GEOSGeometry* geometry) {
929  PositionVector coordinateVector;
930  const GEOSCoordSequence* coordinateSequence = GEOSGeom_getCoordSeq(geometry);
931  unsigned int coordinateSequenceSize;
932  GEOSCoordSeq_getSize(coordinateSequence, &coordinateSequenceSize);
933  double x;
934  double y;
935  for (unsigned int i = 0; i < coordinateSequenceSize; i++) {
936  GEOSCoordSeq_getX(coordinateSequence, i, &x);
937  GEOSCoordSeq_getY(coordinateSequence, i, &y);
938  coordinateVector.push_back(Position(x, y));
939  }
940  return coordinateVector;
941 }
942 
943 
944 std::vector<JPS_Point>
945 MSPModel_JuPedSim::convertToJPSPoints(const GEOSGeometry* geometry) {
946  std::vector<JPS_Point> pointVector;
947  const GEOSCoordSequence* coordinateSequence = GEOSGeom_getCoordSeq(geometry);
948  unsigned int coordinateSequenceSize;
949  GEOSCoordSeq_getSize(coordinateSequence, &coordinateSequenceSize);
950  double x;
951  double y;
952  // Remove the last point so that CGAL doesn't complain about the simplicity of the polygon downstream.
953  for (unsigned int i = 0; i < coordinateSequenceSize - 1; i++) {
954  GEOSCoordSeq_getX(coordinateSequence, i, &x);
955  GEOSCoordSeq_getY(coordinateSequence, i, &y);
956  pointVector.push_back({x, y});
957  }
958  return pointVector;
959 }
960 
961 
962 double
963 MSPModel_JuPedSim::getLinearRingArea(const GEOSGeometry* linearRing) {
964  double area;
965  GEOSGeometry* linearRingAsPolygon = GEOSGeom_createPolygon(GEOSGeom_clone(linearRing), nullptr, 0);
966  GEOSArea(linearRingAsPolygon, &area);
967  GEOSGeom_destroy(linearRingAsPolygon);
968  return area;
969 }
970 
971 
972 void
973 MSPModel_JuPedSim::removePolygonFromDrawing(const std::string& polygonId) {
974  myShapeContainer.removePolygon(polygonId);
975 }
976 
977 
978 void
979 MSPModel_JuPedSim::preparePolygonForDrawing(const GEOSGeometry* polygon, const std::string& polygonId, const RGBColor& color) {
980  const GEOSGeometry* exterior = GEOSGetExteriorRing(polygon);
981  bool added = myShapeContainer.addPolygon(polygonId, std::string("jupedsim.pedestrian_network"), color, 10.0, 0.0,
982  std::string(), false, convertToSUMOPoints(exterior), false, true, 1.0);
983  if (added) {
984  std::vector<PositionVector> holes;
985  int nbrInteriorRings = GEOSGetNumInteriorRings(polygon);
986  if (nbrInteriorRings != -1) {
987  for (int k = 0; k < nbrInteriorRings; k++) {
988  const GEOSGeometry* linearRing = GEOSGetInteriorRingN(polygon, k);
989  double area = getLinearRingArea(linearRing);
990  if (area > GEOS_MIN_AREA) {
991  holes.push_back(convertToSUMOPoints(linearRing));
992  }
993  }
994  myShapeContainer.getPolygons().get(polygonId)->setHoles(holes);
995  }
996  }
997 }
998 
999 
1000 const GEOSGeometry*
1001 MSPModel_JuPedSim::getLargestComponent(const GEOSGeometry* polygon, int& nbrComponents, double& maxArea, double& totalArea) {
1002  nbrComponents = GEOSGetNumGeometries(polygon);
1003  const GEOSGeometry* largestComponent = nullptr;
1004  maxArea = 0.0;
1005  totalArea = 0.0;
1006  for (int i = 0; i < nbrComponents; i++) {
1007  const GEOSGeometry* componentPolygon = GEOSGetGeometryN(polygon, i);
1008  double area;
1009  GEOSArea(componentPolygon, &area);
1010  totalArea += area;
1011  if (area > maxArea) {
1012  maxArea = area;
1013  largestComponent = componentPolygon;
1014  }
1015  }
1016  return largestComponent;
1017 }
1018 
1019 
1020 JPS_Geometry
1022  JPS_GeometryBuilder geometryBuilder = JPS_GeometryBuilder_Create();
1023 
1024  // Handle the exterior polygon.
1025  const GEOSGeometry* exterior = GEOSGetExteriorRing(polygon);
1026  std::vector<JPS_Point> exteriorCoordinates = convertToJPSPoints(exterior);
1027  JPS_GeometryBuilder_AddAccessibleArea(geometryBuilder, exteriorCoordinates.data(), exteriorCoordinates.size());
1028 
1029  // Handle the interior polygons (holes).
1030  int nbrInteriorRings = GEOSGetNumInteriorRings(polygon);
1031  if (nbrInteriorRings != -1) {
1032  for (int k = 0; k < nbrInteriorRings; k++) {
1033  const GEOSGeometry* linearRing = GEOSGetInteriorRingN(polygon, k);
1034  double area = getLinearRingArea(linearRing);
1035  if (area > GEOS_MIN_AREA) {
1036  std::vector<JPS_Point> holeCoordinates = convertToJPSPoints(linearRing);
1037  JPS_GeometryBuilder_ExcludeFromAccessibleArea(geometryBuilder, holeCoordinates.data(), holeCoordinates.size());
1038  }
1039  }
1040  }
1041 
1042  JPS_ErrorMessage message = nullptr;
1043  JPS_Geometry geometry = JPS_GeometryBuilder_Build(geometryBuilder, &message);
1044  if (geometry == nullptr) {
1045  const std::string error = TLF("Error while generating geometry: %", JPS_ErrorMessage_GetMessage(message));
1046  JPS_ErrorMessage_Free(message);
1047  throw ProcessError(error);
1048  }
1049  JPS_GeometryBuilder_Free(geometryBuilder);
1050  return geometry;
1051 }
1052 
1053 
1054 void
1055 MSPModel_JuPedSim::dumpGeometry(const GEOSGeometry* polygon, const std::string& filename, bool useGeoCoordinates) {
1056  GEOSGeometry* polygonGeoCoordinates = nullptr;
1057  if (useGeoCoordinates) {
1058  const GEOSGeometry* exterior = GEOSGetExteriorRing(polygon);
1059  PositionVector exteriorPoints = convertToSUMOPoints(exterior);
1060  for (Position& position : exteriorPoints) {
1062  }
1063  const int nbrInteriorRings = GEOSGetNumInteriorRings(polygon);
1064  std::vector<GEOSGeometry*> holeRings;
1065  if (nbrInteriorRings != -1) {
1066  for (int k = 0; k < nbrInteriorRings; k++) {
1067  const GEOSGeometry* linearRing = GEOSGetInteriorRingN(polygon, k);
1068  PositionVector holePoints = convertToSUMOPoints(linearRing);
1069  for (Position& position : holePoints) {
1071  }
1072  GEOSCoordSequence* holeRingCoords = convertToGEOSPoints(holePoints);
1073  GEOSGeometry* holeRing = GEOSGeom_createLinearRing(holeRingCoords);
1074  holeRings.push_back(holeRing);
1075  }
1076  }
1077  GEOSCoordSequence* exteriorRingCoords = convertToGEOSPoints(exteriorPoints);
1078  GEOSGeometry* exteriorRing = GEOSGeom_createLinearRing(exteriorRingCoords);
1079  polygonGeoCoordinates = GEOSGeom_createPolygon(exteriorRing, holeRings.data(), nbrInteriorRings);
1080  }
1081  std::ofstream dumpFile;
1082  dumpFile.open(filename);
1083  GEOSWKTWriter* writer = GEOSWKTWriter_create();
1084  char* wkt = GEOSWKTWriter_write(writer, polygonGeoCoordinates == nullptr ? polygon : polygonGeoCoordinates);
1085  dumpFile << wkt << std::endl;
1086  dumpFile.close();
1087  GEOSFree(wkt);
1088  GEOSWKTWriter_destroy(writer);
1089  GEOSGeom_destroy(polygonGeoCoordinates);
1090 }
1091 
1092 
1093 double
1095  return 0.5 * MAX2(vehType.getLength(), vehType.getWidth());
1096 }
1097 
1098 
1099 JPS_StageId
1100 MSPModel_JuPedSim::addWaitingSet(const MSLane* const crossing, const bool entry) {
1101  JPS_ErrorMessage message = nullptr;
1102  JPS_StageId waitingStage = 0;
1103  PositionVector shape = crossing->getShape();
1104  const double radius = getRadius(*MSNet::getInstance()->getVehicleControl().getVType(DEFAULT_PEDTYPE_ID, nullptr, true));
1105  shape.extrapolate2D((shape.length() + radius) / shape.length());
1106  const double offset = 2 * radius + POSITION_EPS;
1107  const double lonOffset = entry ? NUMERICAL_EPS : shape.length() - NUMERICAL_EPS;
1108  PositionVector pv;
1109  pv.push_back(shape.positionAtOffset(lonOffset));
1110  for (double latOff = offset; latOff < crossing->getWidth() / 2. - offset; latOff += offset) {
1111  PositionVector moved(shape);
1112  moved.move2side(latOff);
1113  pv.push_back(moved.positionAtOffset(lonOffset));
1114  moved.move2side(-2. * latOff);
1115  const Position wPosOff2 = moved.positionAtOffset(lonOffset);
1116  pv.push_back(moved.positionAtOffset(lonOffset));
1117  }
1118  Position center = Position::INVALID;
1119  if (entry && crossing->getIncomingLanes().size() == 1 && crossing->getIncomingLanes().front().lane->isWalkingArea()) {
1120  pv.push_back(crossing->getIncomingLanes().front().lane->getShape().getCentroid());
1121  }
1122  if (!entry && crossing->getLinkCont().size() == 1 && crossing->getLinkCont().front()->getLane()->isWalkingArea()) {
1123  pv.push_back(crossing->getLinkCont().front()->getLane()->getShape().getCentroid());
1124  }
1125  std::vector<JPS_Point> points;
1126  for (const Position& p : pv) {
1127  GEOSGeometry* point = GEOSGeom_createPointFromXY(p.x(), p.y());
1128  if (GEOSContains(myGEOSPedestrianNetworkLargestComponent, point)) {
1129  points.push_back({p.x(), p.y()});
1130  }
1131  GEOSGeom_destroy(point);
1132  }
1133  waitingStage = JPS_Simulation_AddStageWaitingSet(myJPSSimulation, points.data(), points.size(), &message);
1134  if (message != nullptr) {
1135  const std::string error = TLF("Error while adding waiting set for % on '%': %",
1136  entry ? "entry" : "exit", crossing->getID(), JPS_ErrorMessage_GetMessage(message));
1137  JPS_ErrorMessage_Free(message);
1138  throw ProcessError(error);
1139  }
1140  const auto proxy = JPS_Simulation_GetWaitingSetProxy(myJPSSimulation, waitingStage, &message);
1141  JPS_WaitingSetProxy_SetWaitingSetState(proxy, JPS_WaitingSet_Inactive);
1142  myCrossings[waitingStage] = crossing;
1143  if (myPythonScript != nullptr) {
1144  (*myPythonScript) << "ws = simulation.add_waiting_set_stage([";
1145  for (const JPS_Point& p : points) {
1146  (*myPythonScript) << "(" << p.x << "," << p.y << "),";
1147  }
1148  (*myPythonScript) << "])\nsimulation.get_stage(ws).state = jps.stages.WaitingSetState.INACTIVE\n";
1149  }
1150  return waitingStage;
1151 }
1152 
1153 
1154 void
1156  initGEOS(nullptr, nullptr);
1157  PROGRESS_BEGIN_MESSAGE("Generating initial JuPedSim geometry for pedestrian network");
1159  int nbrComponents = 0;
1160  double maxArea = 0.0;
1161  double totalArea = 0.0;
1163  if (nbrComponents > 1) {
1164  WRITE_WARNINGF(TL("While generating geometry % connected components were detected, %% of total pedestrian area is covered by the largest."),
1165  nbrComponents, maxArea / totalArea * 100.0, "%");
1166  }
1167 #ifdef DEBUG_GEOMETRY_GENERATION
1168  dumpGeometry(myGEOSPedestrianNetworkLargestComponent, "pedestrianNetwork.wkt");
1169 #endif
1170  const std::string filename = oc.getString("pedestrian.jupedsim.wkt");
1171  if (!filename.empty()) {
1172  dumpGeometry(myGEOSPedestrianNetworkLargestComponent, filename, oc.getBool("pedestrian.jupedsim.wkt.geo"));
1173  }
1174  // For the moment, only one connected component is supported.
1179  JPS_ErrorMessage message = nullptr;
1180 
1181  double strengthGeometryRepulsion = oc.getFloat("pedestrian.jupedsim.strength-geometry-repulsion");
1182  double rangeGeometryRepulsion = oc.getFloat("pedestrian.jupedsim.range-geometry-repulsion");
1183  if (myJPSDeltaT == 20) {
1184  if (oc.isDefault("pedestrian.jupedsim.strength-geometry-repulsion") && oc.isDefault("pedestrian.jupedsim.range-geometry-repulsion")) {
1185  WRITE_MESSAGE(TL("Adapting geometry repulsion default values for jupedsim timestep of 0.02."));
1186  strengthGeometryRepulsion = 35.;
1187  rangeGeometryRepulsion = 0.019;
1188  }
1189  }
1190 
1191  switch (myJPSModel) {
1193  JPS_CollisionFreeSpeedModelBuilder modelBuilder = JPS_CollisionFreeSpeedModelBuilder_Create(
1194  oc.getFloat("pedestrian.jupedsim.strength-neighbor-repulsion"),
1195  oc.getFloat("pedestrian.jupedsim.range-neighbor-repulsion"),
1196  strengthGeometryRepulsion,
1197  rangeGeometryRepulsion);
1198  myJPSOperationalModel = JPS_CollisionFreeSpeedModelBuilder_Build(modelBuilder, &message);
1199  JPS_CollisionFreeSpeedModelBuilder_Free(modelBuilder);
1200  break;
1201  }
1203  JPS_CollisionFreeSpeedModelV2Builder modelBuilder = JPS_CollisionFreeSpeedModelV2Builder_Create();
1204  myJPSOperationalModel = JPS_CollisionFreeSpeedModelV2Builder_Build(modelBuilder, &message);
1205  JPS_CollisionFreeSpeedModelV2Builder_Free(modelBuilder);
1206  break;
1207  }
1209  JPS_GeneralizedCentrifugalForceModelBuilder modelBuilder = JPS_GeneralizedCentrifugalForceModelBuilder_Create(
1210  oc.getFloat("pedestrian.jupedsim.strength-neighbor-repulsion"),
1211  strengthGeometryRepulsion,
1212  2.0,
1213  2.0,
1214  0.1,
1215  0.1,
1216  9.0,
1217  3.0);
1218  myJPSOperationalModel = JPS_GeneralizedCentrifugalForceModelBuilder_Build(modelBuilder, &message);
1219  JPS_GeneralizedCentrifugalForceModelBuilder_Free(modelBuilder);
1220  break;
1221  }
1222  case JPS_Model::SocialForce: {
1223  JPS_SocialForceModelBuilder modelBuilder = JPS_SocialForceModelBuilder_Create(120000.0, 240000.0);
1224  myJPSOperationalModel = JPS_SocialForceModelBuilder_Build(modelBuilder, &message);
1225  JPS_SocialForceModelBuilder_Free(modelBuilder);
1226  break;
1227  }
1228  }
1229 
1230  if (myJPSOperationalModel == nullptr) {
1231  const std::string error = TLF("Error creating the pedestrian model: %", JPS_ErrorMessage_GetMessage(message));
1232  JPS_ErrorMessage_Free(message);
1233  throw ProcessError(error);
1234  }
1235  myJPSSimulation = JPS_Simulation_Create(myJPSOperationalModel, myJPSGeometry, STEPS2TIME(myJPSDeltaT), &message);
1236  if (myJPSSimulation == nullptr) {
1237  const std::string error = TLF("Error creating the simulation: %", JPS_ErrorMessage_GetMessage(message));
1238  JPS_ErrorMessage_Free(message);
1239  throw ProcessError(error);
1240  }
1241  // Polygons that define vanishing areas aren't part of the regular JuPedSim geometry.
1242  for (const auto& polygonWithID : myNetwork->getShapeContainer().getPolygons()) {
1243  const SUMOPolygon* const poly = polygonWithID.second;
1244  if (poly->getShapeType() == "jupedsim.vanishing_area" || poly->getShapeType() == "jupedsim.influencer") {
1245  std::vector<JPS_Point> areaBoundary;
1246  for (const Position& p : poly->getShape()) {
1247  areaBoundary.push_back({p.x(), p.y()});
1248  }
1249  // Make sure the shape is not repeating the first point.
1250  if (areaBoundary.back().x == areaBoundary.front().x && areaBoundary.back().y == areaBoundary.front().y) {
1251  areaBoundary.pop_back();
1252  }
1253  const std::string type = StringTokenizer(poly->getShapeType(), ".").getVector()[1];
1254  myAreas.push_back({poly->getID(), type, areaBoundary, poly->getParametersMap(), 0});
1255  }
1256  }
1257  if (OutputDevice::createDeviceByOption("pedestrian.jupedsim.py")) {
1258  myPythonScript = &OutputDevice::getDeviceByOption("pedestrian.jupedsim.py");
1260  (*myPythonScript) << "import jupedsim as jps\nimport shapely\n\n"
1261  "with open('" << filename << "') as f: geom = shapely.from_wkt(f.read())\n"
1262  "simulation = jps.Simulation(dt=" << STEPS2TIME(myJPSDeltaT) << ", model=jps.CollisionFreeSpeedModel(), geometry=geom,\n"
1263  "trajectory_writer=jps.SqliteTrajectoryWriter(output_file='out.sql'))\n";
1264  }
1265  // add waiting sets at crossings
1266  for (auto& crossing : myCrossingWaits) {
1267  crossing.second.first = addWaitingSet(crossing.first, true);
1268  crossing.second.second = addWaitingSet(crossing.first, false);
1269  }
1270 }
1271 
1272 
1273 // ===========================================================================
1274 // MSPModel_Remote::PState method definitions
1275 // ===========================================================================
1277  JPS_JourneyId journeyId, JPS_StageId stageId,
1278  const std::vector<WaypointDesc>& waypoints) :
1279  MSPModel_InteractingState(person, stage, nullptr),
1280  myJourneyId(journeyId), myStageId(stageId), myWaypoints(waypoints), myAgentId(0) {
1281  myDir = FORWARD;
1282 }
1283 
1284 
1285 void
1286 MSPModel_JuPedSim::PState::reinit(MSStageMoving* stage, JPS_JourneyId journeyId, JPS_StageId stageId,
1287  const std::vector<WaypointDesc>& waypoints) {
1288  if (myStage != nullptr) {
1289  myStage->setPState(nullptr); // we need to remove the old state reference to avoid double deletion
1290  }
1291  myStage = stage;
1292  myJourneyId = journeyId;
1293  myStageId = stageId;
1294  myWaypoints = waypoints;
1295 }
1296 
1297 
1299 }
1300 
1301 
1302 void MSPModel_JuPedSim::PState::setPosition(double x, double y) {
1303  if (myRemoteXYPos != Position::INVALID) {
1304  mySpeed = myRemoteXYPos.distanceTo2D(Position(x, y)) / STEPS2TIME(DELTA_T);
1305  } else {
1306  mySpeed = 0.;
1307  }
1308  myRemoteXYPos.set(x, y);
1309 }
1310 
1311 
1313  return stage.getNextRouteEdge();
1314 }
1315 
1316 
1319  return offset < (int)myWaypoints.size() ? &myWaypoints[offset] : nullptr;
1320 }
1321 
1322 
1323 /****************************************************************************/
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:263
const MSJunction * getFromJunction() const
Definition: MSEdge.h:414
bool isTazConnector() const
Definition: MSEdge.h:291
const MSJunction * getToJunction() const
Definition: MSEdge.h:418
virtual void addEvent(Command *operation, SUMOTime execTimeStep=-1)
Adds an Event.
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:1515
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:184
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
const NamedObjectCont< MSStoppingPlace * > & getStoppingPlaces(SumoXMLTag category) const
Definition: MSNet.cpp:1408
ActiveLanes myActiveLanes
store of all lanes which have pedestrians on them
int myNumActivePedestrians
the total number of active pedestrians
Container for pedestrian state and individual position update function.
int myDir
the walking direction on the current lane (1 forward, -1 backward)
virtual double getAngle(const MSStageMoving &, SUMOTime) const
return the current orientation in degrees
MSPerson * getPerson() const
return the represented person
MSStageMoving * getStage() const
return the current stage
bool isWaitingToEnter() const
whether the person still waits to entere the network
const MSLane * getLane() const
the current lane of the transportable
Holds pedestrian state and performs updates.
JPS_StageId getStageId() const
first stage of the journey
void setPosition(double x, double y)
void setAngle(double angle)
void setLane(MSLane *lane)
JPS_AgentId getAgentId() const
void setStage(MSStageMoving *const stage)
void setAgentId(JPS_AgentId id)
const MSPModel_JuPedSim::WaypointDesc * getNextWaypoint(const int offset=0) const
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
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 &, SUMOTime) const override
return the network coordinate of the transportable
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)
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)
void registerArrived(const JPS_AgentId agentID)
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
JPS_OperationalModel myJPSOperationalModel
static const int GEOS_QUADRANT_SEGMENTS
static const std::vector< MSPModel_JuPedSim::PState * > noPedestrians
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
static const int BACKWARD
Definition: MSPModel.h:55
static const int FORWARD
Definition: MSPModel.h:54
static const double RANDOM_POS_LAT
magic value to encode randomized lateral offset for persons when starting a walk
Definition: MSPModel.h:68
static const int UNDEFINED_DIRECTION
Definition: MSPModel.h:56
static const double UNSPECIFIED_POS_LAT
the default lateral offset for persons when starting a walk
Definition: MSPModel.h:65
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:94
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:154
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:322
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:276
void norm2D()
Normalizes the given vector.
Definition: Position.h:182
double x() const
Returns the x-position.
Definition: Position.h:55
double length2D() const
Computes the length of the given vector neglecting the z coordinate.
Definition: Position.h:177
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.
void extrapolate2D(const double val, const bool onlyFirst=false)
extrapolate position vector in two dimensions (Z is ignored)
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
double getFloatParam(const std::string &paramName, const bool required=false, const double deflt=INVALID_DOUBLE) const
Retrieve a floating point parameter for the traffic object.
Representation of a vehicle.
Definition: SUMOVehicle.h:62
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
#define M_PI
Definition: odrSpiral.cpp:45
Structure that keeps data related to vanishing areas (and other types of areas).