@wemap/osm 5.2.0 → 5.4.1

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.
@@ -0,0 +1,227 @@
1
+ /* eslint-disable max-depth */
2
+ /* eslint-disable max-statements */
3
+ import { Coordinates } from '@wemap/geo';
4
+
5
+ import Itinerary from '../Itinerary.js';
6
+ import Leg from '../Leg.js';
7
+ import RouterResponse from '../RouterResponse.js';
8
+ import Step from '../Step.js';
9
+ import { generateStepsMetadata } from '../Utils.js';
10
+
11
+ /**
12
+ * @param {object} json
13
+ * @returns {Coordinates}
14
+ */
15
+ export function jsonToCoordinates(json) {
16
+ return new Coordinates(json.Lat, json.Long);
17
+ }
18
+
19
+ /**
20
+ * @param {string} jsonDate
21
+ * @returns {number}
22
+ */
23
+ function jsonDateToTimestamp(jsonDate) {
24
+ const [dateStr, timeStr] = jsonDate.split(' ');
25
+ const [dayStr, monthStr, yearStr] = dateStr.split('/');
26
+ const [hoursStr, minutesStr, secondsStr] = timeStr.split(':');
27
+ const date = new Date(
28
+ Number(yearStr), Number(monthStr) - 1, Number(dayStr),
29
+ Number(hoursStr), Number(minutesStr), Number(secondsStr)
30
+ );
31
+ return date.getTime();
32
+ }
33
+
34
+ /**
35
+ * @param {string} wktGeometry
36
+ * @returns {Coordinates[]}
37
+ */
38
+ function parseWKTGeometry(wktGeometry) {
39
+ const tmpCoordsStr = wktGeometry.match(/LINESTRING \((.*)\)/i);
40
+ if (!tmpCoordsStr) {
41
+ return null;
42
+ }
43
+ return tmpCoordsStr[1].split(',').map(str => {
44
+ const sp = str.trim().split(' ');
45
+ return new Coordinates(Number(sp[1]), Number(sp[0]));
46
+ });
47
+ }
48
+
49
+ /**
50
+ * @param {string} iso8601Duration
51
+ * @see https://stackoverflow.com/a/29153059/2239938
52
+ */
53
+ function parseDuration(iso8601Duration) {
54
+ const iso8601DurationRegex = /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/;
55
+
56
+ var matches = iso8601Duration.match(iso8601DurationRegex);
57
+
58
+ // const sign = typeof matches[1] === 'undefined' ? '+' : '-',
59
+ const years = typeof matches[2] === 'undefined' ? 0 : Number(matches[2]);
60
+ const months = typeof matches[3] === 'undefined' ? 0 : Number(matches[3]);
61
+ const weeks = typeof matches[4] === 'undefined' ? 0 : Number(matches[4]);
62
+ const days = typeof matches[5] === 'undefined' ? 0 : Number(matches[5]);
63
+ const hours = typeof matches[6] === 'undefined' ? 0 : Number(matches[6]);
64
+ const minutes = typeof matches[7] === 'undefined' ? 0 : Number(matches[7]);
65
+ const seconds = typeof matches[8] === 'undefined' ? 0 : Number(matches[8]);
66
+
67
+ return seconds
68
+ + minutes * 60
69
+ + hours * 3600
70
+ + days * 86400
71
+ + weeks * (86400 * 7)
72
+ + months * (86400 * 30)
73
+ + years * (86400 * 365.25);
74
+ }
75
+
76
+ /**
77
+ * Generate multi itineraries from Cityway JSON
78
+ * @param {object} json JSON file provided by Cityway.
79
+ * @returns {?RouterResponse}
80
+ * @example https://preprod.api.lia2.cityway.fr/journeyplanner/api/opt/PlanTrips/json?DepartureLatitude=49.51509388236216&DepartureLongitude=0.09341749619366316&ArrivalLatitude=49.5067090188444&ArrivalLongitude=0.1694842115417831&DepartureType=COORDINATES&ArrivalType=COORDINATES
81
+ */
82
+ export function createRouterResponseFromJson(json) {
83
+
84
+ if (json.StatusCode !== 200 || !json.Data || !json.Data.length) {
85
+ return null;
86
+ }
87
+
88
+ const routerResponse = new RouterResponse();
89
+ routerResponse.routerName = 'cityway';
90
+
91
+
92
+ // For the moment, take the first of the Data array.
93
+ const jsonResponse = json.Data[0].response;
94
+
95
+ for (const trip of jsonResponse.trips.Trip) {
96
+
97
+ const itinerary = new Itinerary();
98
+
99
+ itinerary.duration = parseDuration(trip.Duration);
100
+ itinerary.startTime = jsonDateToTimestamp(trip.Departure.Time);
101
+ itinerary.from = jsonToCoordinates(trip.Departure.Site.Position);
102
+ itinerary.endTime = jsonDateToTimestamp(trip.Arrival.Time);
103
+ itinerary.to = jsonToCoordinates(trip.Arrival.Site.Position);
104
+ routerResponse.itineraries.push(itinerary);
105
+
106
+ for (const jsonSection of trip.sections.Section) {
107
+
108
+ const jsonLeg = jsonSection.Leg ? jsonSection.Leg : jsonSection.PTRide;
109
+
110
+ const leg = new Leg();
111
+
112
+ leg.mode = jsonLeg.TransportMode;
113
+ leg.duration = parseDuration(jsonLeg.Duration);
114
+ leg.startTime = jsonDateToTimestamp(jsonLeg.Departure.Time);
115
+ leg.endTime = jsonDateToTimestamp(jsonLeg.Arrival.Time);
116
+ leg.coords = [];
117
+
118
+ if (leg.mode === 'WALK') {
119
+
120
+ leg.from = {
121
+ name: jsonLeg.Departure.Site.Name,
122
+ coords: jsonToCoordinates(jsonLeg.Departure.Site.Position)
123
+ };
124
+ leg.to = {
125
+ name: jsonLeg.Arrival.Site.Name,
126
+ coords: jsonToCoordinates(jsonLeg.Arrival.Site.Position)
127
+ };
128
+
129
+ leg.steps = [];
130
+ for (const jsonPathLink of jsonLeg.pathLinks.PathLink) {
131
+ const step = new Step();
132
+ let stepCoords;
133
+ if (jsonPathLink.Geometry) {
134
+ stepCoords = parseWKTGeometry(jsonPathLink.Geometry);
135
+ } else {
136
+ stepCoords = [leg.from.coords, leg.to.coords];
137
+ }
138
+ step.coords = stepCoords[0];
139
+ step._idCoordsInLeg = leg.coords.length;
140
+ stepCoords.forEach((coords, idx) => {
141
+ if (
142
+ idx !== 0
143
+ || leg.coords.length === 0
144
+ || !leg.coords[leg.coords.length - 1].equalsTo(coords)
145
+ ) {
146
+ leg.coords.push(coords);
147
+ }
148
+ });
149
+
150
+
151
+ step.name = jsonPathLink.Departure.Site.Name;
152
+ step.levelChange = null;
153
+
154
+ step.distance = jsonPathLink.Distance;
155
+
156
+ leg.steps.push(step);
157
+ }
158
+
159
+ } else if (leg.mode === 'BUS' || leg.mode === 'TRAM') {
160
+
161
+ leg.from = {
162
+ name: jsonLeg.Departure.StopPlace.Name,
163
+ coords: jsonToCoordinates(jsonLeg.Departure.StopPlace.Position)
164
+ };
165
+ leg.to = {
166
+ name: jsonLeg.Arrival.StopPlace.Name,
167
+ coords: jsonToCoordinates(jsonLeg.Arrival.StopPlace.Position)
168
+ };
169
+
170
+ leg.transportInfo = {
171
+ name: jsonLeg.Line.Number,
172
+ routeColor: jsonLeg.Line.Color,
173
+ routeTextColor: jsonLeg.Line.TextColor,
174
+ directionName: jsonLeg.Destination
175
+ };
176
+
177
+ for (const jsonStep of jsonLeg.steps.Step) {
178
+ const stepCoords = parseWKTGeometry(jsonStep.Geometry);
179
+ stepCoords.forEach((coords, idx) => {
180
+ if (
181
+ idx !== 0
182
+ || leg.coords.length === 0
183
+ || !leg.coords[leg.coords.length - 1].equalsTo(coords)
184
+ ) {
185
+ leg.coords.push(coords);
186
+ }
187
+ });
188
+ }
189
+
190
+ const legStep = new Step();
191
+ legStep.coords = leg.coords[0];
192
+ legStep._idCoordsInLeg = 0;
193
+ legStep.name = jsonLeg.Line.Name;
194
+ legStep.levelChange = null;
195
+ legStep.distance = jsonLeg.Distance;
196
+ leg.steps = [legStep];
197
+ }
198
+
199
+ leg.distance = leg.coords.reduce((acc, coords, idx, arr) => {
200
+ if (idx === 0) {
201
+ return acc;
202
+ }
203
+ return acc + arr[idx - 1].distanceTo(coords);
204
+ }, 0);
205
+
206
+ itinerary.legs.push(leg);
207
+
208
+ }
209
+
210
+ itinerary.distance = itinerary.coords.reduce((acc, coords, idx, arr) => {
211
+ if (idx === 0) {
212
+ return acc;
213
+ }
214
+ return acc + arr[idx - 1].distanceTo(coords);
215
+ }, 0);
216
+
217
+ // All legs have to be parsed before computing steps metadata
218
+ generateStepsMetadata(itinerary);
219
+ }
220
+
221
+ routerResponse.from = routerResponse.itineraries[0].from;
222
+ routerResponse.to = routerResponse.itineraries[routerResponse.itineraries.length - 1].to;
223
+
224
+ return routerResponse;
225
+ }
226
+
227
+
@@ -0,0 +1,65 @@
1
+ /* eslint-disable max-statements */
2
+ import chai from 'chai';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ import { Coordinates } from '@wemap/geo';
8
+
9
+ import { createRouterResponseFromJson } from './CitywayUtils.js';
10
+
11
+ import { verifyRouterResponseData } from '../RouterResponse.type.spec.js';
12
+
13
+
14
+ const { expect } = chai;
15
+
16
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
+
18
+ describe('CitywayUtils - createRouterResponseFromJson', () => {
19
+
20
+ it('RouterResponse - 1', () => {
21
+
22
+ const filePath = path.resolve(__dirname, '../../../assets/itinerary-lehavre-cityway.json');
23
+ const fileString = fs.readFileSync(filePath, 'utf8');
24
+ const json = JSON.parse(fileString);
25
+
26
+ const routerResponse = createRouterResponseFromJson(json);
27
+ verifyRouterResponseData(routerResponse);
28
+
29
+ expect(routerResponse.routerName).equal('cityway');
30
+ expect(routerResponse.itineraries.length).equal(3);
31
+ expect(routerResponse.from.equalsTo(new Coordinates(49.515093882362159, 0.093417496193663158))).true;
32
+ expect(routerResponse.to.equalsTo(new Coordinates(49.5067090188444, 0.16948421154178309))).true;
33
+
34
+ const itinerary1 = routerResponse.itineraries[0];
35
+ expect(itinerary1.distance).to.be.closeTo(6887, 1);
36
+ expect(itinerary1.duration).equal(2379);
37
+ // Do not work because of the input time format
38
+ // expect(itinerary1.startTime).equal(1620659156000);
39
+ // expect(itinerary1.endTime).equal(1620661535000);
40
+ expect(itinerary1.legs.length).equal(5);
41
+
42
+ const itinerary1leg1 = itinerary1.legs[0];
43
+ // Do not work because of the input time format
44
+ // expect(itinerary1leg1.startTime).equal(1620659156000);
45
+ // expect(itinerary1leg1.endTime).equal(1620659340000);
46
+ expect(itinerary1leg1.distance).to.be.closeTo(200.14, 0.1);
47
+ expect(itinerary1leg1.mode).equal('WALK');
48
+ expect(itinerary1leg1.transportInfo).is.null;
49
+ expect(itinerary1leg1.from.name).equal('RUE DU QUARTIER NEUF');
50
+ expect(itinerary1leg1.from.coords.equalsTo(new Coordinates(49.515093882362159, 0.093417496193663158))).true;
51
+ expect(itinerary1leg1.to.name).equal('T. Gautier');
52
+ expect(itinerary1leg1.to.coords.equalsTo(new Coordinates(49.5147550229, 0.0911025378))).true;
53
+
54
+ const itinerary1leg2 = itinerary1.legs[1];
55
+ expect(itinerary1leg2.mode).equal('BUS');
56
+ expect(itinerary1leg2.transportInfo).is.not.null;
57
+ expect(itinerary1leg2.transportInfo.name).equal('03');
58
+ expect(itinerary1leg2.transportInfo.routeColor).equal('3FA435');
59
+ expect(itinerary1leg2.transportInfo.routeTextColor).is.null;
60
+ expect(itinerary1leg2.transportInfo.directionName).equal('Graville');
61
+ });
62
+
63
+
64
+ });
65
+