@wemap/routers 12.10.8 → 12.11.0
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.
- package/dist/helpers/InstructionManager.d.ts +20 -0
- package/dist/helpers/InstructionManagerV1.d.ts +7 -0
- package/{index.ts → dist/index.d.ts} +1 -11
- package/dist/index.js +1 -5099
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +100 -5
- package/dist/index.mjs.map +1 -1
- package/dist/src/ItineraryInfoManager.d.ts +31 -0
- package/dist/src/RoutingError.d.ts +24 -0
- package/{src/StatusCode.ts → dist/src/StatusCode.d.ts} +2 -3
- package/dist/src/Utils.d.ts +6 -0
- package/dist/src/graph/Edge.d.ts +38 -0
- package/dist/src/graph/Graph.d.ts +40 -0
- package/dist/src/graph/GraphProjection.d.ts +11 -0
- package/dist/src/graph/GraphProjectionOptions.d.ts +7 -0
- package/dist/src/graph/GraphRoute.d.ts +19 -0
- package/dist/src/graph/GraphRouter.d.ts +20 -0
- package/dist/src/graph/GraphRouterEngine.d.ts +80 -0
- package/{src/graph/GraphRouterOptions.ts → dist/src/graph/GraphRouterOptions.d.ts} +6 -7
- package/dist/src/graph/GraphRouterOptionsBuilder.d.ts +23 -0
- package/dist/src/graph/GraphRouterOptionsFactors.d.ts +8 -0
- package/dist/src/graph/NoRouteFoundError.d.ts +12 -0
- package/dist/src/graph/Vertex.d.ts +19 -0
- package/dist/src/model/Itinerary.d.ts +81 -0
- package/dist/src/model/Leg.d.ts +66 -0
- package/dist/src/model/LevelChange.d.ts +7 -0
- package/dist/src/model/RouterRequest.d.ts +42 -0
- package/dist/src/model/Step.d.ts +42 -0
- package/dist/src/model/StepExtra.d.ts +3 -0
- package/dist/src/model/StepsBuilder.d.ts +16 -0
- package/dist/src/model/TransitMode.d.ts +5 -0
- package/{src/model/TravelMode.ts → dist/src/model/TravelMode.d.ts} +1 -2
- package/dist/src/model/generateSteps.d.ts +0 -0
- package/{src/remote/RemoteRouter.ts → dist/src/remote/RemoteRouter.d.ts} +2 -7
- package/dist/src/remote/RemoteRouterManager.d.ts +731 -0
- package/dist/src/remote/RemoteRouterUtils.d.ts +6 -0
- package/dist/src/remote/cityway/CitywayRemoteRouter.d.ts +109 -0
- package/dist/src/remote/deutsche-bahn/DeutscheBahnRemoteRouter.d.ts +31 -0
- package/dist/src/remote/geovelo/GeoveloRemoteRouter.d.ts +106 -0
- package/dist/src/remote/idfm/IdfmRemoteRouter.d.ts +123 -0
- package/dist/src/remote/navitia/NavitiaRemoteRouter.d.ts +34 -0
- package/dist/src/remote/navitia/types.d.ts +87 -0
- package/dist/src/remote/osrm/OsrmRemoteRouter.d.ts +70 -0
- package/dist/src/remote/otp/OtpRemoteRouter.d.ts +69 -0
- package/dist/src/remote/wemap-multi/WemapMultiRemoteRouter.d.ts +19 -0
- package/dist/src/types.d.ts +31 -0
- package/dist/src/wemap-multi/CustomGraphMap.d.ts +56 -0
- package/dist/src/wemap-multi/CustomGraphMapTester.d.ts +39 -0
- package/dist/src/wemap-multi/WemapMultiRouter.d.ts +14 -0
- package/dist/src/wemap-osm/OsmGraphUtils.d.ts +11 -0
- package/dist/tests/CommonTest.d.ts +8 -0
- package/package.json +11 -8
- package/assets/biocbon-bergere-rdc-network.osm +0 -163
- package/assets/bureaux-wemap-montpellier-network.osm +0 -174
- package/assets/components.osm +0 -146
- package/assets/elevator-models-4.osm +0 -89
- package/assets/elevator-models.osm +0 -354
- package/assets/exit-graph.osm +0 -25
- package/assets/gare-de-lest-network-pp-bounds.osm +0 -1613
- package/assets/gare-de-lyon-extract.osm +0 -174
- package/assets/geovelo-montpellier.json +0 -1144
- package/assets/horizontal-elevator.osm +0 -12
- package/assets/itinerary-deutsche-bahn-1.json +0 -368
- package/assets/itinerary-grenoble-otp-1.json +0 -1536
- package/assets/itinerary-grenoble-otp-2.json +0 -1092
- package/assets/itinerary-info-two-points-proj.osm +0 -39
- package/assets/itinerary-info-two-points.osm +0 -24
- package/assets/itinerary-lehavre-cityway-1.json +0 -6799
- package/assets/itinerary-lehavre-cityway-2.json +0 -2133
- package/assets/itinerary-lehavre-cityway-3.json +0 -12577
- package/assets/itinerary-lehavre-cityway-4.json +0 -1451
- package/assets/itinerary-lehavre-cityway-5.json +0 -5925
- package/assets/itinerary-lemans-navitia.json +0 -7768
- package/assets/itinerary-montpellier-osrm-3.json +0 -185
- package/assets/itinerary-montpellier-outdoor-without-steps.json +0 -110
- package/assets/itinerary-montpellier-outdoor.json +0 -513
- package/assets/itinerary-paris-idfm-2.json +0 -1838
- package/assets/itinerary-paris-idfm.json +0 -27727
- package/assets/itinerary-step-not-on-path-osrm.json +0 -457
- package/assets/itinerary-with-duplicate-nodes.json +0 -110
- package/assets/network-conveying-backward.osm +0 -74
- package/assets/network-elevator.osm +0 -48
- package/assets/network-escalators.osm +0 -50
- package/assets/network-simple.osm +0 -27
- package/assets/network-steps-same-level.osm +0 -283
- package/assets/network-with-modifiers.osm +0 -39
- package/assets/one-way.osm +0 -46
- package/assets/report-map-1.osm +0 -36
- package/assets/report-map-2.osm +0 -29
- package/assets/report-map-3.osm +0 -15
- package/assets/rr-wemap-multi-indoor-outdoor-indoor.json +0 -1352
- package/assets/rr-wemap-multi-indoor-outdoor.json +0 -145
- package/assets/rr-wemap-multi-multi-level.json +0 -262
- package/assets/rr-wemap-multi-outdoor-indoor.json +0 -145
- package/assets/rr-wemap-multi-outdoor-outdoor.json +0 -207
- package/assets/rr-wemap-multi-remote-indoor-indoor.json +0 -155
- package/assets/rr-wemap-multi-remote-indoor-outdoor-indoor.json +0 -668
- package/assets/rr-wemap-multi-remote-indoor-outdoor.json +0 -154
- package/assets/rr-wemap-multi-remote-outdoor-indoor.json +0 -179
- package/assets/rr-wemap-multi-remote-outdoor-outdoor.json +0 -109
- package/assets/stairs-and-exit.osm +0 -47
- package/helpers/InstructionManager.ts +0 -184
- package/helpers/InstructionManagerV1.ts +0 -95
- package/src/ItineraryInfoManager.spec.ts +0 -183
- package/src/ItineraryInfoManager.ts +0 -181
- package/src/RoutingError.ts +0 -60
- package/src/Utils.ts +0 -8
- package/src/graph/Edge.spec.ts +0 -32
- package/src/graph/Edge.ts +0 -64
- package/src/graph/Graph.spec.ts +0 -509
- package/src/graph/Graph.ts +0 -272
- package/src/graph/GraphProjection.ts +0 -15
- package/src/graph/GraphProjectionOptions.ts +0 -8
- package/src/graph/GraphRoute.spec.ts +0 -15
- package/src/graph/GraphRoute.ts +0 -43
- package/src/graph/GraphRouter.spec.ts +0 -317
- package/src/graph/GraphRouter.ts +0 -229
- package/src/graph/GraphRouterEngine.ts +0 -248
- package/src/graph/GraphRouterOptionsBuilder.ts +0 -98
- package/src/graph/NoRouteFoundError.ts +0 -39
- package/src/graph/Vertex.spec.ts +0 -42
- package/src/graph/Vertex.ts +0 -45
- package/src/model/Itinerary.spec.ts +0 -134
- package/src/model/Itinerary.ts +0 -370
- package/src/model/Leg.spec.ts +0 -107
- package/src/model/Leg.ts +0 -224
- package/src/model/LevelChange.spec.ts +0 -50
- package/src/model/LevelChange.ts +0 -14
- package/src/model/RouterRequest.ts +0 -33
- package/src/model/Step.spec.ts +0 -99
- package/src/model/Step.ts +0 -90
- package/src/model/StepExtra.ts +0 -1
- package/src/model/StepsBuilder.ts +0 -242
- package/src/model/TransitMode.spec.ts +0 -31
- package/src/model/TransitMode.ts +0 -28
- package/src/model/generateSteps.ts +0 -102
- package/src/remote/RemoteRouterManager.spec.ts +0 -178
- package/src/remote/RemoteRouterManager.ts +0 -72
- package/src/remote/RemoteRouterUtils.ts +0 -25
- package/src/remote/cityway/CitywayRemoteRouter.spec.ts +0 -122
- package/src/remote/cityway/CitywayRemoteRouter.ts +0 -435
- package/src/remote/deutsche-bahn/DeutscheBahnRemoteRouter.spec.ts +0 -52
- package/src/remote/deutsche-bahn/DeutscheBahnRemoteRouter.ts +0 -85
- package/src/remote/geovelo/GeoveloRemoteRouter.spec.ts +0 -54
- package/src/remote/geovelo/GeoveloRemoteRouter.ts +0 -293
- package/src/remote/idfm/IdfmRemoteRouter.spec.ts +0 -102
- package/src/remote/idfm/IdfmRemoteRouter.ts +0 -523
- package/src/remote/navitia/NavitiaRemoteRouter.spec.ts +0 -116
- package/src/remote/navitia/NavitiaRemoteRouter.ts +0 -445
- package/src/remote/navitia/types.ts +0 -73
- package/src/remote/osrm/OsrmRemoteRouter.spec.ts +0 -127
- package/src/remote/osrm/OsrmRemoteRouter.ts +0 -303
- package/src/remote/otp/OtpRemoteRouter.spec.ts +0 -103
- package/src/remote/otp/OtpRemoteRouter.ts +0 -223
- package/src/remote/wemap-multi/WemapMultiRemoteRouter.spec.ts +0 -103
- package/src/remote/wemap-multi/WemapMultiRemoteRouter.ts +0 -56
- package/src/types.ts +0 -32
- package/src/wemap-multi/CustomGraphMap.spec.ts +0 -40
- package/src/wemap-multi/CustomGraphMap.ts +0 -213
- package/src/wemap-multi/CustomGraphMapTester.spec.ts +0 -48
- package/src/wemap-multi/CustomGraphMapTester.ts +0 -90
- package/src/wemap-multi/WemapMultiRouter.spec.ts +0 -138
- package/src/wemap-multi/WemapMultiRouter.ts +0 -329
- package/src/wemap-osm/OsmGraphUtils.spec.ts +0 -165
- package/src/wemap-osm/OsmGraphUtils.ts +0 -173
- package/src/wemap-osm/OsmRouter.elevators.spec.ts +0 -106
- package/src/wemap-osm/OsmRouter.spec.ts +0 -292
- package/tests/CommonTest.ts +0 -78
- package/tsconfig.json +0 -3
- package/vite.config.ts +0 -4
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
/* eslint-disable max-statements */
|
|
2
|
-
|
|
3
|
-
import { Coordinates, Level } from '@wemap/geo';
|
|
4
|
-
import { rad2deg, positiveMod } from '@wemap/maths';
|
|
5
|
-
import { LineString, Position } from 'geojson';
|
|
6
|
-
|
|
7
|
-
import Itinerary from '../../model/Itinerary.js';
|
|
8
|
-
import Leg from '../../model/Leg.js';
|
|
9
|
-
import StepsBuilder from '../../model/StepsBuilder.js';
|
|
10
|
-
import RemoteRouter from '../RemoteRouter.js';
|
|
11
|
-
import { type TravelMode } from '../../model/TravelMode.js';
|
|
12
|
-
import { type TransitMode } from '../../model/TransitMode.js';
|
|
13
|
-
import { type RouterRequest } from '../../model/RouterRequest.js';
|
|
14
|
-
import { RemoteRoutingError } from '../../RoutingError.js';
|
|
15
|
-
|
|
16
|
-
type OsrmCoordinates = Position;
|
|
17
|
-
type OsrmModifier = 'sharp right' | 'sharp left' | 'slight right'
|
|
18
|
-
| 'slight left' | 'right' | 'left' | 'u turn' | 'straight';
|
|
19
|
-
type OsrmManeuverType = 'depart' | 'turn' | 'arrive';
|
|
20
|
-
type OsrmStep = {
|
|
21
|
-
geometry: LineString,
|
|
22
|
-
distance: number,
|
|
23
|
-
duration: number,
|
|
24
|
-
name?: string,
|
|
25
|
-
maneuver: {
|
|
26
|
-
bearing_before: number,
|
|
27
|
-
bearing_after: number,
|
|
28
|
-
location: OsrmCoordinates,
|
|
29
|
-
modifier: OsrmModifier,
|
|
30
|
-
type: OsrmManeuverType
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
type OsrmJson = {
|
|
34
|
-
code?: string,
|
|
35
|
-
message?: string,
|
|
36
|
-
routes?: {
|
|
37
|
-
geometry: LineString,
|
|
38
|
-
legs: {
|
|
39
|
-
distance: number,
|
|
40
|
-
duration: number,
|
|
41
|
-
steps: OsrmStep[]
|
|
42
|
-
}[],
|
|
43
|
-
distance: number,
|
|
44
|
-
duration: number,
|
|
45
|
-
weight_name: string,
|
|
46
|
-
weight: number
|
|
47
|
-
}[],
|
|
48
|
-
waypoints?: []
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
type OsrmMode = 'driving' | 'walking' | 'bike' | 'pmr' | 'bike-safest' | 'bike-fastest';
|
|
52
|
-
|
|
53
|
-
const outputModeCorrespondance = new Map<TravelMode, TransitMode>();
|
|
54
|
-
outputModeCorrespondance.set('CAR', 'CAR');
|
|
55
|
-
outputModeCorrespondance.set('WALK', 'WALK');
|
|
56
|
-
outputModeCorrespondance.set('BIKE', 'BIKE');
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Singleton.
|
|
60
|
-
*/
|
|
61
|
-
class OsrmRemoteRouter extends RemoteRouter {
|
|
62
|
-
|
|
63
|
-
get rname() { return 'osrm' as const; }
|
|
64
|
-
|
|
65
|
-
async getItineraries(endpointUrl: string, routerRequest: RouterRequest) {
|
|
66
|
-
const url = this.getURL(endpointUrl, routerRequest);
|
|
67
|
-
|
|
68
|
-
const res = await (fetch(url).catch(() => {
|
|
69
|
-
throw RemoteRoutingError.unreachableServer(this.rname, url);
|
|
70
|
-
}));
|
|
71
|
-
|
|
72
|
-
const jsonResponse = await res.json().catch(() => {
|
|
73
|
-
throw RemoteRoutingError.responseNotParsing(this.rname, url);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
return this.parseResponse(jsonResponse, routerRequest.origin, routerRequest.destination, routerRequest.travelMode);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @throws {TravelModeCorrespondanceNotFound}
|
|
81
|
-
*/
|
|
82
|
-
getURL(endpointUrl: string, routerRequest: RouterRequest) {
|
|
83
|
-
const { origin, destination } = routerRequest;
|
|
84
|
-
|
|
85
|
-
const osrmMode = this.inputModeCorrespondance(routerRequest);
|
|
86
|
-
const waypoints = [origin, ...routerRequest.waypoints || [], destination];
|
|
87
|
-
|
|
88
|
-
let url = endpointUrl + '/route/v1/' + osrmMode + '/';
|
|
89
|
-
url += waypoints.map(waypoint => [waypoint.longitude + ',' + waypoint.latitude]).join(';');
|
|
90
|
-
url += '?geometries=geojson&overview=full&steps=true';
|
|
91
|
-
routerRequest.provideItineraryAlternatives && (url += '&alternatives=true')
|
|
92
|
-
return url;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
inputModeCorrespondance = (routerRequest: RouterRequest): OsrmMode => {
|
|
96
|
-
const { travelMode, travelModePreference: preference } = routerRequest;
|
|
97
|
-
if (travelMode === 'WALK' && routerRequest.itineraryModifiers?.avoidStairs) return 'pmr';
|
|
98
|
-
if (travelMode === 'WALK') return 'walking';
|
|
99
|
-
if (travelMode === 'BIKE') {
|
|
100
|
-
if (preference === 'FASTEST') return 'bike-fastest';
|
|
101
|
-
if (preference === 'SAFEST') return 'bike-safest';
|
|
102
|
-
if (preference === 'TOURISM') return 'bike-safest';
|
|
103
|
-
return 'bike-safest';
|
|
104
|
-
}
|
|
105
|
-
if (travelMode === 'CAR') return 'driving';
|
|
106
|
-
throw RemoteRoutingError.travelModeUnimplemented(this.rname, travelMode);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
coordinatesToJson({ lat, lng, level }: Coordinates): OsrmCoordinates {
|
|
110
|
-
if (level === null) {
|
|
111
|
-
return [lng, lat];
|
|
112
|
-
}
|
|
113
|
-
if (Level.isRange(level)) {
|
|
114
|
-
return [lng, lat, level[0]];
|
|
115
|
-
}
|
|
116
|
-
return [lng, lat, level];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @param {object} json
|
|
121
|
-
* @returns {Coordinates}
|
|
122
|
-
*/
|
|
123
|
-
jsonToCoordinates(json: OsrmCoordinates): Coordinates {
|
|
124
|
-
const coords = new Coordinates(json[1], json[0]);
|
|
125
|
-
if (json.length > 2) {
|
|
126
|
-
coords.level = json[2] as number;
|
|
127
|
-
}
|
|
128
|
-
return coords;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
getModifierFromAngle(_angle: number): OsrmModifier {
|
|
132
|
-
|
|
133
|
-
const angle = positiveMod(rad2deg(_angle), 360);
|
|
134
|
-
|
|
135
|
-
if (angle > 0 && angle < 60) {
|
|
136
|
-
return 'sharp right';
|
|
137
|
-
}
|
|
138
|
-
if (angle >= 60 && angle < 140) {
|
|
139
|
-
return 'right';
|
|
140
|
-
}
|
|
141
|
-
if (angle >= 140 && angle < 160) {
|
|
142
|
-
return 'slight right';
|
|
143
|
-
}
|
|
144
|
-
if (angle >= 160 && angle <= 200) {
|
|
145
|
-
return 'straight';
|
|
146
|
-
}
|
|
147
|
-
if (angle > 200 && angle <= 220) {
|
|
148
|
-
return 'slight left';
|
|
149
|
-
}
|
|
150
|
-
if (angle > 220 && angle <= 300) {
|
|
151
|
-
return 'left';
|
|
152
|
-
}
|
|
153
|
-
if (angle > 300 && angle < 360) {
|
|
154
|
-
return 'sharp left';
|
|
155
|
-
}
|
|
156
|
-
return 'u turn';
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
noRouteFoundJson(message: object) {
|
|
160
|
-
return {
|
|
161
|
-
'code': 'NoRoute',
|
|
162
|
-
message
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @deprecated
|
|
168
|
-
*/
|
|
169
|
-
itineraryToOsrmJson(itinerary: Itinerary): OsrmJson {
|
|
170
|
-
|
|
171
|
-
const lastLegId = itinerary.legs.length - 1;
|
|
172
|
-
const itinerarySteps = itinerary.steps;
|
|
173
|
-
|
|
174
|
-
const jsonLegs = itinerary.legs.map(({ distance, duration, coords }, idLeg) => {
|
|
175
|
-
|
|
176
|
-
// Filter steps which are in leg
|
|
177
|
-
const legSteps = itinerarySteps.filter(step =>
|
|
178
|
-
coords.find(_coords => _coords.equals(step.coords))
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
const lastStepId = legSteps.length - 1;
|
|
182
|
-
|
|
183
|
-
return {
|
|
184
|
-
distance,
|
|
185
|
-
duration: duration || 0,
|
|
186
|
-
steps: legSteps.map((step, idStep, arr) => {
|
|
187
|
-
|
|
188
|
-
let type: OsrmManeuverType = idStep === 0 && idLeg === 0 ? 'depart' : 'turn';
|
|
189
|
-
type = idStep === lastStepId && idLeg === lastLegId ? 'arrive' : type;
|
|
190
|
-
|
|
191
|
-
const stepCoordsIdx = coords.findIndex(p => p.equals(step.coords));
|
|
192
|
-
const nextStepCoordsIdx = idStep === lastStepId
|
|
193
|
-
? stepCoordsIdx
|
|
194
|
-
: coords.findIndex(p => p.equals(arr[idStep + 1].coords));
|
|
195
|
-
|
|
196
|
-
const osrmStep: OsrmStep = {
|
|
197
|
-
geometry: {
|
|
198
|
-
type: 'LineString',
|
|
199
|
-
coordinates: coords.slice(stepCoordsIdx, nextStepCoordsIdx + 1).map(this.coordinatesToJson)
|
|
200
|
-
},
|
|
201
|
-
distance: step.distance,
|
|
202
|
-
duration: step.duration || 0,
|
|
203
|
-
...(step.name && { name: step.name }),
|
|
204
|
-
maneuver: {
|
|
205
|
-
bearing_before: rad2deg(step.previousBearing),
|
|
206
|
-
bearing_after: rad2deg(step.nextBearing),
|
|
207
|
-
location: this.coordinatesToJson(step.coords),
|
|
208
|
-
modifier: this.getModifierFromAngle(step.angle),
|
|
209
|
-
type
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
return osrmStep;
|
|
213
|
-
})
|
|
214
|
-
};
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
'code': 'Ok',
|
|
219
|
-
'routes': [
|
|
220
|
-
{
|
|
221
|
-
'geometry': {
|
|
222
|
-
'type': 'LineString',
|
|
223
|
-
'coordinates': itinerary.coords.map(this.coordinatesToJson)
|
|
224
|
-
},
|
|
225
|
-
'legs': jsonLegs,
|
|
226
|
-
'distance': itinerary.distance,
|
|
227
|
-
'duration': itinerary.duration,
|
|
228
|
-
'weight_name': 'routability',
|
|
229
|
-
'weight': 0
|
|
230
|
-
}
|
|
231
|
-
],
|
|
232
|
-
'waypoints': []
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
parseResponse(json: OsrmJson, origin: Coordinates, destination: Coordinates, travelMode: TravelMode) {
|
|
237
|
-
|
|
238
|
-
const transitMode = outputModeCorrespondance.get(travelMode)!;
|
|
239
|
-
|
|
240
|
-
const { routes: jsonRoutes } = json;
|
|
241
|
-
if (!jsonRoutes) {
|
|
242
|
-
throw RemoteRoutingError.notFound(this.rname, json.message);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return jsonRoutes.map(jsonItinerary => {
|
|
246
|
-
|
|
247
|
-
const legs = jsonItinerary.legs.map((jsonLeg) => {
|
|
248
|
-
const legCoords = jsonLeg.steps
|
|
249
|
-
.map(step => step.geometry.coordinates.map(this.jsonToCoordinates))
|
|
250
|
-
.flat()
|
|
251
|
-
// Remove duplicates
|
|
252
|
-
.filter((coords, idx, arr) => idx === 0 || !arr[idx - 1].equals(coords));
|
|
253
|
-
|
|
254
|
-
const startCoords = legCoords[0];
|
|
255
|
-
const endCoords = legCoords[legCoords.length - 1];
|
|
256
|
-
|
|
257
|
-
const stepsBuilder = new StepsBuilder().setPathCoords(legCoords).setStart(startCoords).setEnd(endCoords);
|
|
258
|
-
jsonLeg.steps?.forEach(({ maneuver, name, distance, duration }) => {
|
|
259
|
-
|
|
260
|
-
const stepCoords = this.jsonToCoordinates(maneuver.location);
|
|
261
|
-
|
|
262
|
-
// Sometimes, OSRM step does not have the same coordinates than a point in legCoords.
|
|
263
|
-
// ex: first step of https://routing-orsm.getwemap.com/route/v1/walking/2.33222164147,48.87084765712;2.3320734,48.8730212?geometries=geojson&overview=full&steps=true
|
|
264
|
-
// That is why we look for the closest point.
|
|
265
|
-
const distances = legCoords.map(coords => coords.distanceTo(stepCoords));
|
|
266
|
-
const idStepCoordsInLeg = distances.indexOf(Math.min(...distances));
|
|
267
|
-
if (idStepCoordsInLeg < 0) {
|
|
268
|
-
throw new Error('Osrm Parser: Cannot find step coords in leg coordinates');
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
stepsBuilder.addStepInfo({
|
|
272
|
-
coords: legCoords[idStepCoordsInLeg],
|
|
273
|
-
name,
|
|
274
|
-
distance,
|
|
275
|
-
duration
|
|
276
|
-
});
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
return new Leg({
|
|
280
|
-
transitMode,
|
|
281
|
-
duration: jsonLeg.duration,
|
|
282
|
-
coords: legCoords,
|
|
283
|
-
start: {
|
|
284
|
-
coords: startCoords
|
|
285
|
-
},
|
|
286
|
-
end: {
|
|
287
|
-
coords: endCoords
|
|
288
|
-
},
|
|
289
|
-
steps: stepsBuilder.build()
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
return new Itinerary({
|
|
294
|
-
duration: jsonItinerary.duration,
|
|
295
|
-
origin,
|
|
296
|
-
destination,
|
|
297
|
-
legs
|
|
298
|
-
});
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
export default new OsrmRemoteRouter();
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/* eslint-disable max-statements */
|
|
2
|
-
import chai from 'chai';
|
|
3
|
-
import chaiAlmost from 'chai-almost';
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
7
|
-
|
|
8
|
-
import { Coordinates } from '@wemap/geo';
|
|
9
|
-
|
|
10
|
-
import OtpRemoteRouter from './OtpRemoteRouter.js';
|
|
11
|
-
import { verifyStepsCoherence } from '../../model/Itinerary.spec.js';
|
|
12
|
-
import { areTransitAndTravelModeConsistent } from '../../model/TransitMode.js';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const { expect } = chai;
|
|
16
|
-
chai.use(chaiAlmost(0.1));
|
|
17
|
-
|
|
18
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
const assetsPath = path.resolve(__dirname, '../../../assets');
|
|
20
|
-
|
|
21
|
-
describe('OtpRouter - createRouterResponseFromJson', () => {
|
|
22
|
-
|
|
23
|
-
it('Itineraries - 1', () => {
|
|
24
|
-
|
|
25
|
-
const filePath = path.resolve(assetsPath, 'itinerary-grenoble-otp-1.json');
|
|
26
|
-
const fileString = fs.readFileSync(filePath, 'utf8');
|
|
27
|
-
const json = JSON.parse(fileString);
|
|
28
|
-
|
|
29
|
-
const itineraries = OtpRemoteRouter.parseResponse(json);
|
|
30
|
-
itineraries.forEach(verifyStepsCoherence);
|
|
31
|
-
|
|
32
|
-
expect(itineraries.length).equal(2);
|
|
33
|
-
|
|
34
|
-
const itinerary1 = itineraries[0];
|
|
35
|
-
expect(itinerary1.origin.equals(new Coordinates(45.187291, 5.723455))).true;
|
|
36
|
-
expect(itinerary1.destination.equals(new Coordinates(45.163729, 5.74085))).true;
|
|
37
|
-
expect(itinerary1.coords.length).equal(162);
|
|
38
|
-
expect(itinerary1.duration).equal(1206);
|
|
39
|
-
expect(itinerary1.startTime).equal(1614607210000);
|
|
40
|
-
expect(itinerary1.endTime).equal(1614608416000);
|
|
41
|
-
expect(itinerary1.legs.length).equal(3);
|
|
42
|
-
expect(itinerary1.transitMode).equal('BUS');
|
|
43
|
-
expect(areTransitAndTravelModeConsistent(itinerary1.transitMode, 'TRANSIT')).is.true;
|
|
44
|
-
|
|
45
|
-
const itinerary1leg1 = itinerary1.legs[0];
|
|
46
|
-
expect(itinerary1leg1.startTime).equal(1614607210000);
|
|
47
|
-
expect(itinerary1leg1.endTime).equal(1614607499000);
|
|
48
|
-
expect(itinerary1leg1.distance).almost.equal(260.14);
|
|
49
|
-
expect(itinerary1leg1.transitMode).equal('WALK');
|
|
50
|
-
expect(itinerary1leg1.transportInfo).is.null;
|
|
51
|
-
expect(itinerary1leg1.start.name).equal('Origin');
|
|
52
|
-
expect(itinerary1leg1.start.coords.equals(new Coordinates(45.187291, 5.723455))).true;
|
|
53
|
-
expect(itinerary1leg1.end.name).equal('Grenoble, Victor Hugo');
|
|
54
|
-
expect(itinerary1leg1.end.coords.equals(new Coordinates(45.1887, 5.72533))).true;
|
|
55
|
-
|
|
56
|
-
const itinerary1leg2 = itinerary1.legs[1];
|
|
57
|
-
expect(itinerary1leg2.transitMode).equal('BUS');
|
|
58
|
-
expect(itinerary1leg2.transportInfo).is.not.null;
|
|
59
|
-
expect(itinerary1leg2.transportInfo?.name).equal('C4');
|
|
60
|
-
expect(itinerary1leg2.transportInfo?.routeColor).equal('F3D000');
|
|
61
|
-
expect(itinerary1leg2.transportInfo?.routeTextColor).equal('000000');
|
|
62
|
-
expect(itinerary1leg2.transportInfo?.directionName).equal('Eybens, Le Verderet');
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
it('Itineraries - 2', () => {
|
|
68
|
-
|
|
69
|
-
const filePath = path.resolve(assetsPath, 'itinerary-grenoble-otp-2.json');
|
|
70
|
-
const fileString = fs.readFileSync(filePath, 'utf8');
|
|
71
|
-
const json = JSON.parse(fileString);
|
|
72
|
-
|
|
73
|
-
const itineraries = OtpRemoteRouter.parseResponse(json);
|
|
74
|
-
itineraries.forEach(verifyStepsCoherence);
|
|
75
|
-
|
|
76
|
-
expect(itineraries.length).equal(2);
|
|
77
|
-
|
|
78
|
-
const itinerary1 = itineraries[0];
|
|
79
|
-
expect(itinerary1.origin.equals(new Coordinates(45.192514856662456, 5.731452541397871))).true;
|
|
80
|
-
expect(itinerary1.destination.equals(new Coordinates(45.18544, 5.73123))).true;
|
|
81
|
-
expect(itinerary1.legs.length).equal(5);
|
|
82
|
-
|
|
83
|
-
expect(itinerary1.transitMode).equal('TRAM');
|
|
84
|
-
expect(areTransitAndTravelModeConsistent(itinerary1.transitMode, 'TRANSIT')).is.true;
|
|
85
|
-
|
|
86
|
-
expect(itinerary1.legs[0].transitMode).equal('WALK');
|
|
87
|
-
|
|
88
|
-
expect(itinerary1.legs[1].transitMode).equal('TRAM');
|
|
89
|
-
expect(itinerary1.legs[1].transportInfo).is.not.null;
|
|
90
|
-
|
|
91
|
-
expect(itinerary1.legs[2].transitMode).equal('WALK');
|
|
92
|
-
|
|
93
|
-
expect(itinerary1.legs[3].transitMode).equal('TRAM');
|
|
94
|
-
expect(itinerary1.legs[3].transportInfo).is.not.null;
|
|
95
|
-
|
|
96
|
-
expect(itinerary1.legs[4].transitMode).equal('WALK');
|
|
97
|
-
|
|
98
|
-
expect(itinerary1.steps[0].firstStep).is.true;
|
|
99
|
-
expect(itinerary1.steps[itinerary1.steps.length - 1].lastStep).is.true;
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
});
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import Polyline from '@mapbox/polyline';
|
|
2
|
-
|
|
3
|
-
import { Coordinates } from '@wemap/geo';
|
|
4
|
-
import Logger from '@wemap/logger';
|
|
5
|
-
|
|
6
|
-
import Itinerary from '../../model/Itinerary.js';
|
|
7
|
-
import Leg, { type TransportInfo } from '../../model/Leg.js';
|
|
8
|
-
import StepsBuilder from '../../model/StepsBuilder.js';
|
|
9
|
-
import RemoteRouter from '../RemoteRouter.js';
|
|
10
|
-
import { type TravelMode } from '../../model/TravelMode.js';
|
|
11
|
-
import { type RouterRequest } from '../../model/RouterRequest.js';
|
|
12
|
-
import { RemoteRoutingError } from '../../RoutingError.js';
|
|
13
|
-
|
|
14
|
-
type OtpCoordinates = { lat: number, lon: number };
|
|
15
|
-
type FromTo = OtpCoordinates & { name: string };
|
|
16
|
-
type FromToPT = FromTo & { stopId: string, stopCode: string };
|
|
17
|
-
|
|
18
|
-
type CommonLeg = {
|
|
19
|
-
startTime: number,
|
|
20
|
-
endTime: number,
|
|
21
|
-
legGeometry: {
|
|
22
|
-
points: string,
|
|
23
|
-
length: number
|
|
24
|
-
},
|
|
25
|
-
duration: number,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
type LegWalk = CommonLeg & {
|
|
29
|
-
mode: 'WALK',
|
|
30
|
-
from: FromTo,
|
|
31
|
-
to: FromTo,
|
|
32
|
-
steps: (OtpCoordinates & {
|
|
33
|
-
distance: number,
|
|
34
|
-
streetName: string
|
|
35
|
-
})[]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
type LegPT = CommonLeg & {
|
|
39
|
-
mode: 'BUS' | 'TRAM',
|
|
40
|
-
from: FromToPT,
|
|
41
|
-
to: FromToPT,
|
|
42
|
-
intermediateStops: FromToPT,
|
|
43
|
-
routeShortName: 'string',
|
|
44
|
-
routeColor?: 'string',
|
|
45
|
-
routeTextColor?: 'string',
|
|
46
|
-
headsign: 'string'
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
type OtpJson = {
|
|
50
|
-
plan: {
|
|
51
|
-
from: FromTo,
|
|
52
|
-
to: FromTo,
|
|
53
|
-
itineraries: {
|
|
54
|
-
duration: number,
|
|
55
|
-
startTime: number,
|
|
56
|
-
endTime: number,
|
|
57
|
-
walkDistance: number,
|
|
58
|
-
legs: (LegWalk | LegPT)[]
|
|
59
|
-
}[]
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function isLegPT(leg: LegPT | LegWalk): leg is LegPT {
|
|
64
|
-
return leg.mode === 'BUS' || leg.mode === 'TRAM';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function jsonToCoordinates(json: OtpCoordinates) {
|
|
68
|
-
return new Coordinates(json.lat, json.lon);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Input mode correspondance
|
|
74
|
-
*/
|
|
75
|
-
const inputModeCorrespondance = new Map<TravelMode, string>();
|
|
76
|
-
inputModeCorrespondance.set('CAR', 'CAR');
|
|
77
|
-
inputModeCorrespondance.set('WALK', 'WALK');
|
|
78
|
-
inputModeCorrespondance.set('BIKE', 'BICYCLE');
|
|
79
|
-
inputModeCorrespondance.set('TRANSIT', 'WALK,TRANSIT');
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Singleton.
|
|
83
|
-
*/
|
|
84
|
-
class OtpRemoteRouter extends RemoteRouter {
|
|
85
|
-
|
|
86
|
-
get rname() { return 'otp' as const; }
|
|
87
|
-
|
|
88
|
-
async getItineraries(endpointUrl: string, routerRequest: RouterRequest) {
|
|
89
|
-
const url = this.getURL(endpointUrl, routerRequest);
|
|
90
|
-
const res = await (fetch(url).catch(() => {
|
|
91
|
-
throw RemoteRoutingError.unreachableServer(this.rname, url);
|
|
92
|
-
}));
|
|
93
|
-
|
|
94
|
-
const jsonResponse = await res.json().catch(() => {
|
|
95
|
-
throw RemoteRoutingError.responseNotParsing(this.rname, url);
|
|
96
|
-
});
|
|
97
|
-
return this.parseResponse(jsonResponse);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
getURL(endpointUrl: string, routerRequest: RouterRequest) {
|
|
101
|
-
const { origin, destination, waypoints, travelMode } = routerRequest;
|
|
102
|
-
|
|
103
|
-
const otpMode = inputModeCorrespondance.get(travelMode);
|
|
104
|
-
if (!otpMode) {
|
|
105
|
-
throw RemoteRoutingError.travelModeUnimplemented(this.rname, travelMode);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if ((waypoints || []).length > 0) {
|
|
109
|
-
Logger.warn(`${this.rname} router uses only the first 2 waypoints (asked ${waypoints?.length})`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const fromPlace = `fromPlace=${origin.latitude},${origin.longitude}`;
|
|
113
|
-
const toPlace = `toPlace=${destination.latitude},${destination.longitude}`;
|
|
114
|
-
const queryMode = `mode=${otpMode}`;
|
|
115
|
-
|
|
116
|
-
const url = new URL(endpointUrl);
|
|
117
|
-
let { search } = url;
|
|
118
|
-
search = (search ? `${search}&` : '?') + `${fromPlace}&${toPlace}&${queryMode}`;
|
|
119
|
-
|
|
120
|
-
return `${url.origin}${url.pathname}${search}`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Generate multi itineraries from OTP JSON
|
|
126
|
-
*/
|
|
127
|
-
parseResponse(json: OtpJson) {
|
|
128
|
-
|
|
129
|
-
const { plan: jsonPlan } = json;
|
|
130
|
-
if (!jsonPlan) {
|
|
131
|
-
throw RemoteRoutingError.notFound(this.rname);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const itineraries = [];
|
|
135
|
-
|
|
136
|
-
const itinerariesOrigin = jsonToCoordinates(jsonPlan.from);
|
|
137
|
-
const itinerariesDestination = jsonToCoordinates(jsonPlan.to);
|
|
138
|
-
|
|
139
|
-
for (const jsonItinerary of jsonPlan.itineraries) {
|
|
140
|
-
|
|
141
|
-
const legs: Leg[] = [];
|
|
142
|
-
|
|
143
|
-
for (const jsonLeg of jsonItinerary.legs) {
|
|
144
|
-
|
|
145
|
-
const startCoordinates = jsonToCoordinates(jsonLeg.from);
|
|
146
|
-
const endCoordinates = jsonToCoordinates(jsonLeg.to);
|
|
147
|
-
|
|
148
|
-
const legCoords = Polyline.decode(jsonLeg.legGeometry.points)
|
|
149
|
-
.map(([lat, lon]) => new Coordinates(lat, lon));
|
|
150
|
-
|
|
151
|
-
let transportInfo: TransportInfo | undefined;
|
|
152
|
-
const stepsBuilder = new StepsBuilder().setStart(startCoordinates).setEnd(endCoordinates).setPathCoords(legCoords);
|
|
153
|
-
|
|
154
|
-
if (isLegPT(jsonLeg)) {
|
|
155
|
-
|
|
156
|
-
transportInfo = {
|
|
157
|
-
name: jsonLeg.routeShortName,
|
|
158
|
-
routeColor: jsonLeg.routeColor,
|
|
159
|
-
routeTextColor: jsonLeg.routeTextColor,
|
|
160
|
-
directionName: jsonLeg.headsign
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
stepsBuilder.addStepInfo({
|
|
164
|
-
coords: legCoords[0],
|
|
165
|
-
name: jsonLeg.headsign
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
} else {
|
|
169
|
-
|
|
170
|
-
jsonLeg.steps.forEach(jsonStep => {
|
|
171
|
-
// OTP step does not have the same coordinates than a point in legCoords.
|
|
172
|
-
// That is why we look for the closest point.
|
|
173
|
-
const distances = legCoords.map(coords => coords.distanceTo(jsonToCoordinates(jsonStep)));
|
|
174
|
-
const idStepCoordsInLeg = distances.indexOf(Math.min(...distances));
|
|
175
|
-
if (idStepCoordsInLeg < 0) {
|
|
176
|
-
throw new Error('OTP Parser: Cannot find closest step');
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
stepsBuilder.addStepInfo({
|
|
180
|
-
coords: legCoords[idStepCoordsInLeg],
|
|
181
|
-
name: jsonStep.streetName
|
|
182
|
-
});
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const leg = new Leg({
|
|
188
|
-
transitMode: jsonLeg.mode,
|
|
189
|
-
duration: jsonLeg.duration,
|
|
190
|
-
startTime: jsonLeg.startTime,
|
|
191
|
-
endTime: jsonLeg.endTime,
|
|
192
|
-
start: {
|
|
193
|
-
name: jsonLeg.from.name,
|
|
194
|
-
coords: jsonToCoordinates(jsonLeg.from)
|
|
195
|
-
},
|
|
196
|
-
end: {
|
|
197
|
-
name: jsonLeg.to.name,
|
|
198
|
-
coords: jsonToCoordinates(jsonLeg.to)
|
|
199
|
-
},
|
|
200
|
-
coords: legCoords,
|
|
201
|
-
transportInfo,
|
|
202
|
-
steps: stepsBuilder.build()
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
legs.push(leg);
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
const itinerary = new Itinerary({
|
|
209
|
-
duration: jsonItinerary.duration,
|
|
210
|
-
startTime: jsonItinerary.startTime,
|
|
211
|
-
endTime: jsonItinerary.endTime,
|
|
212
|
-
origin: itinerariesOrigin,
|
|
213
|
-
destination: itinerariesDestination,
|
|
214
|
-
legs
|
|
215
|
-
});
|
|
216
|
-
itineraries.push(itinerary);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return itineraries;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export default new OtpRemoteRouter();
|