201 lines
6.1 KiB
JavaScript
201 lines
6.1 KiB
JavaScript
const DAY_IN_MS = 8.64e+7;
|
|
const MIN_TIME_BETWEEN_DAYS = 2 * 60 * 60000 // 2 hours
|
|
|
|
class StopId {
|
|
constructor(stopIdString){
|
|
let [lineId, direction, stopId] = stopIdString.split(/\//)
|
|
this.lineId = lineId
|
|
this.stopId = stopId
|
|
this.direction = direction
|
|
}
|
|
}
|
|
|
|
async function getNextTripJSON(stop_id){
|
|
let res = await fetch(`/api/interface/tcl/next-trips/stops/${encodeURIComponent(stop_id.stopId)}/${encodeURIComponent(stop_id.lineId)}/${stop_id.direction}`);
|
|
if(!res.ok){
|
|
throw new Error(`Server responded with ${res.status} ${res.statusText}`)
|
|
}
|
|
|
|
let result = await res.json();
|
|
|
|
if(!result.data){
|
|
throw new Error(`Stop ${stop_id.stopId} not found`)
|
|
}
|
|
|
|
return result.data[0]
|
|
}
|
|
|
|
async function getStopsJSON(stop_id){
|
|
let res = await fetch(`/api/interface/tcl/lines/${encodeURIComponent(stop_id.lineId)}/stops`);
|
|
if(!res.ok){
|
|
throw new Error(`Server responded with ${res.status} ${res.statusText}`)
|
|
}
|
|
|
|
let result = await res.json();
|
|
|
|
if(!result.data){
|
|
throw new Error(`Line ${stop_id.lineId} not found`)
|
|
}
|
|
|
|
return result.data
|
|
}
|
|
|
|
async function getTimetableJSON(stop_id, date){
|
|
let res = await fetch(`/api/interface/tcl/timetables/${encodeURIComponent(stop_id.stopId)}/${encodeURIComponent(stop_id.lineId)}/${stop_id.direction}?date=${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`);
|
|
if(!res.ok){
|
|
throw new Error(`Server responded with ${res.status} ${res.statusText}`)
|
|
}
|
|
|
|
let result = await res.json();
|
|
|
|
if(!result.data){
|
|
throw new Error(`Timetable for ${stop_id.lineId} not found`)
|
|
}
|
|
|
|
return result.data
|
|
}
|
|
|
|
async function getLineDetailsJSON(stop_id){
|
|
let res = await fetch(`/api/interface/tcl/lines/${encodeURIComponent(stop_id.lineId)}`);
|
|
if(!res.ok){
|
|
throw new Error(`Server responded with ${res.status} ${res.statusText}`)
|
|
}
|
|
|
|
let result = await res.json();
|
|
|
|
if(!result.data){
|
|
throw new Error(`Line ${stop_id.lineId} not found`)
|
|
}
|
|
|
|
return result.data
|
|
}
|
|
|
|
function getIconURL(stop_id){
|
|
return new URL(`/api/valkyrie/assets/lines/${stop_id.lineId}.svg?type=image%2Fsvg%2Bxml`, window.location).toString()
|
|
}
|
|
|
|
async function getLineDetails(stop_id){
|
|
let line_json = await getLineDetailsJSON(stop_id)
|
|
|
|
let route = line_json.routes.find(it => it.direction == stop_id.direction)
|
|
|
|
let line = {
|
|
id: stop_id.lineId,
|
|
displayName: route?.name,
|
|
picto: getIconURL(stop_id),
|
|
name: line_json.code,
|
|
direction: route?.name,
|
|
timetable: null
|
|
}
|
|
|
|
return line
|
|
}
|
|
|
|
async function getTimetable(stop_id, date){
|
|
let timetable_json = await getTimetableJSON(stop_id, date)
|
|
|
|
let timetable = []
|
|
for(let scheduled_time of timetable_json.scheduleTimes){
|
|
let time = new Date(scheduled_time.dateTime)
|
|
timetable.push({
|
|
displayHours: time.getHours().toString().padStart(2, "0"),
|
|
displayMinutes: time.getMinutes().toString().padStart(2, "0"),
|
|
displayTime: `${time.getHours().toString().padStart(2, "0")}h${time.getMinutes().toString().padStart(2, "0")}`,
|
|
time
|
|
})
|
|
}
|
|
|
|
return timetable
|
|
}
|
|
|
|
/**
|
|
* Get next Bus/Metro/Tram passage of given stop description
|
|
* @param {String} stop_description Stop description string
|
|
*
|
|
* To get stop description go to https://carte-interactive.tcl.fr/public-transport/lines/
|
|
* then find your line and your stop in this line.
|
|
* Your URL must look like something like this
|
|
* https://carte-interactive.tcl.fr/public-transport/lines/line:SYTNEX:C/forward/stop_point:SYTNEX:10787
|
|
* Stop description is everything after "lines/" ("line:SYTNEX:C/forward/stop_point:SYTNEX:10787" in example above)
|
|
*/
|
|
export async function getNextPassage(stop_description, options = {
|
|
timetable: +2 // Today and tomorrow
|
|
}){
|
|
let stopId = new StopId(stop_description);
|
|
|
|
let proms = [
|
|
getLineDetails(stopId),
|
|
getNextTripJSON(stopId),
|
|
getStopsJSON(stopId),
|
|
Promise.resolve([])
|
|
]
|
|
|
|
let timeTableAmount = options?.timetable || +2;
|
|
if(timeTableAmount){
|
|
for(let dayOffset = 0; dayOffset<timeTableAmount; dayOffset++){
|
|
let now = new Date(Date.now()+(dayOffset*DAY_IN_MS))
|
|
proms.push(await getTimetable(stopId, now))
|
|
}
|
|
}
|
|
|
|
let [line, next_trip, line_stops, ...all_timetables] = await Promise.all(proms);
|
|
|
|
let line_timetables = []
|
|
for(let timetable of all_timetables){
|
|
line_timetables.push(...timetable)
|
|
}
|
|
line.timetable = line_timetables
|
|
line.getLastPassageTime = getLastPassageTime.bind(line)
|
|
|
|
let stop = line_stops.find(it => it.id == stopId.stopId)
|
|
|
|
let time;
|
|
if(next_trip.schedules){
|
|
let next_schedule = next_trip.schedules.find(it => (new Date(it.dateTime).getTime() - Date.now()) > 5*60*1000 )
|
|
if(!next_schedule) {
|
|
next_schedule = next_trip.schedules[next_trip.schedules.length - 1]
|
|
}
|
|
if(next_schedule){
|
|
time = new Date(next_schedule.dateTime)
|
|
}
|
|
}
|
|
|
|
let nextPassage = {
|
|
line,
|
|
displayName: stop?.name,
|
|
displayTime: "<unsupported>",
|
|
time: next_trip.schedules?.[0] ? new Date(next_trip.schedules?.[0].dateTime) : null
|
|
}
|
|
nextPassage.getLastPassageTime = getLastPassageTime.bind(nextPassage)
|
|
|
|
return nextPassage
|
|
}
|
|
|
|
export async function getVelovBikeState(station_id){
|
|
let res = await fetch(`/api/interface/tcl/realtime/navitia/${decodeURIComponent(station_id)}`)
|
|
if(!res.ok){
|
|
throw new Error(`Server responded with ${res.status} ${res.statusText}`)
|
|
}
|
|
return await res.json()
|
|
}
|
|
|
|
function getLastPassageTime(){
|
|
let timetable = this.timetable || this.line.timetable
|
|
|
|
if(!timetable){
|
|
return
|
|
}
|
|
|
|
let now = new Date()
|
|
|
|
let last_passage = timetable
|
|
.find((it, i) => {
|
|
if((timetable[i+1]) && it.time.getTime() > now && (it.time.getTime() + MIN_TIME_BETWEEN_DAYS) < timetable[i+1].time.getTime()){
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
})
|
|
|
|
return last_passage
|
|
} |