Import initial : code en production sur Hetzner
This commit is contained in:
commit
cf6ee64e31
43 changed files with 14404 additions and 0 deletions
89
shared/polyline.ts
Normal file
89
shared/polyline.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import type { Coord } from './types'
|
||||
|
||||
/**
|
||||
* Decode le format « encoded polyline » de Google, utilise par MOTIS.
|
||||
* La precision n'est pas universelle : Google encode a 1e5, MOTIS a 1e7.
|
||||
* Se tromper d'un facteur 100 place le trace a des milliers de kilometres.
|
||||
*/
|
||||
export function decodePolyline(encoded: string, precision = 5): Coord[] {
|
||||
const factor = 10 ** precision
|
||||
const coords: Coord[] = []
|
||||
let index = 0
|
||||
let lat = 0
|
||||
let lon = 0
|
||||
|
||||
while (index < encoded.length) {
|
||||
let result = 0
|
||||
let shift = 0
|
||||
let byte: number
|
||||
|
||||
do {
|
||||
byte = encoded.charCodeAt(index++) - 63
|
||||
result |= (byte & 0x1F) << shift
|
||||
shift += 5
|
||||
} while (byte >= 0x20)
|
||||
lat += result & 1 ? ~(result >> 1) : result >> 1
|
||||
|
||||
result = 0
|
||||
shift = 0
|
||||
do {
|
||||
byte = encoded.charCodeAt(index++) - 63
|
||||
result |= (byte & 0x1F) << shift
|
||||
shift += 5
|
||||
} while (byte >= 0x20)
|
||||
lon += result & 1 ? ~(result >> 1) : result >> 1
|
||||
|
||||
// Ordre GeoJSON : longitude d'abord, comme l'attend MapLibre.
|
||||
coords.push([lon / factor, lat / factor])
|
||||
}
|
||||
|
||||
return coords
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduit le nombre de points d'un trace.
|
||||
* Un aller Lille-Paris peut compter plusieurs milliers de points ; au-dela
|
||||
* d'un millier, l'oeil ne voit plus la difference mais le reseau, si.
|
||||
*/
|
||||
export function thin(coords: Coord[], max = 800): Coord[] {
|
||||
if (coords.length <= max) return coords
|
||||
const step = Math.ceil(coords.length / max)
|
||||
const out = coords.filter((_, i) => i % step === 0)
|
||||
// Le dernier point est le terminus : il ne doit jamais sauter.
|
||||
if (out[out.length - 1] !== coords[coords.length - 1]) out.push(coords[coords.length - 1]!)
|
||||
return out
|
||||
}
|
||||
/**
|
||||
* Renvoie le point situe a un ratio donne de la distance cumulee le long
|
||||
* du trace. midpointAt(coords, 0.5) donne le vrai milieu geometrique, pas
|
||||
* le point d'index milieu qui atterrit dans le vide sur un trajet inegal.
|
||||
* Utilise Haversine simplifie en distance euclidienne : suffisant pour
|
||||
* placer une bulle a l'echelle d'un trajet inter-villes.
|
||||
*/
|
||||
export function midpointAt(coords: Coord[], ratio = 0.5): Coord {
|
||||
if (coords.length === 0) throw new Error('trace vide')
|
||||
if (coords.length === 1) return coords[0]!
|
||||
if (ratio <= 0) return coords[0]!
|
||||
if (ratio >= 1) return coords[coords.length - 1]!
|
||||
|
||||
const distances: number[] = [0]
|
||||
let total = 0
|
||||
for (let i = 1; i < coords.length; i++) {
|
||||
const [x1, y1] = coords[i - 1]!
|
||||
const [x2, y2] = coords[i]!
|
||||
total += Math.hypot(x2 - x1, y2 - y1)
|
||||
distances.push(total)
|
||||
}
|
||||
|
||||
const target = total * ratio
|
||||
for (let i = 1; i < distances.length; i++) {
|
||||
if (distances[i]! >= target) {
|
||||
const [x1, y1] = coords[i - 1]!
|
||||
const [x2, y2] = coords[i]!
|
||||
const segLength = distances[i]! - distances[i - 1]!
|
||||
const segRatio = segLength === 0 ? 0 : (target - distances[i - 1]!) / segLength
|
||||
return [x1 + (x2 - x1) * segRatio, y1 + (y2 - y1) * segRatio]
|
||||
}
|
||||
}
|
||||
return coords[coords.length - 1]!
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue