1 package org.opentrafficsim.core.gtu;
2
3 import java.util.ArrayList;
4 import java.util.LinkedHashMap;
5 import java.util.LinkedHashSet;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Objects;
9 import java.util.Optional;
10 import java.util.Set;
11
12 import org.djunits.unit.DirectionUnit;
13 import org.djunits.unit.DurationUnit;
14 import org.djunits.unit.PositionUnit;
15 import org.djunits.value.vdouble.scalar.Acceleration;
16 import org.djunits.value.vdouble.scalar.Direction;
17 import org.djunits.value.vdouble.scalar.Duration;
18 import org.djunits.value.vdouble.scalar.Length;
19 import org.djunits.value.vdouble.scalar.Speed;
20 import org.djunits.value.vdouble.vector.PositionVector;
21 import org.djutils.base.Identifiable;
22 import org.djutils.draw.bounds.Bounds2d;
23 import org.djutils.draw.line.Polygon2d;
24 import org.djutils.draw.point.DirectedPoint2d;
25 import org.djutils.draw.point.Point2d;
26 import org.djutils.event.EventType;
27 import org.djutils.event.LocalEventProducer;
28 import org.djutils.exceptions.Throw;
29 import org.djutils.exceptions.Try;
30 import org.djutils.immutablecollections.Immutable;
31 import org.djutils.immutablecollections.ImmutableLinkedHashMap;
32 import org.djutils.immutablecollections.ImmutableMap;
33 import org.djutils.metadata.MetaData;
34 import org.djutils.metadata.ObjectDescriptor;
35 import org.opentrafficsim.base.HierarchicallyTyped;
36 import org.opentrafficsim.base.OtsRuntimeException;
37 import org.opentrafficsim.base.geometry.OffsetRectangleShape;
38 import org.opentrafficsim.base.geometry.OtsLine2d;
39 import org.opentrafficsim.base.geometry.OtsShape;
40 import org.opentrafficsim.base.geometry.PolygonShape;
41 import org.opentrafficsim.base.logger.Logger;
42 import org.opentrafficsim.base.parameters.ParameterException;
43 import org.opentrafficsim.base.parameters.Parameters;
44 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
45 import org.opentrafficsim.core.gtu.RelativePosition.Type;
46 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlan;
47 import org.opentrafficsim.core.gtu.plan.operational.OperationalPlanException;
48 import org.opentrafficsim.core.gtu.plan.strategical.StrategicalPlanner;
49 import org.opentrafficsim.core.gtu.plan.tactical.TacticalPlanner;
50 import org.opentrafficsim.core.network.NetworkException;
51 import org.opentrafficsim.core.perception.Historical;
52 import org.opentrafficsim.core.perception.HistoricalValue;
53 import org.opentrafficsim.core.perception.HistoryManager;
54 import org.opentrafficsim.core.perception.PerceivableContext;
55
56 import nl.tudelft.simulation.dsol.SimRuntimeException;
57 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEventInterface;
58
59
60
61
62
63
64
65
66
67
68 public class Gtu extends LocalEventProducer implements HierarchicallyTyped<GtuType, Gtu>, OtsShape, Identifiable
69 {
70
71 private final String id;
72
73
74 private final int uniqueNumber;
75
76
77 private static int staticUNIQUENUMBER = 0;
78
79
80 private final GtuType gtuType;
81
82
83 private final OtsSimulatorInterface simulator;
84
85
86 private Parameters parameters;
87
88
89 private Acceleration maximumAcceleration;
90
91
92 private Acceleration maximumDeceleration;
93
94
95
96
97
98
99 private Historical<Length> odometer;
100
101
102 private final Historical<StrategicalPlanner> strategicalPlanner;
103
104
105 private final Historical<TacticalPlanner<?, ?>> tacticalPlanner;
106
107
108 private final Historical<OperationalPlan> operationalPlan;
109
110
111 private SimEventInterface<Duration> nextMoveEvent;
112
113
114 private PerceivableContext perceivableContext;
115
116
117 private boolean destroyed = false;
118
119
120 private double alignStep = Double.NaN;
121
122
123 private Duration cacheLocationTime = Duration.NaN;
124
125
126 private DirectedPoint2d cacheLocation = null;
127
128
129 private double cachedSpeedTime = Double.NaN;
130
131
132 private Speed cachedSpeed = null;
133
134
135 private double cachedAccelerationTime = Double.NaN;
136
137
138 private Acceleration cachedAcceleration = null;
139
140
141 private Gtu parent = null;
142
143
144 private Set<Gtu> children = new LinkedHashSet<>();
145
146
147 private GtuErrorHandler errorHandler = GtuErrorHandler.THROW;
148
149
150 private final OtsShape shape;
151
152
153 private final Map<RelativePosition.Type, RelativePosition> relativePositions = new LinkedHashMap<>();
154
155
156 private final Length length;
157
158
159 private final Length width;
160
161
162 private final Speed maximumSpeed;
163
164
165 private final Map<String, String> tags = new LinkedHashMap<>();
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 @SuppressWarnings("checkstyle:parameternumber")
182 private Gtu(final String id, final GtuType gtuType, final OtsSimulatorInterface simulator,
183 final PerceivableContext perceivableContext, final Length length, final Length width, final Length front,
184 final Polygon2d contour, final Speed maximumSpeed) throws GtuException
185 {
186 Throw.whenNull(id, "id");
187 Throw.whenNull(gtuType, "gtuType");
188 Throw.whenNull(simulator, "simulator");
189 Throw.whenNull(perceivableContext, "perceivableContext");
190 Throw.when(perceivableContext.containsGtuId(id), GtuException.class,
191 "GTU with id %s already registered in perceivableContext %s", id, perceivableContext.getId());
192 Throw.whenNull(maximumSpeed, "maximumSpeed");
193 this.maximumSpeed = maximumSpeed;
194
195 HistoryManager historyManager = simulator.getReplication().getHistoryManager(simulator);
196 this.id = id;
197 this.uniqueNumber = ++staticUNIQUENUMBER;
198 this.gtuType = gtuType;
199 this.simulator = simulator;
200 this.odometer = new HistoricalValue<>(historyManager, this, Length.ZERO);
201 this.perceivableContext = perceivableContext;
202 this.perceivableContext.addGTU(this);
203 this.strategicalPlanner = new HistoricalValue<>(historyManager, this);
204 this.tacticalPlanner = new HistoricalValue<>(historyManager, this, null);
205 this.operationalPlan = new HistoricalValue<>(historyManager, this, null);
206
207 this.length = length;
208 this.width = width;
209 if (contour == null)
210 {
211 this.shape =
212 new OffsetRectangleShape(front.si - this.length.si, front.si, -this.width.si / 2.0, this.width.si / 2.0)
213 {
214 @Override
215 public DirectedPoint2d getLocation()
216 {
217 return Gtu.this.getLocation();
218 }
219 };
220 }
221 else
222 {
223 this.shape = new PolygonShape(contour)
224 {
225 @Override
226 public DirectedPoint2d getLocation()
227 {
228 return Gtu.this.getLocation();
229 }
230 };
231 }
232
233 this.relativePositions.put(RelativePosition.REFERENCE, RelativePosition.REFERENCE_POSITION);
234 this.relativePositions.put(RelativePosition.FRONT,
235 new RelativePosition(front, Length.ZERO, Length.ZERO, RelativePosition.FRONT));
236 this.relativePositions.put(RelativePosition.REAR,
237 new RelativePosition(front.minus(this.length), Length.ZERO, Length.ZERO, RelativePosition.REAR));
238 Point2d midPoint = this.shape.getRelativeBounds().midPoint();
239 this.relativePositions.put(RelativePosition.CENTER,
240 new RelativePosition(Length.ofSI(midPoint.x), Length.ofSI(midPoint.y), Length.ZERO, RelativePosition.CENTER));
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254 public Gtu(final String id, final GtuType gtuType, final OtsSimulatorInterface simulator,
255 final PerceivableContext perceivableContext, final Polygon2d contour, final Speed maximumSpeed) throws GtuException
256 {
257 this(id, gtuType, simulator, perceivableContext, Length.ofSI(contour.getAbsoluteBounds().getDeltaX()),
258 Length.ofSI(contour.getAbsoluteBounds().getDeltaY()), Length.ofSI(contour.getAbsoluteBounds().getMaxX()),
259 contour, maximumSpeed);
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275 @SuppressWarnings("checkstyle:parameternumber")
276 public Gtu(final String id, final GtuType gtuType, final OtsSimulatorInterface simulator,
277 final PerceivableContext perceivableContext, final Length length, final Length width, final Length front,
278 final Speed maximumSpeed) throws GtuException
279 {
280 this(id, gtuType, simulator, perceivableContext, length, width, front, null, maximumSpeed);
281 }
282
283
284
285
286
287
288
289
290
291
292
293 @SuppressWarnings({"checkstyle:hiddenfield", "checkstyle:designforextension"})
294 public void init(final StrategicalPlanner strategicalPlanner, final DirectedPoint2d initialLocation,
295 final Speed initialSpeed) throws SimRuntimeException, GtuException
296 {
297 Throw.whenNull(strategicalPlanner, "strategicalPlanner");
298 Throw.whenNull(initialLocation, "Initial location of GTU cannot be null");
299 Throw.when(Double.isNaN(initialLocation.x) || Double.isNaN(initialLocation.y), GtuException.class,
300 "initialLocation %s invalid for GTU with id %s", initialLocation, this.id);
301 Throw.whenNull(initialSpeed, "initialSpeed");
302 Throw.when(!getId().equals(strategicalPlanner.getGtu().getId()), GtuException.class,
303 "GTU %s is initialized with a strategical planner for GTU %s", getId(), strategicalPlanner.getGtu().getId());
304
305 this.strategicalPlanner.set(strategicalPlanner);
306 this.tacticalPlanner.set(strategicalPlanner.getTacticalPlanner());
307
308 try
309 {
310 move(initialLocation);
311 }
312 catch (OperationalPlanException | NetworkException | ParameterException exception)
313 {
314 throw new GtuException("Failed to create OperationalPlan for GTU " + this.id, exception);
315 }
316 }
317
318
319
320
321
322 public RelativePosition getFront()
323 {
324 return this.relativePositions.get(RelativePosition.FRONT);
325 }
326
327
328
329
330
331 public RelativePosition getRear()
332 {
333 return this.relativePositions.get(RelativePosition.REAR);
334 }
335
336
337
338
339
340 public RelativePosition getCenter()
341 {
342 return this.relativePositions.get(RelativePosition.CENTER);
343 }
344
345
346
347
348
349 public ImmutableMap<Type, RelativePosition> getRelativePositions()
350 {
351 return new ImmutableLinkedHashMap<>(this.relativePositions, Immutable.WRAP);
352 }
353
354
355
356
357
358 public Length getLength()
359 {
360 return this.length;
361 }
362
363
364
365
366
367 public Length getWidth()
368 {
369 return this.width;
370 }
371
372
373
374
375
376 public Speed getMaximumSpeed()
377 {
378 return this.maximumSpeed;
379 }
380
381 @Override
382 public Bounds2d getRelativeBounds()
383 {
384 return this.shape.getRelativeBounds();
385 }
386
387
388
389
390 @SuppressWarnings("checkstyle:designforextension")
391 public void destroy()
392 {
393 DirectedPoint2d location = getLocation();
394 fireTimedEvent(Gtu.DESTROY_EVENT,
395 new Object[] {getId(), new PositionVector(new double[] {location.x, location.y}, PositionUnit.METER),
396 new Direction(location.getDirZ(), DirectionUnit.EAST_RADIAN), getOdometer()},
397 this.simulator.getSimulatorTime());
398
399
400 if (this.nextMoveEvent != null)
401 {
402 this.simulator.cancelEvent(this.nextMoveEvent);
403 this.nextMoveEvent = null;
404 }
405
406 this.perceivableContext.removeGTU(this);
407 this.destroyed = true;
408 }
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423 @SuppressWarnings("checkstyle:designforextension")
424 protected boolean move(final DirectedPoint2d fromLocation)
425 throws SimRuntimeException, GtuException, NetworkException, ParameterException
426 {
427 try
428 {
429 Duration now = this.simulator.getSimulatorTime();
430
431
432
433 Length currentOdometer;
434 if (this.operationalPlan.get() != null)
435 {
436 currentOdometer = this.odometer.get().plus(this.operationalPlan.get().getTraveledDistance(now));
437 }
438 else
439 {
440 currentOdometer = this.odometer.get();
441 }
442
443
444 TacticalPlanner<?, ?> tactPlanner = this.tacticalPlanner.get();
445 if (tactPlanner == null)
446 {
447
448 tactPlanner = this.strategicalPlanner.get().getTacticalPlanner();
449 this.tacticalPlanner.set(tactPlanner);
450 }
451 synchronized (this)
452 {
453 tactPlanner.getPerception().perceive();
454 }
455 OperationalPlan newOperationalPlan = tactPlanner.generateOperationalPlan(now, fromLocation);
456 synchronized (this)
457 {
458 this.operationalPlan.set(newOperationalPlan);
459 this.cachedSpeedTime = Double.NaN;
460 this.cachedAccelerationTime = Double.NaN;
461 this.odometer.set(currentOdometer);
462 }
463
464 if (!Double.isNaN(this.alignStep))
465 {
466
467 double tNext = Math.floor(now.si / this.alignStep + 1.0) * this.alignStep;
468 DirectedPoint2d p = (tNext - now.si < this.alignStep) ? newOperationalPlan.getEndLocation()
469 : newOperationalPlan.getLocationFromStart(new Duration(tNext - now.si, DurationUnit.SI));
470 this.nextMoveEvent = this.simulator.scheduleEventRel(Duration.ofSI(tNext), () ->
471 {
472 try
473 {
474 move(p);
475 }
476 catch (SimRuntimeException | GtuException | NetworkException | ParameterException exception)
477 {
478 throw new OtsRuntimeException("Exception during move.", exception);
479 }
480 });
481 }
482 else
483 {
484
485
486 this.nextMoveEvent = this.simulator.scheduleEventRel(newOperationalPlan.getTotalDuration(), () ->
487 {
488 try
489 {
490 move(newOperationalPlan.getEndLocation());
491 }
492 catch (SimRuntimeException | GtuException | NetworkException | ParameterException exception)
493 {
494 throw new OtsRuntimeException("Exception during move.", exception);
495 }
496 });
497 }
498
499 fireTimedEvent(Gtu.MOVE_EVENT,
500 new Object[] {getId(),
501 new PositionVector(new double[] {fromLocation.x, fromLocation.y}, PositionUnit.METER),
502 new Direction(fromLocation.getDirZ(), DirectionUnit.EAST_RADIAN), getSpeed(), getAcceleration(),
503 getOdometer()},
504 this.simulator.getSimulatorTime());
505
506 return false;
507 }
508 catch (RuntimeException ex)
509 {
510 try
511 {
512 this.errorHandler.handle(this, ex);
513 }
514 catch (Exception exception)
515 {
516 throw new GtuException(exception);
517 }
518 return true;
519 }
520 }
521
522
523
524
525
526
527
528
529
530 @SuppressWarnings("checkstyle:designforextension")
531 protected void interruptMove() throws SimRuntimeException, GtuException, NetworkException, ParameterException
532 {
533 this.simulator.cancelEvent(this.nextMoveEvent);
534 move(this.operationalPlan.get().getLocation(this.simulator.getSimulatorTime()));
535 }
536
537 @Override
538 public String getId()
539 {
540 return this.id;
541 }
542
543
544
545
546
547
548 public void setTag(final String tag, final String value)
549 {
550 this.tags.put(tag, value);
551 }
552
553
554
555
556
557
558 public Optional<String> getTag(final String tag)
559 {
560 return Optional.ofNullable(this.tags.get(tag));
561 }
562
563 @Override
564 public GtuType getType()
565 {
566 return this.gtuType;
567 }
568
569
570
571
572
573 public RelativePosition getReference()
574 {
575 return RelativePosition.REFERENCE_POSITION;
576 }
577
578
579
580
581
582 public OtsSimulatorInterface getSimulator()
583 {
584 return this.simulator;
585 }
586
587
588
589
590
591 public Parameters getParameters()
592 {
593 return this.parameters;
594 }
595
596
597
598
599
600 public void setParameters(final Parameters parameters)
601 {
602 this.parameters = parameters;
603 }
604
605
606
607
608
609
610 public StrategicalPlanner getStrategicalPlanner()
611 {
612 return this.strategicalPlanner.get();
613 }
614
615
616
617
618
619
620
621 public StrategicalPlanner getStrategicalPlanner(final Duration time)
622 {
623 return this.strategicalPlanner.get(time);
624 }
625
626
627
628
629
630 public TacticalPlanner<?, ?> getTacticalPlanner()
631 {
632 return getStrategicalPlanner().getTacticalPlanner();
633 }
634
635
636
637
638
639
640 public TacticalPlanner<?, ?> getTacticalPlanner(final Duration time)
641 {
642 return getStrategicalPlanner(time).getTacticalPlanner(time);
643 }
644
645
646
647
648
649 public OperationalPlan getOperationalPlan()
650 {
651 return this.operationalPlan.get();
652 }
653
654
655
656
657
658
659 public OperationalPlan getOperationalPlan(final Duration time)
660 {
661 return this.operationalPlan.get(time);
662 }
663
664
665
666
667
668 protected void setOperationalPlan(final OperationalPlan operationalPlan)
669 {
670 this.operationalPlan.set(operationalPlan);
671 }
672
673
674
675
676
677 public Length getOdometer()
678 {
679 return getOdometer(this.simulator.getSimulatorTime());
680 }
681
682
683
684
685
686
687 public Length getOdometer(final Duration time)
688 {
689 synchronized (this)
690 {
691 OperationalPlan historicalPlan = getOperationalPlan(time);
692 if (historicalPlan == null || historicalPlan.getStartTime().gt(time) || historicalPlan.getEndTime().lt(time))
693 {
694 return this.odometer.get(time);
695 }
696 try
697 {
698 return this.odometer.get(time).plus(getOperationalPlan(time).getTraveledDistance(time));
699 }
700 catch (OperationalPlanException ope)
701 {
702 Logger.ots().warn("OperationalPlan could not give a traveled distance it the requested time.");
703 return this.odometer.get(time);
704 }
705 }
706 }
707
708
709
710
711
712 public Speed getSpeed()
713 {
714 synchronized (this)
715 {
716 return getSpeed(this.simulator.getSimulatorTime());
717 }
718 }
719
720
721
722
723
724
725 public Speed getSpeed(final Duration time)
726 {
727 synchronized (this)
728 {
729 if (this.cachedSpeedTime != time.si)
730 {
731
732 this.cachedSpeedTime = Double.NaN;
733 this.cachedSpeed = null;
734 OperationalPlan plan = getOperationalPlan(time);
735 if (plan == null)
736 {
737 this.cachedSpeed = Speed.ZERO;
738 }
739 else if (time.si < plan.getStartTime().si)
740 {
741 this.cachedSpeed = plan.getStartSpeed();
742 }
743 else if (time.si > plan.getEndTime().si)
744 {
745 if (time.si - plan.getEndTime().si < 1e-6)
746 {
747 this.cachedSpeed = Try.assign(() -> plan.getSpeed(plan.getEndTime()),
748 "getSpeed() could not derive a valid speed for the current operationalPlan");
749 }
750 else
751 {
752 throw new IllegalStateException("Requesting speed value beyond plan.");
753 }
754 }
755 else
756 {
757 this.cachedSpeed = Try.assign(() -> plan.getSpeed(time),
758 "getSpeed() could not derive a valid speed for the current operationalPlan");
759 }
760 this.cachedSpeedTime = time.si;
761 }
762 return this.cachedSpeed;
763 }
764 }
765
766
767
768
769
770 public Acceleration getAcceleration()
771 {
772 synchronized (this)
773 {
774 return getAcceleration(this.simulator.getSimulatorTime());
775 }
776 }
777
778
779
780
781
782
783 public Acceleration getAcceleration(final Duration time)
784 {
785 synchronized (this)
786 {
787 if (this.cachedAccelerationTime != time.si)
788 {
789
790 this.cachedAccelerationTime = Double.NaN;
791 this.cachedAcceleration = null;
792 OperationalPlan plan = getOperationalPlan(time);
793 if (plan == null)
794 {
795 this.cachedAcceleration = Acceleration.ZERO;
796 }
797 else if (time.si < plan.getStartTime().si)
798 {
799 this.cachedAcceleration =
800 Try.assign(() -> plan.getAcceleration(plan.getStartTime()), "Exception obtaining acceleration.");
801 }
802 else if (time.si > plan.getEndTime().si)
803 {
804 if (time.si - plan.getEndTime().si < 1e-6)
805 {
806 this.cachedAcceleration = Try.assign(() -> plan.getAcceleration(plan.getEndTime()),
807 "getAcceleration() could not derive a valid acceleration for the current operationalPlan");
808 }
809 else
810 {
811 throw new IllegalStateException("Requesting acceleration value beyond plan.");
812 }
813 }
814 else
815 {
816 this.cachedAcceleration = Try.assign(() -> plan.getAcceleration(time),
817 "getAcceleration() could not derive a valid acceleration for the current operationalPlan");
818 }
819 this.cachedAccelerationTime = time.si;
820 }
821 return this.cachedAcceleration;
822 }
823 }
824
825
826
827
828
829 public Acceleration getMaximumAcceleration()
830 {
831 return this.maximumAcceleration;
832 }
833
834
835
836
837
838 public void setMaximumAcceleration(final Acceleration maximumAcceleration)
839 {
840 if (maximumAcceleration.le(Acceleration.ZERO))
841 {
842 throw new OtsRuntimeException("Maximum acceleration of GTU " + this.id + " set to value <= 0");
843 }
844 this.maximumAcceleration = maximumAcceleration;
845 }
846
847
848
849
850
851 public Acceleration getMaximumDeceleration()
852 {
853 return this.maximumDeceleration;
854 }
855
856
857
858
859
860 public void setMaximumDeceleration(final Acceleration maximumDeceleration)
861 {
862 if (maximumDeceleration.ge(Acceleration.ZERO))
863 {
864 throw new OtsRuntimeException("Cannot set maximum deceleration of GTU " + this.id + " to " + maximumDeceleration
865 + " (value must be negative)");
866 }
867 this.maximumDeceleration = maximumDeceleration;
868 }
869
870 @Override
871 public synchronized DirectedPoint2d getLocation()
872 {
873 Duration locationTime = this.simulator.getSimulatorTime();
874 if (null == this.cacheLocationTime || this.cacheLocationTime.si != locationTime.si)
875 {
876 this.cacheLocation = getLocation(locationTime);
877 this.cacheLocationTime = locationTime;
878 }
879 return this.cacheLocation;
880 }
881
882
883
884
885
886
887 public synchronized DirectedPoint2d getLocation(final Duration time)
888 {
889 try
890 {
891 return this.operationalPlan.get(time).getLocation(time);
892 }
893 catch (OperationalPlanException exception)
894 {
895 return new DirectedPoint2d(0, 0, 0);
896 }
897 }
898
899 @Override
900 public double signedDistance(final Point2d point)
901 {
902 return this.shape.signedDistance(point);
903 }
904
905
906
907
908
909
910
911
912 @Override
913 public Polygon2d getAbsoluteContour(final Duration time)
914 {
915 try
916 {
917 return new Polygon2d(0.0, OtsShape.toAbsoluteTransform(this.operationalPlan.get(time).getLocation(time))
918 .transform(getRelativeContour().iterator()));
919 }
920 catch (OperationalPlanException exception)
921 {
922 throw new OtsRuntimeException(exception);
923 }
924 }
925
926
927
928
929
930
931
932 @Override
933 public Polygon2d getAbsoluteContour()
934 {
935 try
936 {
937
938 OtsLine2d path = this.operationalPlan.get().getPath();
939
940
941 double rear = Math.max(0.0, getReference().dx().si - getRear().dx().si);
942 double front = path.getLength() + Math.max(0.0, getFront().dx().si - getReference().dx().si);
943 Point2d p0 = path.getLocationExtended(-rear);
944 Point2d pn = path.getLocationExtended(front);
945 List<Point2d> pList = path.getPointList();
946 pList.add(0, p0);
947 pList.add(pn);
948 OtsLine2d extendedPath = new OtsLine2d(pList);
949 List<Point2d> swath = new ArrayList<>();
950 swath.addAll(extendedPath.offsetLine(getWidth().si / 2.0).getPointList());
951 swath.addAll(extendedPath.offsetLine(-getWidth().si / 2.0).reverse().getPointList());
952 Polygon2d s = new Polygon2d(0.0, swath);
953 return s;
954 }
955 catch (Exception e)
956 {
957 throw new OtsRuntimeException(e);
958 }
959 }
960
961 @Override
962 public Polygon2d getRelativeContour()
963 {
964 return this.shape.getRelativeContour();
965 }
966
967
968
969
970
971 public boolean isDestroyed()
972 {
973 return this.destroyed;
974 }
975
976
977
978
979
980 public PerceivableContext getPerceivableContext()
981 {
982 return this.perceivableContext;
983 }
984
985
986
987
988
989
990 public void addGtu(final Gtu gtu) throws GtuException
991 {
992 this.children.add(gtu);
993 gtu.setParent(this);
994 }
995
996
997
998
999
1000 public void removeGtu(final Gtu gtu)
1001 {
1002 this.children.remove(gtu);
1003 try
1004 {
1005 gtu.setParent(null);
1006 }
1007 catch (GtuException exception)
1008 {
1009
1010 }
1011 }
1012
1013
1014
1015
1016
1017
1018 public void setParent(final Gtu gtu) throws GtuException
1019 {
1020 Throw.when(gtu != null && this.parent != null, GtuException.class, "GTU %s already has a parent.", this);
1021 this.parent = gtu;
1022 }
1023
1024
1025
1026
1027
1028 public Optional<Gtu> getParent()
1029 {
1030 return Optional.ofNullable(this.parent);
1031 }
1032
1033
1034
1035
1036
1037 public Set<Gtu> getChildren()
1038 {
1039 return new LinkedHashSet<>(this.children);
1040 }
1041
1042
1043
1044
1045
1046 protected GtuErrorHandler getErrorHandler()
1047 {
1048 return this.errorHandler;
1049 }
1050
1051
1052
1053
1054
1055 public void setErrorHandler(final GtuErrorHandler errorHandler)
1056 {
1057 this.errorHandler = errorHandler;
1058 }
1059
1060
1061
1062
1063
1064 public double getAlignStep()
1065 {
1066 return this.alignStep;
1067 }
1068
1069
1070
1071
1072
1073 public void setAlignStep(final double alignStep)
1074 {
1075 this.alignStep = alignStep;
1076 }
1077
1078
1079
1080
1081
1082 public SimEventInterface<Duration> getNextMoveEvent()
1083 {
1084 return this.nextMoveEvent;
1085 }
1086
1087 @Override
1088 public int hashCode()
1089 {
1090 return Objects.hash(this.uniqueNumber);
1091 }
1092
1093 @Override
1094 @SuppressWarnings("checkstyle:needbraces")
1095 public boolean equals(final Object obj)
1096 {
1097 if (this == obj)
1098 return true;
1099 if (obj == null)
1100 return false;
1101 if (getClass() != obj.getClass())
1102 return false;
1103 Gtu other = (Gtu) obj;
1104 return this.uniqueNumber == other.uniqueNumber;
1105 }
1106
1107
1108
1109
1110
1111 public static final EventType MOVE_EVENT = new EventType("GTU.MOVE",
1112 new MetaData("GTU move", "GTU id, position, speed, acceleration, odometer",
1113 new ObjectDescriptor[] {new ObjectDescriptor("Id", "GTU Id", String.class),
1114 new ObjectDescriptor("position", "position", PositionVector.class),
1115 new ObjectDescriptor("direction", "direction", Direction.class),
1116 new ObjectDescriptor("speed", "speed", Speed.class),
1117 new ObjectDescriptor("acceleration", "acceleration", Acceleration.class),
1118 new ObjectDescriptor("Odometer", "Total distance travelled since incarnation", Length.class)}));
1119
1120
1121
1122
1123
1124 public static final EventType DESTROY_EVENT = new EventType("GTU.DESTROY",
1125 new MetaData("GTU destroy", "GTU id, final position, final odometer",
1126 new ObjectDescriptor[] {new ObjectDescriptor("Id", "GTU Id", String.class),
1127 new ObjectDescriptor("position", "position", PositionVector.class),
1128 new ObjectDescriptor("direction", "direction", Direction.class),
1129 new ObjectDescriptor("Odometer", "Total distance travelled since incarnation", Length.class)}));
1130
1131 }