@golemio/pid 2.2.11-dev.613632989 → 2.2.11-dev.613720384

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -80,87 +80,6 @@ export declare class VehiclePositionsWorker extends BaseWorker {
80
80
  * @returns {Object[]} - Arrays of inserted/updated positions and array of (inserted)/updated trips
81
81
  */
82
82
  private updateComputedPositionsAndTrips;
83
- /**
84
- * Compute UTC timestamp of start of day when trip starts
85
- *
86
- * @param {number} startTimestamp - Unix timestamp of start of the trip
87
- * @param {number} firstStopTimeScheduledSeconds - Number of seconds from midnight of first stop departure
88
- * @returns {number} - Returns unix timestamp in milliseconds.
89
- */
90
- private getStartDayTimestamp;
91
- /**
92
- * Compute positions and return computed positions
93
- *
94
- * @param {ITripPositionsWithGTFS} tripPositions - Trip positions with shape anchors data
95
- * @returns {Promise<IProcessedPositions>} - Returns computed/updated positions
96
- */
97
- private computePositions;
98
- /**
99
- * Decide how to process input position data
100
- *
101
- * @param {IPosition} position - Input vehiclepositions_positions row data
102
- * @returns {PositionHandlerEnum} - Returns action handler enum
103
- */
104
- private setPositionUpdateHandler;
105
- /**
106
- * Takes position one by one, set proper handler for type of position, and do the process of position
107
- *
108
- * @param {number} i - Iteration
109
- * @param {IUpdatePositionsIteratorOptions} options - Initial options
110
- * @param {number} cb - Callback function of iterator
111
- * @returns {void} - void
112
- */
113
- private updatePositionsIterator;
114
- /**
115
- * Returns unix in miliseconds for stop time
116
- * + given by start of day when trip starts
117
- * + and GTFS HH:mm:ss format of stop time in seconds
118
- *
119
- * @param {number} startDayTimestamp - Unix timestamp of local time midnight before trip starts
120
- * @param {number} timeSeconds - Number of seconds given by HH:mm:ss format of GTFS stop time
121
- * @returns {number} - Returns unix milisecods
122
- */
123
- private getLocalTimeToUnix;
124
- /**
125
- * Picks only one closest point for each segment/array of points of shape
126
- *
127
- * @param {IShapeAnchorPoint[][]} ptsInRadius - Feature Point of current position
128
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
129
- * @returns {IShapeAnchorPoint[]} - Result closest point for each segment
130
- */
131
- private getClosestPoints;
132
- /**
133
- * Corrects time delay at stop with dwelling time
134
- *
135
- * @param {number} timeDelay - Initial computed delay in seconds, can be negative for trip ahead
136
- * @param {IPositionsContext | null} context - Context state, holds information about previous positions
137
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
138
- * @param {IShapeAnchorPoint} thisClosestPoint - Closest point of shape anchors
139
- * @param { departureTime: number; arrivalTime: number } stopTimes - departure and arrival stop times in seconds
140
- * @returns {number} - Result delay in seconds, can be negative for trip ahead
141
- */
142
- private getCorrectedTimeDelay;
143
- /**
144
- * Picks only one closest point for multiple possible points based on delay of last position and stop times
145
- *
146
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
147
- * @param {IPositionsContext | null} context - Context state, holds information about previous positions
148
- * @param {number} startDayTimestamp - Unix timestamp of start of the day
149
- * @param {IShapeAnchorPoint[]} closestPts - All closest points of possible segments
150
- * @param {IComputationTrip} tripGtfsData - GTFS data and all set of known positions
151
- * @returns {IPositionToUpdate} - Result point as position to update in DB
152
- */
153
- private getClosestPoint;
154
- /**
155
- * Returns estimate of point on shape, where the trip should be with appropriate delay
156
- *
157
- * @param {IShapeAnchorPoint[]} tripShapePoints - Precalculated trip shape equidistant points with scheduled times
158
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Current position of trip
159
- * @param {IPositionsContext | null} context - Context state, holds information about previous positions
160
- * @param {number} startDayTimestamp - Unix timestamp of midnight before trip starts
161
- * @returns {IPositionToUpdate} - Position object to update
162
- */
163
- private getEstimatedPoint;
164
83
  /**
165
84
  * Returns shape anchor points along trip track shape with stop times parameters
166
85
  *
@@ -51,24 +51,13 @@ const golemio_errors_1 = require("@golemio/core/dist/shared/golemio-errors");
51
51
  const golemio_validator_1 = require("@golemio/core/dist/shared/golemio-validator");
52
52
  const moment_timezone_1 = __importDefault(require("@golemio/core/dist/shared/moment-timezone"));
53
53
  const turf = __importStar(require("@turf/turf"));
54
- const cheap_ruler_1 = __importDefault(require("cheap-ruler"));
55
54
  const const_1 = require("../../const");
56
- const DPPUtils_1 = __importDefault(require("./helpers/DPPUtils"));
57
55
  const GtfsTripManager_1 = __importDefault(require("./helpers/GtfsTripManager"));
58
- const PositionCalculator_1 = __importDefault(require("./helpers/PositionCalculator"));
59
- const ValidToCalculator_1 = __importDefault(require("./helpers/ValidToCalculator"));
56
+ const PositionsManager_1 = __importDefault(require("./helpers/PositionsManager"));
60
57
  const models_2 = require("./models");
61
58
  const transformations_1 = require("./transformations");
62
59
  const VehiclePositionsUtils_1 = require("./VehiclePositionsUtils");
63
60
  const GtfsOvapi = require("@golemio/ovapi-gtfs-realtime-bindings/dist/gtfs-realtime");
64
- var PositionHandlerEnum;
65
- (function (PositionHandlerEnum) {
66
- PositionHandlerEnum["TRACKING"] = "TRACKING";
67
- PositionHandlerEnum["NOT_TRACKING"] = "NOT_TRACKING";
68
- PositionHandlerEnum["CANCELED"] = "CANCELED";
69
- PositionHandlerEnum["DO_NOTHING"] = "DO_NOTHING";
70
- })(PositionHandlerEnum || (PositionHandlerEnum = {}));
71
- const ruler = (0, cheap_ruler_1.default)(50);
72
61
  const gtfsRealtime = GtfsOvapi.transit_realtime;
73
62
  class VehiclePositionsWorker extends workers_1.BaseWorker {
74
63
  constructor() {
@@ -463,7 +452,7 @@ class VehiclePositionsWorker extends workers_1.BaseWorker {
463
452
  return;
464
453
  }
465
454
  }
466
- return this.computePositions(trip, inputData.schedule);
455
+ return PositionsManager_1.default.computePositions(trip, inputData.schedule);
467
456
  })));
468
457
  const validProcessedPositions = processedPositions.filter((e) => !!e);
469
458
  yield this.updateComputedPositionsAndTrips(validProcessedPositions);
@@ -646,447 +635,6 @@ class VehiclePositionsWorker extends workers_1.BaseWorker {
646
635
  tripsUpdated,
647
636
  };
648
637
  });
649
- /**
650
- * Compute UTC timestamp of start of day when trip starts
651
- *
652
- * @param {number} startTimestamp - Unix timestamp of start of the trip
653
- * @param {number} firstStopTimeScheduledSeconds - Number of seconds from midnight of first stop departure
654
- * @returns {number} - Returns unix timestamp in milliseconds.
655
- */
656
- this.getStartDayTimestamp = (startTimestamp, firstStopTimeScheduledSeconds) => {
657
- let startDayTimestamp = moment_timezone_1.default.utc(startTimestamp).tz("Europe/Prague").startOf("day");
658
- const stopTimeDayOverflow = Math.floor(firstStopTimeScheduledSeconds / (60 * 60 * 24));
659
- // if trip has 24+ stop times set real startDay to yesterday
660
- if (stopTimeDayOverflow > 0) {
661
- startDayTimestamp.subtract(1, "day");
662
- }
663
- return startDayTimestamp.valueOf();
664
- };
665
- /**
666
- * Compute positions and return computed positions
667
- *
668
- * @param {ITripPositionsWithGTFS} tripPositions - Trip positions with shape anchors data
669
- * @returns {Promise<IProcessedPositions>} - Returns computed/updated positions
670
- */
671
- this.computePositions = (tripPositions, schedule) => __awaiter(this, void 0, void 0, function* () {
672
- const startTimestamp = parseInt(tripPositions.start_timestamp, 10);
673
- const startDayTimestamp = this.getStartDayTimestamp(startTimestamp, tripPositions.gtfsData.shapes_anchor_points[0].time_scheduled_seconds);
674
- const gtfsRouteType = tripPositions.gtfs_route_type;
675
- return new Promise((resolve) => {
676
- const context = {
677
- atStopStreak: {
678
- stop_sequence: null,
679
- firstPositionTimestamp: null,
680
- firstPositionDelay: null,
681
- },
682
- lastPositionLastStop: {
683
- id: null,
684
- sequence: null,
685
- arrival_time: null,
686
- departure_time: null,
687
- },
688
- lastPositionDelay: null,
689
- lastPositionId: null,
690
- lastPositionOriginTimestamp: null,
691
- lastPositionTracking: null,
692
- lastPositionCanceled: null,
693
- lastPositionBeforeTrackDelayed: null,
694
- isLastPositionAfterTrack: false,
695
- tripId: tripPositions.id,
696
- };
697
- const computedPositions = [];
698
- this.updatePositionsIterator(0, { tripPositions, startTimestamp, startDayTimestamp, context, computedPositions, gtfsRouteType }, schedule, () => {
699
- resolve({
700
- context,
701
- positions: computedPositions,
702
- });
703
- });
704
- });
705
- });
706
- /**
707
- * Decide how to process input position data
708
- *
709
- * @param {IPosition} position - Input vehiclepositions_positions row data
710
- * @returns {PositionHandlerEnum} - Returns action handler enum
711
- */
712
- this.setPositionUpdateHandler = (position) => {
713
- if (position.state_process === const_1.StateProcessEnum.PROCESSED)
714
- return PositionHandlerEnum.DO_NOTHING;
715
- if (position.is_canceled)
716
- return PositionHandlerEnum.CANCELED;
717
- if (position.lat && position.lng && position.tracking === const_1.PositionTrackingEnum.TRACKING)
718
- return PositionHandlerEnum.TRACKING;
719
- if (position.tracking !== const_1.PositionTrackingEnum.TRACKING)
720
- return PositionHandlerEnum.NOT_TRACKING;
721
- return PositionHandlerEnum.NOT_TRACKING;
722
- };
723
- /**
724
- * Takes position one by one, set proper handler for type of position, and do the process of position
725
- *
726
- * @param {number} i - Iteration
727
- * @param {IUpdatePositionsIteratorOptions} options - Initial options
728
- * @param {number} cb - Callback function of iterator
729
- * @returns {void} - void
730
- */
731
- this.updatePositionsIterator = (i, options, schedule, cb) => __awaiter(this, void 0, void 0, function* () {
732
- const { tripPositions, startDayTimestamp, startTimestamp, context, computedPositions, gtfsRouteType } = options;
733
- if (i === tripPositions.positions.length) {
734
- return cb();
735
- }
736
- const position = tripPositions.positions[i];
737
- let positionToUpdate = null;
738
- // situations
739
- switch (this.setPositionUpdateHandler(position)) {
740
- case PositionHandlerEnum.TRACKING:
741
- const currentPosition = turf.point([position.lng, position.lat], {
742
- id: position.id,
743
- origin_time: position.origin_time,
744
- origin_timestamp: position.origin_timestamp,
745
- tcp_event: position.tcp_event,
746
- });
747
- positionToUpdate = yield this.getEstimatedPoint(tripPositions.gtfsData, currentPosition, context, startDayTimestamp, tripPositions.gtfs_route_type);
748
- break;
749
- case PositionHandlerEnum.NOT_TRACKING:
750
- // if there is no previous positions with tracking status, set position as before_track
751
- const firstShapesAnchorPoint = tripPositions.gtfsData.shapes_anchor_points[0];
752
- if (context.lastPositionTracking === null) {
753
- const firstStopTime = tripPositions.gtfsData.stop_times[0];
754
- // if there is propagated delay within 10 minutes we can use it for new position
755
- const canDuplicatePropagatedDelay = context.lastPositionBeforeTrackDelayed !== null &&
756
- position.origin_timestamp - context.lastPositionBeforeTrackDelayed.origin_timestamp <= 10 * 60 * 1000;
757
- // if there is no delay to duplicate and DPP trip is far from start then invisible
758
- const setAsInvisible = !canDuplicatePropagatedDelay &&
759
- DPPUtils_1.default.isInvisible(tripPositions.agency_name_scheduled, position.origin_timestamp, startTimestamp, [position.lng, position.lat], firstShapesAnchorPoint.coordinates, schedule, tripPositions.gtfs_trip_id, context.lastPositionTracking, position.tracking);
760
- positionToUpdate = Object.assign({ id: position.id, next_stop_arrival_time: startDayTimestamp + firstStopTime.arrival_time_seconds * 1000, next_stop_departure_time: startDayTimestamp + firstStopTime.departure_time_seconds * 1000, next_stop_id: firstStopTime.stop_id, next_stop_sequence: firstStopTime.stop_sequence, next_stop_name: firstStopTime.stop.stop_name, shape_dist_traveled: firstStopTime.shape_dist_traveled, state_position: setAsInvisible ? const_1.StatePositionEnum.INVISIBLE : const_1.StatePositionEnum.BEFORE_TRACK, state_process: const_1.StateProcessEnum.PROCESSED }, (firstStopTime.stop_headsign && {
761
- last_stop_headsign: firstStopTime.stop_headsign,
762
- }));
763
- if (!setAsInvisible && canDuplicatePropagatedDelay) {
764
- positionToUpdate.delay = context.lastPositionBeforeTrackDelayed.delay;
765
- }
766
- }
767
- else {
768
- // if there is tracking 2 position with same origin_timestamp then this position is duplicate
769
- const statePosition = tripPositions.positions.find((e) => e.origin_timestamp === position.origin_timestamp && e.tracking === const_1.PositionTrackingEnum.TRACKING)
770
- ? const_1.StatePositionEnum.DUPLICATE
771
- : const_1.StatePositionEnum.AFTER_TRACK;
772
- const lastShapesAnchorPoint = tripPositions.gtfsData.shapes_anchor_points[tripPositions.gtfsData.shapes_anchor_points.length - 1];
773
- const lastStopTime = tripPositions.gtfsData.stop_times[tripPositions.gtfsData.stop_times.length - 1];
774
- // set as invisible if there are some AFTER_TRACK before
775
- const setAsInvisible = DPPUtils_1.default.isInvisible(tripPositions.agency_name_scheduled, position.origin_timestamp, startTimestamp, [position.lng, position.lat], firstShapesAnchorPoint.coordinates, schedule, tripPositions.gtfs_trip_id, context.lastPositionTracking, position.tracking) && statePosition === const_1.StatePositionEnum.AFTER_TRACK;
776
- positionToUpdate = Object.assign({ id: position.id, last_stop_arrival_time: startDayTimestamp + lastShapesAnchorPoint.time_scheduled_seconds * 1000, last_stop_departure_time: startDayTimestamp + lastShapesAnchorPoint.time_scheduled_seconds * 1000, last_stop_id: lastStopTime.stop_id, last_stop_sequence: lastShapesAnchorPoint.last_stop_sequence, shape_dist_traveled: lastShapesAnchorPoint.shape_dist_traveled, state_position: setAsInvisible ? const_1.StatePositionEnum.INVISIBLE : statePosition, state_process: const_1.StateProcessEnum.PROCESSED }, (lastStopTime.stop_headsign && {
777
- last_stop_headsign: lastStopTime.stop_headsign,
778
- }));
779
- }
780
- break;
781
- case PositionHandlerEnum.CANCELED:
782
- positionToUpdate = {
783
- id: position.id,
784
- state_position: const_1.StatePositionEnum.CANCELED,
785
- state_process: const_1.StateProcessEnum.PROCESSED,
786
- };
787
- break;
788
- case PositionHandlerEnum.DO_NOTHING:
789
- break;
790
- default:
791
- break;
792
- }
793
- // if not null push to update
794
- if (positionToUpdate) {
795
- positionToUpdate.valid_to = ValidToCalculator_1.default.getValidToAttribute(positionToUpdate, position, gtfsRouteType, tripPositions.gtfsData, startTimestamp, startDayTimestamp);
796
- computedPositions.push(positionToUpdate);
797
- }
798
- // set if last position is AFTER_TRACK
799
- context.isLastPositionAfterTrack =
800
- (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) === const_1.StatePositionEnum.AFTER_TRACK ||
801
- position.state_position === const_1.StatePositionEnum.AFTER_TRACK;
802
- // set last known BEFORE_TRACK_DELAYED position
803
- if (position.state_position === const_1.StatePositionEnum.BEFORE_TRACK_DELAYED) {
804
- context.lastPositionBeforeTrackDelayed = {
805
- delay: position.delay,
806
- origin_timestamp: position.origin_timestamp,
807
- };
808
- }
809
- // set last position tracking (only for at_stop and on_track)
810
- if ((positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) === const_1.StatePositionEnum.AT_STOP ||
811
- (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) === const_1.StatePositionEnum.ON_TRACK ||
812
- position.state_position === const_1.StatePositionEnum.AT_STOP ||
813
- position.state_position === const_1.StatePositionEnum.ON_TRACK) {
814
- context.lastPositionTracking = turf.point([position.lng, position.lat], Object.assign(Object.assign({}, position), positionToUpdate));
815
- }
816
- // set new first position at stop streak if this stop seqence is set and it is not same as before
817
- if (positionToUpdate
818
- ? positionToUpdate.this_stop_sequence &&
819
- context.atStopStreak.stop_sequence !== positionToUpdate.this_stop_sequence
820
- : position.this_stop_sequence && context.atStopStreak.stop_sequence !== position.this_stop_sequence) {
821
- context.atStopStreak.stop_sequence = positionToUpdate
822
- ? positionToUpdate.this_stop_sequence
823
- : position.this_stop_sequence;
824
- context.atStopStreak.firstPositionTimestamp = position.origin_timestamp;
825
- context.atStopStreak.firstPositionDelay = positionToUpdate ? positionToUpdate.delay : position.delay;
826
- }
827
- // IF currently updated position / position was processed before
828
- // and it is NOT AT_STOP
829
- if (positionToUpdate ? !positionToUpdate.this_stop_sequence : !position.this_stop_sequence) {
830
- // THEN disrupt atStopStreak
831
- context.atStopStreak.stop_sequence = null;
832
- }
833
- // duplicated position should not be considered at all
834
- if ((positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.DUPLICATE &&
835
- (positionToUpdate === null || positionToUpdate === void 0 ? void 0 : positionToUpdate.state_position) !== const_1.StatePositionEnum.INVISIBLE) {
836
- context.lastPositionId = position.id;
837
- context.lastPositionCanceled = position.is_canceled;
838
- context.lastPositionOriginTimestamp = position.origin_timestamp;
839
- }
840
- // set delay
841
- context.lastPositionDelay = positionToUpdate ? positionToUpdate.delay : position.delay;
842
- this.updatePositionsIterator(i + 1, options, schedule, cb);
843
- });
844
- /**
845
- * Returns unix in miliseconds for stop time
846
- * + given by start of day when trip starts
847
- * + and GTFS HH:mm:ss format of stop time in seconds
848
- *
849
- * @param {number} startDayTimestamp - Unix timestamp of local time midnight before trip starts
850
- * @param {number} timeSeconds - Number of seconds given by HH:mm:ss format of GTFS stop time
851
- * @returns {number} - Returns unix milisecods
852
- */
853
- this.getLocalTimeToUnix = (startDayTimestamp, timeSeconds) => {
854
- return (0, moment_timezone_1.default)(startDayTimestamp)
855
- .tz("Europe/Prague")
856
- .startOf("day")
857
- .hours(Math.floor(timeSeconds / 3600))
858
- .minutes(Math.floor((timeSeconds % 3600) / 60))
859
- .seconds(timeSeconds % 60)
860
- .valueOf();
861
- };
862
- /**
863
- * Picks only one closest point for each segment/array of points of shape
864
- *
865
- * @param {IShapeAnchorPoint[][]} ptsInRadius - Feature Point of current position
866
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
867
- * @returns {IShapeAnchorPoint[]} - Result closest point for each segment
868
- */
869
- this.getClosestPoints = (ptsInRadius, currentPosition) => __awaiter(this, void 0, void 0, function* () {
870
- const closestPts = [];
871
- const innerPtsInRadiusIterator = (i, options, cb) => {
872
- // end of iteration
873
- if (ptsInRadius[i].length === options.k) {
874
- return cb(options.nPt);
875
- }
876
- const distance = ruler.distance(currentPosition.geometry.coordinates, ptsInRadius[i][options.k].coordinates);
877
- if (distance < options.nPt.distance) {
878
- options.nPt = ptsInRadius[i][options.k];
879
- options.nPt.distance = distance;
880
- }
881
- // next step
882
- options.k++;
883
- innerPtsInRadiusIterator(i, options, cb);
884
- };
885
- const ptsInRadiusIterator = (i, cb) => {
886
- // end of iteration
887
- if (ptsInRadius.length === i) {
888
- return cb();
889
- }
890
- const nPt = { distance: Infinity };
891
- innerPtsInRadiusIterator(i, {
892
- k: 0,
893
- nPt,
894
- }, (res) => {
895
- // so now you have all possible nearest points on shape
896
- // (could be more if shape line is overlaping itself)
897
- closestPts.push(res);
898
- // next step
899
- ptsInRadiusIterator(i + 1, cb);
900
- });
901
- };
902
- return new Promise((resolve) => {
903
- ptsInRadiusIterator(0, () => {
904
- resolve(closestPts);
905
- });
906
- });
907
- });
908
- /**
909
- * Corrects time delay at stop with dwelling time
910
- *
911
- * @param {number} timeDelay - Initial computed delay in seconds, can be negative for trip ahead
912
- * @param {IPositionsContext | null} context - Context state, holds information about previous positions
913
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
914
- * @param {IShapeAnchorPoint} thisClosestPoint - Closest point of shape anchors
915
- * @param { departureTime: number; arrivalTime: number } stopTimes - departure and arrival stop times in seconds
916
- * @returns {number} - Result delay in seconds, can be negative for trip ahead
917
- */
918
- this.getCorrectedTimeDelay = (timeDelay, context, currentPosition, thisClosestPoint, stopTimes) => {
919
- // compute dwell time in stop, most common is zero
920
- const stopDwellTimeSeconds = stopTimes.departureTime - stopTimes.arrivalTime;
921
- // if dwell time is sheduled as zero, return initial computed delay
922
- if (stopDwellTimeSeconds <= 0) {
923
- return timeDelay;
924
- }
925
- // if last position was not in this same stop or there is no last position at all
926
- if (!context || context.atStopStreak.stop_sequence !== thisClosestPoint.this_stop_sequence) {
927
- // timeDelay >= 0 trip is DELAYED
928
- // we presume it will lower delay by shortening its scheduled dwell time,
929
- // cant go under zero of course, trip should not go ahead
930
- // else trip is AHeAD
931
- // left computed delay as it was
932
- return timeDelay >= 0 ? Math.max(timeDelay - stopDwellTimeSeconds, 0) : timeDelay;
933
- }
934
- // we presume that first position at same stop is real arrival time
935
- if (context.atStopStreak.firstPositionDelay >= 0) {
936
- // trip was DELAYED before
937
- // we presume it will lower delay by shortening its scheduled dwell time,
938
- // cant go under zero of course, trip should not go ahead
939
- return Math.max(timeDelay - stopDwellTimeSeconds, 0);
940
- }
941
- // trip was AHEAD before
942
- // real dwell time so far
943
- const realDwellTimeSeconds = Math.round((currentPosition.properties.origin_timestamp - context.atStopStreak.firstPositionTimestamp) / 1000);
944
- // if real dwell is longer than scheduled, then add to negative delay time
945
- return context.atStopStreak.firstPositionDelay + Math.max(realDwellTimeSeconds - stopDwellTimeSeconds, 0);
946
- };
947
- /**
948
- * Picks only one closest point for multiple possible points based on delay of last position and stop times
949
- *
950
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Feature Point of current position
951
- * @param {IPositionsContext | null} context - Context state, holds information about previous positions
952
- * @param {number} startDayTimestamp - Unix timestamp of start of the day
953
- * @param {IShapeAnchorPoint[]} closestPts - All closest points of possible segments
954
- * @param {IComputationTrip} tripGtfsData - GTFS data and all set of known positions
955
- * @returns {IPositionToUpdate} - Result point as position to update in DB
956
- */
957
- this.getClosestPoint = (currentPosition, context, startDayTimestamp, closestPts, tripGtfsData, gtfsRouteType) => __awaiter(this, void 0, void 0, function* () {
958
- // want to find minimum difference of our prediction, where the bus should be
959
- let minTimeRealDiff = Infinity;
960
- const tripShapePoints = tripGtfsData.shapes_anchor_points;
961
- const tripStopTimes = tripGtfsData.stop_times;
962
- const lastTripShapePoint = tripShapePoints[tripShapePoints.length - 1];
963
- const closestPtsIterator = (i, prevTimeDelay, estimatedPoint, cb) => {
964
- // end of iteration
965
- if (closestPts.length === i) {
966
- return cb(estimatedPoint);
967
- }
968
- let thisClosestPoint = closestPts[i];
969
- const timeScheduledTimestamp = this.getLocalTimeToUnix(startDayTimestamp,
970
- // take stop arrival time if is point at stop
971
- thisClosestPoint.this_stop_sequence
972
- ? tripStopTimes[thisClosestPoint.this_stop_sequence - 1].arrival_time_seconds
973
- : thisClosestPoint.time_scheduled_seconds);
974
- let timeDelay = moment_timezone_1.default
975
- .utc(currentPosition.properties.origin_timestamp)
976
- .diff(timeScheduledTimestamp, "seconds");
977
- // time where the bus should on this point
978
- const timeProposed = thisClosestPoint.time_scheduled_seconds + prevTimeDelay;
979
- // difference
980
- const timeRealDiff = thisClosestPoint.time_scheduled_seconds + timeDelay - timeProposed;
981
- // we look for the best fitting point
982
- if (Math.abs(timeRealDiff) < Math.abs(minTimeRealDiff)) {
983
- const isBusOrTram = gtfsRouteType === models_2.GTFSRouteTypeEnum.BUS || gtfsRouteType === models_2.GTFSRouteTypeEnum.TRAM;
984
- minTimeRealDiff = timeRealDiff;
985
- // push trip into nearest at_stop shape anchor point if trip "P" event (tram) / "V" event (bus)
986
- if (isBusOrTram &&
987
- currentPosition.properties.tcp_event === const_1.TCPEventEnum.ARRIVAL_ANNOUNCED &&
988
- thisClosestPoint.this_stop_sequence === null) {
989
- let movingShapePointIndex = thisClosestPoint.index;
990
- while (thisClosestPoint.this_stop_sequence === null && movingShapePointIndex < lastTripShapePoint.index) {
991
- movingShapePointIndex++;
992
- thisClosestPoint = tripShapePoints[movingShapePointIndex];
993
- }
994
- }
995
- let statePosition = thisClosestPoint.this_stop_sequence ? const_1.StatePositionEnum.AT_STOP : const_1.StatePositionEnum.ON_TRACK;
996
- // test if TCP trip with DEPARTURED event should not bet at stop (not applied for last stop)
997
- if (currentPosition.properties.tcp_event === const_1.TCPEventEnum.DEPARTURED &&
998
- thisClosestPoint.this_stop_sequence !== lastTripShapePoint.this_stop_sequence) {
999
- statePosition = const_1.StatePositionEnum.ON_TRACK;
1000
- }
1001
- // interpret the position state on the "V" event for trams and buses as "after track"
1002
- if (isBusOrTram && currentPosition.properties.tcp_event === const_1.TCPEventEnum.V) {
1003
- statePosition = const_1.StatePositionEnum.AFTER_TRACK;
1004
- thisClosestPoint = lastTripShapePoint;
1005
- timeDelay = null;
1006
- }
1007
- // lets correct delay if it is at stop
1008
- if (thisClosestPoint.this_stop_sequence && timeDelay) {
1009
- timeDelay = this.getCorrectedTimeDelay(timeDelay, context, currentPosition, thisClosestPoint, {
1010
- departureTime: tripStopTimes[thisClosestPoint.this_stop_sequence - 1].departure_time_seconds,
1011
- arrivalTime: tripStopTimes[thisClosestPoint.this_stop_sequence - 1].arrival_time_seconds,
1012
- });
1013
- }
1014
- // save it for result
1015
- estimatedPoint = {
1016
- id: currentPosition.properties.id,
1017
- bearing: thisClosestPoint.bearing,
1018
- shape_dist_traveled: Math.round(thisClosestPoint.shape_dist_traveled * 1000) / 1000,
1019
- next_stop_id: tripStopTimes[thisClosestPoint.next_stop_sequence - 1].stop_id,
1020
- last_stop_id: tripStopTimes[thisClosestPoint.last_stop_sequence - 1].stop_id,
1021
- next_stop_name: tripStopTimes[thisClosestPoint.next_stop_sequence - 1].stop.stop_name,
1022
- last_stop_name: tripStopTimes[thisClosestPoint.last_stop_sequence - 1].stop.stop_name,
1023
- next_stop_sequence: thisClosestPoint.next_stop_sequence,
1024
- last_stop_sequence: thisClosestPoint.last_stop_sequence,
1025
- next_stop_arrival_time: this.getLocalTimeToUnix(startDayTimestamp, tripStopTimes[thisClosestPoint.next_stop_sequence - 1].arrival_time_seconds),
1026
- last_stop_arrival_time: this.getLocalTimeToUnix(startDayTimestamp, tripStopTimes[thisClosestPoint.last_stop_sequence - 1].arrival_time_seconds),
1027
- next_stop_departure_time: this.getLocalTimeToUnix(startDayTimestamp, tripStopTimes[thisClosestPoint.next_stop_sequence - 1].departure_time_seconds),
1028
- last_stop_departure_time: this.getLocalTimeToUnix(startDayTimestamp, tripStopTimes[thisClosestPoint.last_stop_sequence - 1].departure_time_seconds),
1029
- delay: timeDelay,
1030
- this_stop_id: (statePosition === const_1.StatePositionEnum.AT_STOP &&
1031
- thisClosestPoint.this_stop_sequence &&
1032
- tripStopTimes[thisClosestPoint.this_stop_sequence - 1].stop_id) ||
1033
- undefined,
1034
- this_stop_name: (statePosition === const_1.StatePositionEnum.AT_STOP &&
1035
- thisClosestPoint.this_stop_sequence &&
1036
- tripStopTimes[thisClosestPoint.this_stop_sequence - 1].stop.stop_name) ||
1037
- undefined,
1038
- this_stop_sequence: (statePosition === const_1.StatePositionEnum.AT_STOP && thisClosestPoint.this_stop_sequence) || undefined,
1039
- last_stop_headsign: tripStopTimes[thisClosestPoint.last_stop_sequence - 1].stop_headsign || undefined,
1040
- state_position: statePosition,
1041
- state_process: const_1.StateProcessEnum.PROCESSED,
1042
- valid_to: ValidToCalculator_1.default.getDefaultValidToAttribute(currentPosition.properties.origin_timestamp),
1043
- };
1044
- }
1045
- // next step
1046
- closestPtsIterator(i + 1, prevTimeDelay, estimatedPoint, cb);
1047
- };
1048
- return new Promise((resolve) => {
1049
- var _a, _b;
1050
- closestPtsIterator(0, (_b = (_a = context === null || context === void 0 ? void 0 : context.lastPositionTracking) === null || _a === void 0 ? void 0 : _a.properties.delay) !== null && _b !== void 0 ? _b : 0, null, (estimatedPoint) => {
1051
- resolve(estimatedPoint);
1052
- });
1053
- });
1054
- });
1055
- /**
1056
- * Returns estimate of point on shape, where the trip should be with appropriate delay
1057
- *
1058
- * @param {IShapeAnchorPoint[]} tripShapePoints - Precalculated trip shape equidistant points with scheduled times
1059
- * @param {Feature<Point, ICurrentPositionProperties>} currentPosition - Current position of trip
1060
- * @param {IPositionsContext | null} context - Context state, holds information about previous positions
1061
- * @param {number} startDayTimestamp - Unix timestamp of midnight before trip starts
1062
- * @returns {IPositionToUpdate} - Position object to update
1063
- */
1064
- this.getEstimatedPoint = (tripGtfsData, currentPosition, context, startDayTimestamp, gtfsRouteType) => __awaiter(this, void 0, void 0, function* () {
1065
- var _e, _f, _g, _h, _j, _k;
1066
- // init radius around GPS position ( 200 meters radius, 16 points polygon aka circle)
1067
- const radius = 0.2;
1068
- //Initial value
1069
- let estimatedPoint = Object.assign({ id: currentPosition.properties.id, state_position: const_1.StatePositionEnum.OFF_TRACK, state_process: const_1.StateProcessEnum.PROCESSED, tcp_event: currentPosition.properties.tcp_event, valid_to: ValidToCalculator_1.default.getDefaultValidToAttribute(currentPosition.properties.origin_timestamp) }, (context &&
1070
- ((_e = context.lastPositionTracking) === null || _e === void 0 ? void 0 : _e.properties.last_stop_sequence) && {
1071
- shape_dist_traveled: (_f = context.lastPositionTracking) === null || _f === void 0 ? void 0 : _f.properties.shape_dist_traveled,
1072
- last_stop_arrival_time: (_g = context.lastPositionTracking) === null || _g === void 0 ? void 0 : _g.properties.last_stop_arrival_time,
1073
- last_stop_departure_time: (_h = context.lastPositionTracking) === null || _h === void 0 ? void 0 : _h.properties.last_stop_departure_time,
1074
- last_stop_sequence: (_j = context.lastPositionTracking) === null || _j === void 0 ? void 0 : _j.properties.last_stop_sequence,
1075
- last_stop_id: (_k = context.lastPositionTracking) === null || _k === void 0 ? void 0 : _k.properties.last_stop_id,
1076
- }));
1077
- // returns array of segments (row of points) of shape anchor points around gps
1078
- const ptsInRadius = yield PositionCalculator_1.default.getShapePointsAroundPosition(tripGtfsData.shapes_anchor_points, currentPosition, radius, tripGtfsData.trip_id);
1079
- if (ptsInRadius.length < 1) {
1080
- return estimatedPoint;
1081
- }
1082
- // picks only closest point for each segment
1083
- const closestPts = yield this.getClosestPoints(ptsInRadius, currentPosition);
1084
- if (closestPts.length < 1) {
1085
- return estimatedPoint;
1086
- }
1087
- // DECIDE WHICH POINT IS PROBABLY RIGHT
1088
- return this.getClosestPoint(currentPosition, context, startDayTimestamp, closestPts, tripGtfsData, gtfsRouteType);
1089
- });
1090
638
  /**
1091
639
  * Returns shape anchor points along trip track shape with stop times parameters
1092
640
  *