Skip to content

Commit

Permalink
feat: use gtfs in front
Browse files Browse the repository at this point in the history
  • Loading branch information
VincentHardouin committed Apr 19, 2024
1 parent 38258ec commit 69472df
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 186 deletions.
2 changes: 1 addition & 1 deletion assets/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -53547,4 +53547,4 @@
"id_red_zdc": "71492"
}
]
}
}
76 changes: 41 additions & 35 deletions src/front.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import * as bootstrap from 'bootstrap';
import arrondissements from '../assets/arrondissements.geojson';
import stationsData from '../assets/stations.geojson';
import linesData from '../assets/lines.geojson';
import { ParisMap } from './map.js';
import { getSeededRandomStations, pickStations } from './pick-stations.js';
import { verifyIfConnected } from './graph.js';
import { searchStations } from './utils.front.js';
import { getAdjacentStops, getRoutes, getStops, getUniqueStops } from './utils.js';

const stations = stationsData.features.filter((s) => {
return s.properties.adjacentStations && s.properties.name;
});
const sortedStations = stations.map(d => d.properties.name).sort();
const lines = linesData.features;
const stations = getUniqueStops();
const adjacentStops = getAdjacentStops();
const sortedStations = stations.map(d => d.stop_name).sort();
const routes = getRoutes();
const stops = getStops();

const map = new ParisMap({ arrondissements, stations, lines });
const map = new ParisMap({ arrondissements, stations, lines: routes });

const addedStations = new Map();

Expand Down Expand Up @@ -70,35 +69,33 @@ function addNameToInput(event) {
}

function addStation({ station, color }) {
const stationName = station.properties.name;
if (addedStations.has(stationName))
const stationUniqueId = station.stop_unique_id;
if (addedStations.has(stationUniqueId))
return;

const adjacentStations = findAdjacentStations({ station, lines, addedStations });
map.addStation({ station, color, adjacentStations });
addedStations.set(stationName, station);
const foundAdjacentStops = findAdjacentStops({
pickStation: station,
addedStations,
adjacentStops,
});
map.addStation({ station, color, adjacentStops: foundAdjacentStops });
addedStations.set(stationUniqueId, station);
}

function findAdjacentStations({ station, addedStations }) {
const adjacentStations = [];
for (const newStationLine of station.properties.lines) {
const lineAdjacentStations = station.properties.adjacentStations[newStationLine];

for (const adjacentStationName of lineAdjacentStations) {
if (!addedStations.has(adjacentStationName))
continue;

adjacentStations.push(addedStations.get(adjacentStationName));
}
}
return adjacentStations;
function findAdjacentStops({ pickStation, addedStations, adjacentStops }) {
const filteredAdjacentStationForPickedStation = adjacentStops.filter((adjacentStop) => {
return adjacentStop.from_stop_unique_id === pickStation.stop_unique_id || adjacentStop.to_stop_unique_id === pickStation.stop_unique_id;
});
return filteredAdjacentStationForPickedStation.filter((adjacentStop) => {
return addedStations.has(adjacentStop.from_stop_unique_id) || addedStations.has(adjacentStop.to_stop_unique_id);
});
}

function handleClickOnTryButton({ pick }) {
document.getElementById('try').addEventListener('click', () => {
const input = document.getElementById('station');
const stationName = input.value;
const station = stations.find(d => d.properties.name === stationName);
const station = stations.find(d => d.stop_name === stationName);
if (station) {
addStation({ station });
input.value = '';
Expand All @@ -109,20 +106,29 @@ function handleClickOnTryButton({ pick }) {
}
});
}

function isFinished({ addedStations, pick }) {
const isFinished = verifyIfConnected({ start: pick.start.properties.name, end: pick.end.properties.name, stations: [...addedStations.values()] });
const isFinished = verifyIfConnected({
start: pick.start.stop_unique_id,
end: pick.end.stop_unique_id,
stationsToVerify: [...addedStations.keys()],
adjacentStops,
});
if (isFinished) {
const modal = new bootstrap.Modal(document.getElementById('finish-modal'));
const result = document.getElementById('result');
result.innerHTML = `<p>Bravo, tu as réussi à relier les deux stations en ${addedStations.size - 2} stations ! 🎉</p><p>Voici le chemin optimal en ${pick.path.distance - 1} stations : </p>`;
result.innerHTML = `<p>Bravo, tu as réussi à relier les deux stations en ${addedStations.size - 2} stations ! 🎉</p><p>Voici le chemin optimal en ${pick.path.length - 2} stations : </p>`;
const list = document.createElement('ul');
for (let i = 0; i < pick.path.path.length; i++) {
const station = pick.path.path[i];
for (let i = 0; i < pick.path.length; i++) {
const stationId = pick.path[i];
const { stop_unique_id, route_id } = stops.find(d => d.stop_id === stationId);
const stopName = stations.find(d => d.stop_unique_id === stop_unique_id).stop_name;
const line = routes.find(d => d.route_id === route_id);
const li = document.createElement('li');
li.textContent = station;
li.textContent = `${stopName} - Ligne ${line.route_short_name}`;
if (i === 0)
li.classList.add('start');
else if (i === pick.path.path.length - 1)
else if (i === pick.path.length - 1)
li.classList.add('end');

list.appendChild(li);
Expand All @@ -135,8 +141,8 @@ function isFinished({ addedStations, pick }) {
function initGame() {
const date = new Date();
const dateToSeed = date.getFullYear() * 10000 + (date.getMonth() + 1) * 100 + date.getDate();
const pick = pickStations({ stations, random: getSeededRandomStations(dateToSeed) });
document.getElementById('instruction').innerHTML = `Aujourd'hui, nous allons de <span class="start">${pick.start.properties.name}</span> jusqu'à <span class="end">${pick.end.properties.name}</span> en passant par le moins de stations possible.`;
const pick = pickStations({ stations, adjacentStops, random: getSeededRandomStations(dateToSeed) });
document.getElementById('instruction').innerHTML = `Aujourd'hui, nous allons de <span class="start">${pick.start.stop_name}</span> jusqu'à <span class="end">${pick.end.stop_name}</span> en passant par le moins de stations possible.`;
addStation({ station: pick.start, color: '#008a22' });
addStation({ station: pick.end, color: '#e52228' });
return pick;
Expand Down
6 changes: 3 additions & 3 deletions src/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ function computeSmallestStationsPath({ start, end, adjacentStops }) {
return graph.dijkstra(start, end);
}

function verifyIfConnected({ start, end, adjacentStations, stationsToVerify }) {
const adjacentStops = adjacentStations.filter((stop) => {
function verifyIfConnected({ start, end, adjacentStops, stationsToVerify }) {
const filteredAdjacentStops = adjacentStops.filter((stop) => {
return stationsToVerify.includes(stop.from_stop_unique_id) && stationsToVerify.includes(stop.to_stop_unique_id);
});
const graph = computeGraph(adjacentStops, start, end);
const graph = computeGraph(filteredAdjacentStops, start, end);
return graph.isConnectedTo(start, end);
}

Expand Down
104 changes: 30 additions & 74 deletions src/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,6 @@ import * as d3 from 'd3';

const PARIS_COORDINATES = [2.3522, 48.8566];

const color = {
'1': '#FFCE00',
'2': '#0064B0',
'3': '#9F9825',
'3bis': '#98D4E2',
'4': '#C04191',
'5': '#F28E42',
'6': '#83C491',
'7': '#F3A4BA',
'7bis': '#83C491',
'8': '#CEADD2',
'9': '#D5C900',
'10': '#E3B32A',
'11': '#8D5E2A',
'12': '#00814F',
'13': '#98D4E2',
'14': '#662483',
'15': '#B90845',
'16': '#F3A4BA',
'17': '#D5C900',
'18': '#00A88F',
};

class ParisMap {
#arrondissements;
#stations;
Expand Down Expand Up @@ -99,67 +76,46 @@ class ParisMap {
this.#svg.attr('width', width).attr('height', height);
}

addStation({ station, color, adjacentStations }) {
addStation({ station, color, adjacentStops }) {
this.#drawStation({ station, color });
this.#addPathBetweenStations({ newStation: station, adjacentStations });
this.#addPathBetweenStations({ adjacentStops });
}

#drawStation({ station, color = '#0d47a1' }) {
for (const coordinates of station.properties.coordinates) {
this.#stationsNode.append('circle')
.attr('class', 'metro-station')
.attr('cx', this.#projection(coordinates)[0])
.attr('cy', this.#projection(coordinates)[1])
.attr('r', 3)
.style('fill', color)
.on('mouseover', () => {
this.#tooltip.style('visibility', 'visible');
this.#tooltip.html(station.properties.name);
})
.on('mousemove', (event) => {
this.#tooltip.style('top', `${event.pageY - 10}px`)
.style('left', `${event.pageX + 10}px`);
})
.on('mouseout', () => {
this.#tooltip.style('visibility', 'hidden');
});
}

this.#stationsNode.append('path')
.attr('d', d3.line()(station.properties.coordinates.map(c => this.#projection(c))))
.attr('stroke', color)
.attr('stroke-width', 2)
.attr('fill', 'none');
const point = [station.stop_lat, station.stop_lon];
this.#stationsNode.append('circle')
.attr('class', 'metro-station')
.attr('cx', this.#projection(point)[0])
.attr('cy', this.#projection(point)[1])
.attr('r', 3)
.style('fill', color)
.on('mouseover', () => {
this.#tooltip.style('visibility', 'visible');
this.#tooltip.html(station.stop_name);
})
.on('mousemove', (event) => {
this.#tooltip.style('top', `${event.pageY - 10}px`)
.style('left', `${event.pageX + 10}px`);
})
.on('mouseout', () => {
this.#tooltip.style('visibility', 'hidden');
});
}

#addPathBetweenStations({ newStation, adjacentStations }) {
for (const newStationLine of newStation.properties.lines) {
const line = this.#lines.find(l => l.properties.name === newStationLine);
for (const adjacentStation of adjacentStations) {
const lineCoordinates = line.geometry.type === 'LineString' ? line.geometry.coordinates : line.geometry.coordinates[0];

const newStationIndex = newStation.properties.inLineIndex[newStationLine];
const adjacentStationIndex = adjacentStation.properties.inLineIndex[newStationLine];

if (lineCoordinates.length < newStationIndex && lineCoordinates.length < adjacentStationIndex)
continue;

if (newStationIndex === undefined || adjacentStationIndex === undefined)
continue;

let drawLine;
if (newStationIndex < adjacentStationIndex)
drawLine = lineCoordinates.slice(newStationIndex, adjacentStationIndex + 1);
else
drawLine = lineCoordinates.slice(adjacentStationIndex, newStationIndex + 1);
#addPathBetweenStations({ adjacentStops }) {
adjacentStops
.filter(adjacentStop => adjacentStop.path)
.forEach((adjacentStop) => {
if (!adjacentStop.path.length || !adjacentStop.route_id)
return;

const { route_color: color } = this.#lines.find(line => line.route_id === adjacentStop.route_id);
this.#stationsNode.append('path')
.attr('d', d3.line()(drawLine.map(c => this.#projection(c))))
.attr('stroke', color[line.properties.ref])
.attr('d', d3.line()(adjacentStop.path.map(p => this.#projection(p))))
.attr('stroke', `#${color}`)
.attr('stroke-width', 2)
.attr('fill', 'none');
}
}
});
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/pick-stations.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function pickStations({ stations, adjacentStops, random = getRandomStations } =
return {
start,
end,
path,
...path,
};
}

Expand Down
29 changes: 18 additions & 11 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import fs from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { adjacentStops, routes, stops, uniqueStops } from '../assets/data';

const __dirname = dirname(fileURLToPath(import.meta.url));
function getAdjacentStops() {
return adjacentStops;
}

function getUniqueStops() {
return uniqueStops;
}

function getRoutes() {
return routes;
}

function getStations() {
const stationsPath = join(__dirname, '../assets/stations.geojson');
const { features: stations } = JSON.parse(fs.readFileSync(stationsPath));
return stations.filter((s) => {
return s.properties.adjacentStations && s.properties.name;
});
function getStops() {
return stops;
}

export {
getStations,
getAdjacentStops,
getRoutes,
getUniqueStops,
getStops,
};
Loading

0 comments on commit 69472df

Please sign in to comment.