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