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