Skip to content

Commit 3a21e70

Browse files
authored
Merge pull request #953 from cartesapp/motis-v2
Motis v2
2 parents d50c930 + b06f547 commit 3a21e70

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1638
-930
lines changed

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
NEXT_PUBLIC_GTFS_SERVER_URL = "https://serveur.cartes.app/gtfs"
22
NEXT_PUBLIC_PMTILES_SERVER_URL = "https://serveur.cartes.app/pmtiles"
3-
NEXT_PUBLIC_MOTIS_SERVER_URL = "https://serveur.cartes.app"
3+
NEXT_PUBLIC_MOTIS_SERVER_URL = "https://serveur.cartes.app/motis2"
44
NEXT_PUBLIC_PHOTON_SERVER_URL = "https://serveur.cartes.app/photon"
55
NEXT_PUBLIC_BASE_DOMAIN = "cartes.app"

app/Content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ export default function Content(props) {
406406
searchParams,
407407
setSnap,
408408
close: () => {
409-
setSearchParams({ allez: undefined, mode: undefined })
409+
itinerary.reset()
410410
itinerary.setIsItineraryMode(false)
411411
},
412412
state,

app/Map.tsx

Lines changed: 2 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ import useHoverOnMapFeatures from './useHoverOnMapFeatures'
1414
import useTerrainControl from './useTerrainControl'
1515

1616
import useMapContent from '@/components/map/useMapContent'
17-
import getBbox from '@turf/bbox'
1817
import { styled } from 'next-yak'
19-
import { useLocalStorage } from 'usehooks-ts'
2018
import CenteredCross from './CenteredCross'
2119
import MapComponents from './MapComponents'
2220
import MapCompassArrow from './boussole/MapCompassArrow'
@@ -37,6 +35,7 @@ import useDrawItinerary from './itinerary/useDrawItinerary'
3735
import { polylineObjectToLineString } from './transport/decodeTransportsData'
3836
import { addDefaultColor } from './transport/enrichTransportsData'
3937
import { computeCenterFromBbox } from './utils'
38+
import useCenterMapOnState from './effects/useCenterMapOnState'
4039

4140
if (process.env.NEXT_PUBLIC_MAPTILER == null) {
4241
throw new Error('You have to configure env NEXT_PUBLIC_MAPTILER, see README')
@@ -92,15 +91,6 @@ export default function Map(props) {
9291

9392
const mapContainerRef = useRef(null)
9493
const stepsLength = state.filter((step) => step?.allezValue).length
95-
const [autoPitchPreference, setAutoPitchPreference] = useLocalStorage(
96-
'autoPitchPreference',
97-
null,
98-
{
99-
initializeWithValue: false,
100-
}
101-
)
102-
103-
const autoPitchPreferenceIsNo = autoPitchPreference === 'no'
10494

10595
const style = useMemo(() => getStyle(styleKey), [styleKey]),
10696
styleUrl = style.url
@@ -323,57 +313,7 @@ export default function Map(props) {
323313

324314
useHoverOnMapFeatures(map)
325315

326-
/*
327-
*
328-
* Fly to hook
329-
*
330-
* */
331-
useEffect(() => {
332-
if (!map || !vers) return
333-
if (!(vers.geojson || vers.center)) return
334-
if (stepsLength > 1) return
335-
336-
const tailoredZoom = //TODO should be defined by the feature's polygon if any
337-
/* ['city'].includes(vers.choice.type)
338-
? 12
339-
: */
340-
Math.max(15, zoom)
341-
console.log(
342-
'blue',
343-
'will fly to in after OSM download from vers marker',
344-
vers,
345-
tailoredZoom
346-
)
347-
if (vers.geojson) {
348-
const bbox = getBbox(vers.geojson)
349-
map.fitBounds(bbox, {
350-
maxZoom: 17.5, // We don't want to zoom at door level for a place, just at street level
351-
})
352-
} else {
353-
if (!autoPitchPreferenceIsNo)
354-
setAutoPitchPreference(Math.round(new Date().getTime() / 1000))
355-
const auto3d = !autoPitchPreferenceIsNo
356-
357-
const center = vers.center.geometry.coordinates
358-
map.flyTo({
359-
center,
360-
zoom: tailoredZoom,
361-
pitch: autoPitchPreferenceIsNo ? 0 : 40, // pitch in degrees
362-
bearing: autoPitchPreferenceIsNo ? 0 : 15, // bearing in degrees
363-
// speed and maxDuration could let us zoom less quickly between shops,
364-
// but then the animation from town to town wouldn't take place anymore.
365-
// This animation lets the user understand the direction of the move.
366-
padding,
367-
})
368-
}
369-
}, [
370-
map,
371-
vers,
372-
stepsLength,
373-
autoPitchPreferenceIsNo,
374-
setAutoPitchPreference,
375-
paddingHash,
376-
])
316+
useCenterMapOnState(map, zoom, vers, stepsLength, state, paddingHash, padding)
377317

378318
/* TODO Transform this to handle the last itinery point if alone (just a POI url),
379319
* but also to add markers to all the steps of the itinerary */

app/effects/useAddMap.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useLocalStorage, useMediaQuery } from 'usehooks-ts'
1111
import { styles } from '../styles/styles'
1212
import useGeolocation from './useGeolocation'
1313
import useMapIcons from './useMapIcons'
14+
import { stamp } from '../itinerary/transit/utils'
1415

1516
/*
1617
*
@@ -131,7 +132,7 @@ export default function useAddMap(
131132
typeof autoPitchPreference === 'number'
132133
if (
133134
autoPitchPreferenceIsWaiting &&
134-
new Date().getTime() / 1000 - autoPitchPreference < 15 // If the user resets the pitch in less than 15 seconds, we consider it a definitive choice
135+
stamp() - autoPitchPreference < 15 // If the user resets the pitch in less than 15 seconds, we consider it a definitive choice
135136
)
136137
setAutoPitchPreference('no')
137138
}

app/effects/useCenterMapOnState.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import getBbox from '@turf/bbox'
2+
import { useEffect } from 'react'
3+
import { useLocalStorage } from 'usehooks-ts'
4+
import { stamp } from '../itinerary/transit/utils'
5+
import { expandBbox } from '@/components/mapUtils'
6+
7+
export default function useCenterMapOnState(
8+
map,
9+
zoom,
10+
vers,
11+
stepsLength,
12+
state,
13+
paddingHash,
14+
padding
15+
) {
16+
const [autoPitchPreference, setAutoPitchPreference] = useLocalStorage(
17+
'autoPitchPreference',
18+
null,
19+
{
20+
initializeWithValue: false,
21+
}
22+
)
23+
24+
const autoPitchPreferenceIsNo = autoPitchPreference === 'no'
25+
/*
26+
*
27+
* Fly to hook
28+
*
29+
* */
30+
useEffect(() => {
31+
if (!map || !vers) return
32+
if (!(vers.geojson || vers.center)) return
33+
if (stepsLength > 1) {
34+
const coordinates = state
35+
.filter((step) => step.center)
36+
.map((step) => step.center.geometry.coordinates.map((el) => +el))
37+
38+
if (coordinates.length < 2) return
39+
40+
const lineString = {
41+
type: 'Feature',
42+
geometry: {
43+
type: 'LineString',
44+
coordinates,
45+
},
46+
properties: {},
47+
}
48+
49+
console.log('indigo yaya', lineString)
50+
const bbox = getBbox(lineString)
51+
map.fitBounds(expandBbox(bbox), { padding })
52+
53+
return
54+
}
55+
56+
const tailoredZoom = //TODO should be defined by the feature's polygon if any
57+
/* ['city'].includes(vers.choice.type)
58+
? 12
59+
: */
60+
Math.max(15, zoom)
61+
console.log(
62+
'blue',
63+
'will fly to in after OSM download from vers marker',
64+
vers,
65+
tailoredZoom
66+
)
67+
if (vers.geojson) {
68+
const bbox = getBbox(vers.geojson)
69+
map.fitBounds(bbox, {
70+
maxZoom: 17.5, // We don't want to zoom at door level for a place, just at street level
71+
})
72+
} else {
73+
if (!autoPitchPreferenceIsNo) setAutoPitchPreference(stamp())
74+
const auto3d = !autoPitchPreferenceIsNo
75+
76+
const center = vers.center.geometry.coordinates
77+
map.flyTo({
78+
center,
79+
zoom: tailoredZoom,
80+
pitch: autoPitchPreferenceIsNo ? 0 : 40, // pitch in degrees
81+
bearing: autoPitchPreferenceIsNo ? 0 : 15, // bearing in degrees
82+
// speed and maxDuration could let us zoom less quickly between shops,
83+
// but then the animation from town to town wouldn't take place anymore.
84+
// This animation lets the user understand the direction of the move.
85+
padding,
86+
})
87+
}
88+
}, [
89+
map,
90+
vers,
91+
stepsLength,
92+
autoPitchPreferenceIsNo,
93+
setAutoPitchPreference,
94+
paddingHash,
95+
state,
96+
])
97+
}

app/effects/useDrawTransit.ts

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,64 @@
11
import { useEffect } from 'react'
2-
import { handleColor } from '@/app/itinerary/transit/motisRequest'
2+
import { handleColor } from '@/app/itinerary/transit/colors'
33
import { findContrastedTextColor } from '@/components/utils/colors'
44
import { safeRemove } from './utils'
55
import { filterNextConnections } from '../itinerary/transit/utils'
66
import bezierSpline from '@turf/bezier-spline'
77
import { lineString } from '@turf/turf'
8+
import mapboxPolyline from '@mapbox/polyline'
9+
import { notTransitType } from '../itinerary/transit/motisRequest'
810

911
export default function useDrawTransit(map, transit, selectedConnection, date) {
12+
console.log('useDrawTransit', transit)
1013
const connections =
1114
transit &&
12-
transit.connections &&
13-
filterNextConnections(transit.connections, date)
15+
transit.itineraries &&
16+
filterNextConnections(transit.itineraries, date)
1417

1518
const connection = connections && connections[selectedConnection || 0]
1619

1720
useEffect(() => {
1821
if (!map || !connection) return
1922

20-
const { transports, stops } = connection
23+
const { legs } = connection
2124

2225
const featureCollection = {
2326
type: 'FeatureCollection',
24-
features: transports
27+
features: legs
2528
.reduce((memo, next) => {
26-
const route_text_color = handleColor(next.route_text_color, '#000000')
29+
const routeTextColor = handleColor(next.routeTextColor, '#000000')
2730
console.log('next', next)
2831

32+
const geometry = mapboxPolyline.toGeoJSON(next.legGeometry.points, 6)
33+
window.mapboxPolyline = mapboxPolyline
34+
2935
return [
3036
...memo,
3137
{
38+
geometry,
3239
type: 'Feature',
3340
properties: {
34-
name: next.route_short_name || '',
35-
move_type: next.move_type,
36-
route_color: next.route_color || '#d3b2ee',
37-
route_color_darker: next.route_color_darker || '',
38-
route_text_color,
39-
inverse_color: findContrastedTextColor(route_text_color, true),
40-
},
41-
geometry: {
42-
type: 'LineString',
43-
coordinates: stops
44-
.slice(next.move.range.from, next.move.range.to + 1)
45-
.map((stop) => [stop.station.pos.lng, stop.station.pos.lat]),
41+
name: next.routeShortName || '',
42+
mode: next.mode,
43+
isTransit: !notTransitType.includes(next.mode) ? 'Yes' : 'No',
44+
route_color: handleColor(next.routeColor) || '#d3b2ee',
45+
route_color_darker: next.routeColorDarker || '',
46+
route_text_color: routeTextColor,
47+
inverse_color: findContrastedTextColor(routeTextColor, true),
48+
stopsCount:
49+
next.intermediateStops && next.intermediateStops.length + 2,
4650
},
4751
},
4852
]
4953
}, [])
5054
.map((feature) => {
5155
const coordinates = feature.geometry.coordinates
52-
if (coordinates.length <= 2) return feature
56+
if (
57+
coordinates.length <= 2 ||
58+
!feature.properties.stopsCount ||
59+
coordinates.length > feature.properties.stopsCount * 1.5 // testing here if the polyline is a GTFS real route or not
60+
)
61+
return feature
5362

5463
var curved = bezierSpline(lineString(coordinates), {
5564
sharpness: 0.6,
@@ -87,7 +96,7 @@ export default function useDrawTransit(map, transit, selectedConnection, date) {
8796
source: id,
8897
type: 'line',
8998
id: id + '-lines-contour',
90-
filter: ['==', ['get', 'move_type'], 'Transport'],
99+
filter: ['==', ['get', 'isTransit'], 'Yes'],
91100
layout: {
92101
'line-join': 'round',
93102
'line-cap': 'round',
@@ -111,7 +120,7 @@ export default function useDrawTransit(map, transit, selectedConnection, date) {
111120
source: id,
112121
type: 'line',
113122
id: id + '-lines',
114-
filter: ['==', ['get', 'move_type'], 'Transport'],
123+
filter: ['==', ['get', 'isTransit'], 'Yes'],
115124

116125
layout: {
117126
'line-join': 'round',
@@ -151,12 +160,14 @@ export default function useDrawTransit(map, transit, selectedConnection, date) {
151160
'text-halo-width': 1,
152161
},
153162
})
163+
// TODO shared line style for non-transit (walk, bike, car)
164+
// could be better, reusing the same styles as the other itineraries
154165
map.addLayer(
155166
{
156167
source: id,
157168
type: 'line',
158169
id: id + '-lines-walking-background',
159-
filter: ['==', ['get', 'move_type'], 'Walk'],
170+
filter: ['==', ['get', 'isTransit'], 'No'],
160171
layout: {
161172
'line-join': 'round',
162173
'line-cap': 'round',
@@ -173,7 +184,7 @@ export default function useDrawTransit(map, transit, selectedConnection, date) {
173184
source: id,
174185
type: 'line',
175186
id: id + '-lines-walking',
176-
filter: ['==', ['get', 'move_type'], 'Walk'],
187+
filter: ['==', ['get', 'isTransit'], 'No'],
177188
layout: {
178189
'line-join': 'round',
179190
'line-cap': 'round',

app/itineraire/api/route.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import splitAllez from '@/components/itinerary/splitAllez'
22
import {
33
computeMotisTrip,
4-
isNotTransitConnection,
4+
isNotTransitItinerary,
55
} from '@/app/itinerary/transit/motisRequest'
66
import { initialDate } from '@/app/itinerary/transit/utils'
77
import { stepOsmRequest } from '@/app/stepOsmRequest'
@@ -28,16 +28,17 @@ export async function GET(request: { url: string | URL }) {
2828
{ lng: lngB, lat: latB },
2929
date
3030
)
31-
if (json.state === 'error' || !json.content) {
31+
32+
if (json.state === 'error' || !json.itineraries) {
3233
console.log(json)
3334
return new Response(`Motis error`, { status: 500 })
3435
}
35-
const { connections } = json.content
36-
const transitConnections = connections.filter(
37-
(connection) => !isNotTransitConnection(connection)
36+
const { itineraries } = json
37+
const transitItineraries = itineraries.filter(
38+
(connection) => !isNotTransitItinerary(connection)
3839
)
3940

40-
if (!transitConnections.length) {
41+
if (!transitItineraries.length) {
4142
return new Response(
4243
`Itinerary API call error : no transit itinerary available at this date`,
4344
{

0 commit comments

Comments
 (0)