diff --git a/apps/api/package.json b/apps/api/package.json index c768e3fd..45c12977 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -17,6 +17,7 @@ "dev": "tsx watch --env-file-if-exists=.env src/server.ts", "lint": "eslint .", "start": "node --enable-source-maps --env-file-if-exists=.env dist/server.js", + "transit:import": "tsx src/transit/import-cli.ts", "test": "vitest run", "typecheck": "tsc" }, diff --git a/apps/api/src/db/seed.test.ts b/apps/api/src/db/seed.test.ts index 7d16c854..590cf0ea 100644 --- a/apps/api/src/db/seed.test.ts +++ b/apps/api/src/db/seed.test.ts @@ -87,6 +87,23 @@ describe('seed', () => { } }); + // Criterion of #180: the access of every demo route, from the reference stations (D-050). + it('computes the access of every demo route, the station nearest its first step', async () => { + const rows = await app.db + .select({ id: routes.id, access: sql`${routes.computed}->'access'` }) + .from(routes) + .where(inArray(routes.id, demoRouteIds)); + expect(rows).toHaveLength(10); + for (const row of rows) { + expect(row.access, row.id).toMatchObject({ station: expect.any(String) }); + } + expect(rows.find((row) => row.id === routeId('montmartre-a-deux'))?.access).toEqual({ + station: 'Abbesses', + lines: [{ mode: 'metro', line: '12' }], + distance_m: 63, + }); + }); + // Criterion of #32, with the operators of the central search query (wiki Modele-de-Donnees). describe('answers every filter value, except the assumed zeros', () => { const filters: Record< diff --git a/apps/api/src/db/seed.ts b/apps/api/src/db/seed.ts index ee953bb0..4098eba1 100644 --- a/apps/api/src/db/seed.ts +++ b/apps/api/src/db/seed.ts @@ -11,6 +11,7 @@ import { certificationSettingKeys, defaultCertificationThresholds } from '@widoo/shared'; import { and, eq, inArray, sql } from 'drizzle-orm'; import { stableRecommendationOf } from '../recommendation/stable'; +import { accessColumnOf } from '../transit/access'; import type { Db } from './client'; import { envelopeOf } from './geography'; import { @@ -199,7 +200,8 @@ function routeRow(route: DemoRoute, cityId: string) { tips: route.tips, startLocation: locations[0], bounds: envelopeOf(locations), - computed: route.computed, + // The access is computed like the editor does, from the reference stations (D-050). + computed: { ...route.computed, access: accessColumnOf(locations[0]) }, ...bucketsOf(route), publishedAt: seedAt, // As the nightly job would compute it on the dataset date: no rating, every place verified. diff --git a/apps/api/src/editor/save.ts b/apps/api/src/editor/save.ts index e34adbb9..b54d2ee6 100644 --- a/apps/api/src/editor/save.ts +++ b/apps/api/src/editor/save.ts @@ -14,12 +14,14 @@ import { envelopeOf } from '../db/geography'; import { routeRevisions, routes } from '../db/schema'; import { conflict } from '../errors'; import { mainTransportOf, replaceSteps, type RouteContent } from './content'; +import { accessColumnOf } from '../transit/access'; import type { RouteRow } from './lifecycle'; import { evaluate, type Workspace } from './workspace'; /** * Writes the content into the route itself, with what is derived from it: stored trips, transport, - * start, envelope, city of the first step, `computed` and its buckets. + * start, envelope, city of the first step, `computed` with the access of the first step (D-050) + * and its buckets: the access follows the first step at every save. */ export async function writeInPlace( tx: Tx, @@ -57,6 +59,7 @@ export async function writeInPlace( walking_m: computed.walkingM, effort: computed.effort, warnings: warnings.map((issue) => issue.code), + access: accessColumnOf(located[0]?.location), computed_at: computed.computedAt, }, durationBucket: computed && durationBucketOf(computed.durationMin), diff --git a/apps/api/src/routes/editor-edit.test.ts b/apps/api/src/routes/editor-edit.test.ts index ebba808c..7acc0fd7 100644 --- a/apps/api/src/routes/editor-edit.test.ts +++ b/apps/api/src/routes/editor-edit.test.ts @@ -128,6 +128,34 @@ describe('PUT /routes/:id/steps', () => { expect((await rowOf(id))?.transport).toBe('bike'); }); + // Criterion of #180: the access follows the first step (D-050). + it('computes the access of the first step, again when the first step changes', async () => { + const headers = await fixtures.signIn(); + const { id } = OwnRoute.parse((await create(headers)).json()); + const [victorHugo, carnavalet] = demoPlaceIds; + const accessOf = async () => ((await rowOf(id))?.computed as { access: unknown }).access; + + await putSteps(headers, id, [ + { placeId: victorHugo, durationMin: 30 }, + { placeId: carnavalet, durationMin: 30 }, + ]); + expect(await accessOf()).toEqual({ + station: 'Bastille', + lines: ['1', '5', '8'].map((line) => ({ mode: 'metro', line })), + distance_m: 316, + }); + + await putSteps(headers, id, [ + { placeId: carnavalet, durationMin: 30 }, + { placeId: victorHugo, durationMin: 30 }, + ]); + expect(await accessOf()).toEqual({ + station: 'Saint-Paul', + lines: [{ mode: 'metro', line: '1' }], + distance_m: 333, + }); + }); + it('answers 422 to the step of another route, or a place that does not exist', async () => { const headers = await fixtures.signIn(); const { id } = OwnRoute.parse((await create(headers)).json()); diff --git a/apps/api/src/transit/access.test.ts b/apps/api/src/transit/access.test.ts new file mode 100644 index 00000000..4cee7dc2 --- /dev/null +++ b/apps/api/src/transit/access.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from 'vitest'; +import { demoDataset } from '../db/seed/demo-routes'; +import { accessColumnOf, ACCESS_MAX_M, nearestAccess } from './access'; +import type { TransitStation } from './stations'; + +const abbesses = { lat: 48.88459, lng: 2.33795 }; +const stations: TransitStation[] = [ + { + id: '1', + lines: [{ mode: 'metro', line: '12' }], + stops: [{ name: 'Abbesses', ...abbesses }], + }, + { + id: '2', + lines: [ + { mode: 'metro', line: '2' }, + { mode: 'metro', line: '12' }, + ], + stops: [ + { name: 'Pigalle', lat: 48.88218, lng: 2.33727 }, + { name: 'Pigalle', lat: 48.88197, lng: 2.33707 }, + ], + }, +]; + +describe('nearestAccess', () => { + it('names the stop nearest the start, every line of its station and the walk to it', () => { + const access = nearestAccess({ lat: 48.8845, lng: 2.3385 }, stations); + expect(access).toMatchObject({ station: 'Abbesses', lines: [{ mode: 'metro', line: '12' }] }); + // About 41 m in a straight line, walked × 1.3. + expect(access?.distanceM).toBeGreaterThan(45); + expect(access?.distanceM).toBeLessThan(60); + }); + + it('measures to the nearest stop of a station with several', () => { + const access = nearestAccess({ lat: 48.8819, lng: 2.337 }, stations); + expect(access?.station).toBe('Pigalle'); + expect(access?.lines.map((line) => line.line)).toEqual(['2', '12']); + expect(access?.distanceM).toBeLessThan(20); + }); + + it('gives none beyond a walk, nor without any station', () => { + // Saint-Germain-en-Laye forest: far from both demo stations. + expect(nearestAccess({ lat: 48.92, lng: 2.08 }, stations)).toBeNull(); + expect(nearestAccess(abbesses, [])).toBeNull(); + expect(ACCESS_MAX_M).toBe(1500); + }); +}); + +describe('accessColumnOf', () => { + it('stores the access in snake case, and none without a start', () => { + expect(accessColumnOf({ lat: 48.88459, lng: 2.33795 })).toEqual({ + station: 'Abbesses', + lines: [{ mode: 'metro', line: '12' }], + distance_m: 0, + }); + expect(accessColumnOf(null)).toBeNull(); + }); + + it('finds a station near the start of every demo route, in the reference file', () => { + const places = new Map(demoDataset.places.map((place) => [place.key, place.location])); + for (const route of demoDataset.routes) { + const start = places.get(route.steps[0]?.place ?? ''); + const access = accessColumnOf(start); + expect(access, route.key).not.toBeNull(); + expect(access?.distance_m, route.key).toBeLessThan(1000); + } + }); +}); diff --git a/apps/api/src/transit/access.ts b/apps/api/src/transit/access.ts new file mode 100644 index 00000000..3f64f9ec --- /dev/null +++ b/apps/api/src/transit/access.ts @@ -0,0 +1,48 @@ +/** + * Access of a route (Orchestration › Accès, D-050): the station nearest the first step, its lines + * and the distance walked to it, estimated like a walking trip (D-088): straight line × detour. + * Stored in `routes.computed.access`; null when no station is within a walk. + */ +import { estimateOfMode, straightLineM, WALK_PROPOSAL_MAX_M } from '@widoo/orchestration'; +import type { LatLng } from '@widoo/shared'; +import { transitDataset, type TransitLine, type TransitStation } from './stations'; + +export interface StationAccess { + /** Name of the stop nearest the start: `Abbesses`. */ + station: string; + /** Every line of the station, metro first: « Métro Abbesses, ligne 12 ». */ + lines: TransitLine[]; + /** Walked, estimated: « à 50 m du départ ». */ + distanceM: number; +} + +/** Beyond it, the start is not « near » a station: the walk a trip would propose (1.5 km). */ +export const ACCESS_MAX_M = WALK_PROPOSAL_MAX_M; + +/** The station nearest the point, among the reference stations by default. */ +export function nearestAccess( + location: LatLng, + stations: readonly TransitStation[] = transitDataset.stations, +): StationAccess | null { + let nearest: { station: TransitStation; name: string; straightM: number } | null = null; + for (const station of stations) { + for (const stop of station.stops) { + const straightM = straightLineM(location, stop); + if (!nearest || straightM < nearest.straightM) + nearest = { station, name: stop.name, straightM }; + } + } + if (!nearest) return null; + const distanceM = Math.round(nearest.straightM * estimateOfMode.walk.detour); + if (distanceM > ACCESS_MAX_M) return null; + return { station: nearest.name, lines: nearest.station.lines, distanceM }; +} + +/** `routes.computed.access`: snake_case like the rest of `computed`. */ +export type AccessColumn = { station: string; lines: TransitLine[]; distance_m: number } | null; + +/** The access of a start as `computed` stores it; null without a start or a station near it. */ +export function accessColumnOf(start: LatLng | null | undefined): AccessColumn { + const access = start ? nearestAccess(start) : null; + return access && { station: access.station, lines: access.lines, distance_m: access.distanceM }; +} diff --git a/apps/api/src/transit/generated/stations.json b/apps/api/src/transit/generated/stations.json new file mode 100644 index 00000000..0e9f6454 --- /dev/null +++ b/apps/api/src/transit/generated/stations.json @@ -0,0 +1,772 @@ +{ + "source": {"publisher":"Île-de-France Mobilités","dataset":"Gares et stations du réseau ferré d'Île-de-France (par ligne)","url":"https://data.iledefrance-mobilites.fr/explore/dataset/emplacement-des-gares-idf/","licence":"Licence Ouverte v2.0 (Etalab)","licenceUrl":"https://www.etalab.gouv.fr/licence-ouverte-open-licence/","modifiedOn":"2025-12-10"}, + "stations": [ + {"id":"59420","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Boigneville","lat":48.34166,"lng":2.37882}]}, + {"id":"59447","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Buno-Gironville","lat":48.37138,"lng":2.38721}]}, + {"id":"59470","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Maisse","lat":48.39364,"lng":2.39341}]}, + {"id":"59531","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Saint-Martin-d'Étampes","lat":48.42736,"lng":2.14525}]}, + {"id":"59565","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Boutigny","lat":48.43449,"lng":2.37667}]}, + {"id":"59661","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"La Ferté-Alais","lat":48.48551,"lng":2.35219}]}, + {"id":"59678","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Étréchy","lat":48.49364,"lng":2.19458}]}, + {"id":"59734","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Chamarande","lat":48.51425,"lng":2.21552}]}, + {"id":"59761","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Lardy","lat":48.5204,"lng":2.25474}]}, + {"id":"59825","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Ballancourt","lat":48.5315,"lng":2.37052}]}, + {"id":"59830","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Bouray","lat":48.53289,"lng":2.29008}]}, + {"id":"59836","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Dourdan","lat":48.53373,"lng":2.00877}]}, + {"id":"59842","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Sermaise","lat":48.53581,"lng":2.06938}]}, + {"id":"59843","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Dourdan-la-Forêt","lat":48.53562,"lng":1.9959}]}, + {"id":"59882","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Saint-Chéron","lat":48.55093,"lng":2.12494}]}, + {"id":"59940","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Breuillet-Village","lat":48.56433,"lng":2.17038}]}, + {"id":"59948","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Marolles-en-Hurepoix","lat":48.56559,"lng":2.29092}]}, + {"id":"59953","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Le Coudray-Montceaux","lat":48.56612,"lng":2.49271}]}, + {"id":"59984","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Mennecy","lat":48.5706,"lng":2.4321}]}, + {"id":"60006","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Le Plessis-Chenet","lat":48.57381,"lng":2.47947}]}, + {"id":"60028","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Breuillet","lat":48.57751,"lng":2.19206}]}, + {"id":"60052","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Égly","lat":48.58272,"lng":2.22345}]}, + {"id":"60072","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Arpajon","lat":48.58601,"lng":2.24107}]}, + {"id":"60084","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Moulin-Galant","lat":48.58715,"lng":2.47349}]}, + {"id":"60115","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"La Norville","lat":48.59112,"lng":2.26621}]}, + {"id":"60124","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Villabé","lat":48.59281,"lng":2.46155}]}, + {"id":"60247","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Brétigny","lat":48.60674,"lng":2.30206}]}, + {"id":"60309","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Corbeil-Essonnes","lat":48.61343,"lng":2.47293}]}, + {"id":"60387","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Bois Briard","lat":48.62213,"lng":2.41545}]}, + {"id":"60422","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Le Bras-de-Fer","lat":48.62333,"lng":2.45141}]}, + {"id":"60450","lines":[{"mode":"rer","line":"D"},{"mode":"tram","line":"12"}],"stops":[{"name":"Évry - Courcouronnes","lat":48.62468,"lng":2.42759},{"name":"Évry Courcouronnes","lat":48.6259,"lng":2.43016}]}, + {"id":"60522","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Évry","lat":48.63427,"lng":2.45257}]}, + {"id":"60536","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Saint-Michel-sur-Orge","lat":48.63588,"lng":2.30683}]}, + {"id":"60542","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Orangis-Bois de l'Epine","lat":48.63635,"lng":2.4078}]}, + {"id":"60776","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Ferme Neuve","lat":48.65213,"lng":2.3834}]}, + {"id":"60785","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Sainte-Geneviève-des-Bois","lat":48.65281,"lng":2.31274}]}, + {"id":"60797","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Grigny-Centre","lat":48.65485,"lng":2.39678}]}, + {"id":"60839","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Ris-Orangis","lat":48.65898,"lng":2.41521}]}, + {"id":"60856","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Amédée Gordini","lat":48.65902,"lng":2.37385}]}, + {"id":"60892","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Parc du Château","lat":48.66789,"lng":2.35474}]}, + {"id":"60915","lines":[{"mode":"rer","line":"C"},{"mode":"tram","line":"12"}],"stops":[{"name":"Épinay-sur-Orge","lat":48.66948,"lng":2.33262},{"name":"Épinay-sur-Orge","lat":48.67064,"lng":2.33536}]}, + {"id":"60981","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Viry-Châtillon","lat":48.67609,"lng":2.38678}]}, + {"id":"60985","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Petit-Vaux","lat":48.67638,"lng":2.33325}]}, + {"id":"60987","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Savigny-sur-Orge","lat":48.67645,"lng":2.35248}]}, + {"id":"61850","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Vosves","lat":48.51515,"lng":2.5993}]}, + {"id":"61926","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Melun","lat":48.52724,"lng":2.65499}]}, + {"id":"61943","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Boissise-le-Roi","lat":48.52975,"lng":2.57207}]}, + {"id":"61976","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Ponthierry-Pringy","lat":48.53514,"lng":2.54435}]}, + {"id":"62003","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Le Mée","lat":48.53878,"lng":2.62552}]}, + {"id":"62172","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Saint-Fargeau","lat":48.56507,"lng":2.54261}]}, + {"id":"62176","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Cesson","lat":48.56495,"lng":2.59455}]}, + {"id":"62290","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Savigny-le-Temple-Nandy","lat":48.59556,"lng":2.58395}]}, + {"id":"62412","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Lieusaint-Moissy","lat":48.62838,"lng":2.56958}]}, + {"id":"62558","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Combs-la-Ville - Quincy","lat":48.6679,"lng":2.54755}]}, + {"id":"62596","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Boussy-Saint-Antoine","lat":48.68102,"lng":2.53313}]}, + {"id":"62667","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Gravigny-Balizy","lat":48.68571,"lng":2.31763}]}, + {"id":"62825","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"La Hacquinière","lat":48.69526,"lng":2.15164}]}, + {"id":"62851","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Bures-sur-Yvette","lat":48.69625,"lng":2.16438}]}, + {"id":"62890","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Gif-sur-Yvette","lat":48.6982,"lng":2.13657}]}, + {"id":"62892","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Orsay-Ville","lat":48.69751,"lng":2.18105}]}, + {"id":"62939","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Chilly-Mazarin","lat":48.70068,"lng":2.30784}]}, + {"id":"62951","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Courcelle-sur-Yvette","lat":48.70122,"lng":2.09798}]}, + {"id":"62977","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Longjumeau","lat":48.70224,"lng":2.29456}]}, + {"id":"62978","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Saint-Rémy-lès-Chevreuse","lat":48.70275,"lng":2.07124}]}, + {"id":"63025","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Le Guichet","lat":48.7054,"lng":2.19273}]}, + {"id":"63029","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Lozère","lat":48.70597,"lng":2.21226}]}, + {"id":"63044","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Yerres","lat":48.70661,"lng":2.48424}]}, + {"id":"63067","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Palaiseau-Villebon","lat":48.70814,"lng":2.23731}]}, + {"id":"63071","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Vigneux-sur-Seine","lat":48.70812,"lng":2.41387}]}, + {"id":"63072","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Montgeron-Crosne","lat":48.70824,"lng":2.46228}]}, + {"id":"63134","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Athis-Mons","lat":48.71263,"lng":2.40363}]}, + {"id":"63175","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Palaiseau","lat":48.71695,"lng":2.24567}]}, + {"id":"63244","lines":[{"mode":"rer","line":"B"},{"mode":"rer","line":"C"},{"mode":"tram","line":"12"}],"stops":[{"name":"Massy-Palaiseau","lat":48.72473,"lng":2.25752},{"name":"Massy-Palaiseau","lat":48.726,"lng":2.25832},{"name":"Massy-Palaiseau","lat":48.7251,"lng":2.25995}]}, + {"id":"63278","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Aéroport d'Orly","lat":48.72955,"lng":2.36826}]}, + {"id":"63284","lines":[{"mode":"metro","line":"14"}],"stops":[{"name":"Aéroport d'Orly","lat":48.72808,"lng":2.36259}]}, + {"id":"63320","lines":[{"mode":"rer","line":"B"},{"mode":"rer","line":"C"}],"stops":[{"name":"Massy-Verrières","lat":48.73489,"lng":2.274}]}, + {"id":"63703","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Louvois","lat":48.78009,"lng":2.19651}]}, + {"id":"63706","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Mairie de Vélizy","lat":48.78045,"lng":2.19141}]}, + {"id":"63717","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Inovel Parc Nord","lat":48.7814,"lng":2.20913}]}, + {"id":"63812","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Saint-Quentin-en-Yvelines","lat":48.78739,"lng":2.04462}]}, + {"id":"63880","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Versailles-Chantiers","lat":48.79514,"lng":2.13516}]}, + {"id":"63886","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Porchefontaine","lat":48.79653,"lng":2.15238}]}, + {"id":"63923","lines":[{"mode":"rer","line":"C"},{"mode":"tram","line":"6"}],"stops":[{"name":"Viroflay Rive Gauche","lat":48.80072,"lng":2.17139},{"name":"Viroflay Rive Gauche","lat":48.80108,"lng":2.17097}]}, + {"id":"64049","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"Allée Royale","lat":48.81565,"lng":2.07883}]}, + {"id":"64246","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"Noisy-le-Roi","lat":48.84131,"lng":2.06145}]}, + {"id":"64382","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"Saint-Nom La Bretèche - Forêt de Marly","lat":48.86795,"lng":2.05083}]}, + {"id":"64483","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Châtou-Croissy","lat":48.88521,"lng":2.1559}]}, + {"id":"64514","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Le Vésinet-Centre","lat":48.89009,"lng":2.1346}]}, + {"id":"64566","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"Fourqueux - Bel Air","lat":48.89474,"lng":2.06997}]}, + {"id":"64582","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Le Vésinet-Le Pecq","lat":48.89797,"lng":2.12212}]}, + {"id":"64589","lines":[{"mode":"rer","line":"A"},{"mode":"tram","line":"13"}],"stops":[{"name":"Saint-Germain-en-Laye","lat":48.89808,"lng":2.09484},{"name":"Saint-Germain-en-Laye","lat":48.90171,"lng":2.09521}]}, + {"id":"64622","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"Lisière Pereire","lat":48.9032,"lng":2.07297}]}, + {"id":"64741","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Houilles-Carrières-sur-Seine","lat":48.92038,"lng":2.18535}]}, + {"id":"64758","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Pont de Bezons","lat":48.92327,"lng":2.21759}]}, + {"id":"64883","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Poissy","lat":48.93327,"lng":2.04107}]}, + {"id":"64918","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Sartrouville","lat":48.93774,"lng":2.15709}]}, + {"id":"65048","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Maisons-Laffitte","lat":48.9455,"lng":2.14502}]}, + {"id":"65190","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Achères-Grand-Cormier","lat":48.9554,"lng":2.09352}]}, + {"id":"65227","lines":[{"mode":"tram","line":"11"}],"stops":[{"name":"Épinay Villetaneuse","lat":48.959,"lng":2.32835}]}, + {"id":"65305","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Saint-Gratien","lat":48.96359,"lng":2.28593}]}, + {"id":"65560","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Les Flanades","lat":48.97667,"lng":2.37742}]}, + {"id":"65582","lines":[{"mode":"rer","line":"D"},{"mode":"tram","line":"5"}],"stops":[{"name":"Garges-Sarcelles","lat":48.97716,"lng":2.39067},{"name":"Garges-Sarcelles","lat":48.97675,"lng":2.39059}]}, + {"id":"65586","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Les Cholettes","lat":48.97749,"lng":2.37224}]}, + {"id":"65604","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Lochères","lat":48.97852,"lng":2.38505}]}, + {"id":"65618","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Paul Valéry","lat":48.97928,"lng":2.3802}]}, + {"id":"65669","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Ermont-Eaubonne","lat":48.98065,"lng":2.27208}]}, + {"id":"65752","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Cernay","lat":48.985,"lng":2.25747}]}, + {"id":"66005","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Franconville-Le Plessis-Bouchard","lat":48.99371,"lng":2.23428}]}, + {"id":"66009","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Villiers-le-Bel-Gonesse-Arnouville","lat":48.99378,"lng":2.41619}]}, + {"id":"66335","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Montigny-Beauchamp","lat":49.00777,"lng":2.1965}]}, + {"id":"66436","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Neuville Université","lat":49.01413,"lng":2.07831}]}, + {"id":"66491","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Pierrelaye","lat":49.01914,"lng":2.15466}]}, + {"id":"66535","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Goussainville","lat":49.02316,"lng":2.46217}]}, + {"id":"66650","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Les Noues","lat":49.03318,"lng":2.47803}]}, + {"id":"66659","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Saint-Ouen-l'Aumône-Liesse","lat":49.03399,"lng":2.12826}]}, + {"id":"66696","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Cergy-Préfecture","lat":49.03646,"lng":2.07953}]}, + {"id":"66803","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Saint-Ouen-l'Aumône","lat":49.04531,"lng":2.10511}]}, + {"id":"66816","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Pontoise","lat":49.04665,"lng":2.0952}]}, + {"id":"66834","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Cergy Le Haut","lat":49.04788,"lng":2.011}]}, + {"id":"66858","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Cergy-Saint-Christophe","lat":49.04965,"lng":2.03429}]}, + {"id":"67476","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Brunoy","lat":48.69911,"lng":2.50668}]}, + {"id":"67625","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Tournan","lat":48.73949,"lng":2.75882}]}, + {"id":"67658","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Gretz-Armainvilliers","lat":48.74645,"lng":2.72723}]}, + {"id":"67839","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Roissy-en-Brie","lat":48.79565,"lng":2.65039}]}, + {"id":"67897","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Émerainville-Pontault-Combault","lat":48.80584,"lng":2.61919}]}, + {"id":"68105","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Bussy-Saint-Georges","lat":48.83654,"lng":2.70898}]}, + {"id":"68123","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Lognes","lat":48.83922,"lng":2.63321}]}, + {"id":"68129","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Torcy","lat":48.83965,"lng":2.65513}]}, + {"id":"68153","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Noisiel","lat":48.84351,"lng":2.61668}]}, + {"id":"68266","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Val d'Europe","lat":48.85482,"lng":2.77243}]}, + {"id":"68293","lines":[{"mode":"tram","line":"14"}],"stops":[{"name":"Crécy-la-Chapelle","lat":48.85956,"lng":2.90574}]}, + {"id":"68311","lines":[{"mode":"tram","line":"14"}],"stops":[{"name":"Villiers-Montbarbin","lat":48.86356,"lng":2.88259}]}, + {"id":"68385","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Marne-la-Vallée-Chessy","lat":48.86995,"lng":2.78223}]}, + {"id":"68407","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Chelles-Gournay","lat":48.87414,"lng":2.58333}]}, + {"id":"68505","lines":[{"mode":"tram","line":"14"}],"stops":[{"name":"Couilly-Saint-Germain-Quincy","lat":48.88268,"lng":2.85432}]}, + {"id":"68582","lines":[{"mode":"tram","line":"14"}],"stops":[{"name":"Montry-Condé","lat":48.89086,"lng":2.82641}]}, + {"id":"68653","lines":[{"mode":"tram","line":"14"}],"stops":[{"name":"Esbly","lat":48.90307,"lng":2.81025}]}, + {"id":"68916","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Villeparisis-Mitry-le-Neuf","lat":48.95315,"lng":2.60314}]}, + {"id":"69065","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Mitry-Claye","lat":48.97586,"lng":2.64241}]}, + {"id":"69300","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Louvres","lat":49.04933,"lng":2.50176}]}, + {"id":"69450","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Survilliers-Fosses","lat":49.09959,"lng":2.52553}]}, + {"id":"69466","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Meudon-la-Forêt","lat":48.78354,"lng":2.22528}]}, + {"id":"69487","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Chaville-Vélizy","lat":48.79973,"lng":2.18368}]}, + {"id":"69533","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Brimborion","lat":48.82231,"lng":2.23132}]}, + {"id":"69554","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Ablon","lat":48.72554,"lng":2.41957}]}, + {"id":"69568","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Villeneuve-Saint-Georges","lat":48.73064,"lng":2.44623}]}, + {"id":"69577","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Coeur d'Orly","lat":48.7337,"lng":2.37141}]}, + {"id":"69606","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Caroline Aigle","lat":48.7386,"lng":2.36991}]}, + {"id":"69622","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Les Baconnets","lat":48.73968,"lng":2.28767}]}, + {"id":"69625","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Villeneuve-le-Roi","lat":48.73983,"lng":2.4264}]}, + {"id":"69633","lines":[{"mode":"rer","line":"C"},{"mode":"tram","line":"7"}],"stops":[{"name":"La Fraternelle","lat":48.74168,"lng":2.3528},{"name":"Rungis-La Fraternelle","lat":48.7402,"lng":2.35163}]}, + {"id":"69639","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Orly Ville","lat":48.74188,"lng":2.40291}]}, + {"id":"69647","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Fontaine-Michalon","lat":48.74302,"lng":2.29592}]}, + {"id":"69656","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Orly - Gaston Viens","lat":48.745,"lng":2.40832}]}, + {"id":"69662","lines":[{"mode":"rer","line":"C"},{"mode":"tram","line":"9"}],"stops":[{"name":"Les Saules","lat":48.74694,"lng":2.41632},{"name":"Les Saules","lat":48.74507,"lng":2.41752}]}, + {"id":"69670","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Robert Schuman","lat":48.74657,"lng":2.35288}]}, + {"id":"69671","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Villeneuve-Triage","lat":48.7445,"lng":2.43867}]}, + {"id":"69677","lines":[{"mode":"metro","line":"14"},{"mode":"rer","line":"C"}],"stops":[{"name":"Thiais - Orly","lat":48.74698,"lng":2.37284},{"name":"Pont de Rungis","lat":48.74835,"lng":2.37311}]}, + {"id":"69682","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Chemin d'Antony","lat":48.74794,"lng":2.31271}]}, + {"id":"69700","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Saarinen","lat":48.75021,"lng":2.35439}]}, + {"id":"69719","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Christophe Colomb","lat":48.75211,"lng":2.41695}]}, + {"id":"69721","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Porte de Rungis","lat":48.75267,"lng":2.35536}]}, + {"id":"69729","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Place de la Logistique","lat":48.75281,"lng":2.36195}]}, + {"id":"69742","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Four-Peary","lat":48.75455,"lng":2.4119}]}, + {"id":"69759","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Antony","lat":48.75495,"lng":2.30099}]}, + {"id":"69762","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"La Belle Épine","lat":48.7567,"lng":2.36837}]}, + {"id":"69781","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Carle-Darthe","lat":48.7592,"lng":2.40888}]}, + {"id":"69807","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"LaVallée","lat":48.76142,"lng":2.29044}]}, + {"id":"69813","lines":[{"mode":"rer","line":"B"},{"mode":"tram","line":"10"}],"stops":[{"name":"La Croix-de-Berny-Fresnes","lat":48.76169,"lng":2.30423},{"name":"La Croix de Berny","lat":48.76297,"lng":2.30475}]}, + {"id":"69814","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Petit-Châtenay","lat":48.76273,"lng":2.2807}]}, + {"id":"69837","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Auguste Perret","lat":48.76338,"lng":2.36779}]}, + {"id":"69839","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Les Peintres","lat":48.76472,"lng":2.26588}]}, + {"id":"69846","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Rouget de Lisle","lat":48.76391,"lng":2.40581}]}, + {"id":"69852","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Choisy-le-Roi","lat":48.76379,"lng":2.41139}]}, + {"id":"69870","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Verdun Hoche","lat":48.76844,"lng":2.40286}]}, + {"id":"69884","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Créteil - Pointe du Lac","lat":48.76881,"lng":2.46428}]}, + {"id":"69888","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Bretagne","lat":48.76819,"lng":2.36804}]}, + {"id":"69906","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Parc de Sceaux","lat":48.76956,"lng":2.30991}]}, + {"id":"69925","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Moulin Vert","lat":48.77207,"lng":2.36782}]}, + {"id":"69939","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Trois communes","lat":48.77299,"lng":2.39936}]}, + {"id":"69950","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Créteil Pompadour","lat":48.77127,"lng":2.43531}]}, + {"id":"69974","lines":[{"mode":"metro","line":"14"}],"stops":[{"name":"L'Haÿ - les - Roses","lat":48.77531,"lng":2.35436}]}, + {"id":"69978","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Noveos","lat":48.77669,"lng":2.25003}]}, + {"id":"70001","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Watteau - Rondenay","lat":48.77867,"lng":2.39636}]}, + {"id":"70009","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Parc des Sports","lat":48.77984,"lng":2.25189}]}, + {"id":"70021","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Créteil–Préfecture","lat":48.78,"lng":2.45936}]}, + {"id":"70022","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Domaine Chérioux","lat":48.77894,"lng":2.36741}]}, + {"id":"70027","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Constant Coquelin","lat":48.78301,"lng":2.39384}]}, + {"id":"70033","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Bourg-la-Reine","lat":48.78031,"lng":2.31231}]}, + {"id":"70036","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Lamartine","lat":48.78311,"lng":2.36716}]}, + {"id":"70049","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Pavé Blanc","lat":48.78073,"lng":2.23983}]}, + {"id":"70060","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Le Hameau","lat":48.78259,"lng":2.25313}]}, + {"id":"70064","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Les Ardoines","lat":48.78223,"lng":2.40959}]}, + {"id":"70099","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Mail de la Plaine","lat":48.78369,"lng":2.24698}]}, + {"id":"70101","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Camille Groult","lat":48.78567,"lng":2.39199}]}, + {"id":"70133","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Fontenay-aux-Roses","lat":48.78757,"lng":2.29299}]}, + {"id":"70143","lines":[{"mode":"metro","line":"7"},{"mode":"tram","line":"7"}],"stops":[{"name":"Villejuif-Louis Aragon","lat":48.78728,"lng":2.36699},{"name":"Villejuif - Louis Aragon","lat":48.78756,"lng":2.3678}]}, + {"id":"70149","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Georges Pompidou","lat":48.7849,"lng":2.23713}]}, + {"id":"70163","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Jardin Parisien","lat":48.79007,"lng":2.25272}]}, + {"id":"70169","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Mairie de Vitry","lat":48.79043,"lng":2.38824}]}, + {"id":"70172","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Créteil-Université","lat":48.78992,"lng":2.45051}]}, + {"id":"70186","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Soleil Levant","lat":48.79033,"lng":2.26339}]}, + {"id":"70198","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Musée MAC VAL","lat":48.79289,"lng":2.38621}]}, + {"id":"70210","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Bagneux","lat":48.79334,"lng":2.3222}]}, + {"id":"70222","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Division Leclerc","lat":48.79447,"lng":2.27265}]}, + {"id":"70248","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Villejuif Paul Vaillant-Couturier","lat":48.79648,"lng":2.36827}]}, + {"id":"70263","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Arcueil-Cachan","lat":48.79889,"lng":2.32814}]}, + {"id":"70269","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Créteil–L’Échat","lat":48.79668,"lng":2.44941}]}, + {"id":"70273","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Beethoven - Concorde","lat":48.79882,"lng":2.38151}]}, + {"id":"70310","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Parc André Malraux","lat":48.79989,"lng":2.28195}]}, + {"id":"70313","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Vitry-sur-Seine","lat":48.80021,"lng":2.40271}]}, + {"id":"70350","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Germaine Tailleferre","lat":48.80335,"lng":2.37779}]}, + {"id":"70358","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Maisons-Alfort-Les Juilliottes","lat":48.80336,"lng":2.44557}]}, + {"id":"70359","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Le Parc de Saint-Maur","lat":48.80538,"lng":2.48635}]}, + {"id":"70362","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Centre de Châtillon","lat":48.8042,"lng":2.28906}]}, + {"id":"70375","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Villejuif - Léo Lagrange","lat":48.80469,"lng":2.36395}]}, + {"id":"70391","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"La Briqueterie","lat":48.80721,"lng":2.37491}]}, + {"id":"70393","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Saint-Maur-Créteil","lat":48.8062,"lng":2.47272}]}, + {"id":"70396","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Vauban","lat":48.80717,"lng":2.29409}]}, + {"id":"70412","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Meudon-Val-Fleury","lat":48.80749,"lng":2.24088}]}, + {"id":"70426","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Maisons-Alfort-Stade","lat":48.80914,"lng":2.43451}]}, + {"id":"70427","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Laplace","lat":48.8083,"lng":2.33435}]}, + {"id":"70441","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Barbara","lat":48.8097,"lng":2.31745}]}, + {"id":"70445","lines":[{"mode":"metro","line":"14"}],"stops":[{"name":"Hôpital Bicêtre","lat":48.80981,"lng":2.34975}]}, + {"id":"70452","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Le Kremlin-Bicêtre","lat":48.81013,"lng":2.36226}]}, + {"id":"70486","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Cimetière Parisien d'Ivry","lat":48.81263,"lng":2.37066}]}, + {"id":"70488","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Mairie d'Ivry","lat":48.81052,"lng":2.38429}]}, + {"id":"70522","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"École Vétérinaire de Maisons-Alfort","lat":48.8148,"lng":2.42238}]}, + {"id":"70537","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Pierre et Marie Curie","lat":48.81593,"lng":2.37727}]}, + {"id":"70543","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Gentilly","lat":48.81523,"lng":2.34089}]}, + {"id":"70549","lines":[{"mode":"tram","line":"9"}],"stops":[{"name":"Châteaudun - Barbès","lat":48.81557,"lng":2.36834}]}, + {"id":"70562","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Malakoff - Rue Étienne Dolet","lat":48.81531,"lng":2.29708}]}, + {"id":"70586","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Mairie de Montrouge","lat":48.81868,"lng":2.31974}]}, + {"id":"70587","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Meudon-sur-Seine","lat":48.81917,"lng":2.2386}]}, + {"id":"70596","lines":[{"mode":"metro","line":"7"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Porte d'Italie","lat":48.81922,"lng":2.35971},{"name":"Porte d'Italie","lat":48.81905,"lng":2.36013}]}, + {"id":"70604","lines":[{"mode":"metro","line":"7"},{"mode":"tram","line":"3a"},{"mode":"tram","line":"9"}],"stops":[{"name":"Porte de Choisy","lat":48.81988,"lng":2.36453},{"name":"Porte de Choisy","lat":48.81945,"lng":2.36519},{"name":"Porte de Choisy","lat":48.81976,"lng":2.36402}]}, + {"id":"70605","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Stade Charléty","lat":48.8195,"lng":2.3458}]}, + {"id":"70612","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Issy","lat":48.81934,"lng":2.25789}]}, + {"id":"70620","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Montsouris","lat":48.82151,"lng":2.33334}]}, + {"id":"70622","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Les Moulineaux","lat":48.82152,"lng":2.25134}]}, + {"id":"70623","lines":[{"mode":"rer","line":"B"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Cité Universitaire","lat":48.82109,"lng":2.33898},{"name":"Cité Universitaire","lat":48.82035,"lng":2.33858}]}, + {"id":"70626","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Poterne des Peupliers","lat":48.82087,"lng":2.35093}]}, + {"id":"70636","lines":[{"mode":"metro","line":"7"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Porte d'Ivry","lat":48.82136,"lng":2.36929},{"name":"Porte d'Ivry","lat":48.82183,"lng":2.37005}]}, + {"id":"70640","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Joinville-le-Pont","lat":48.82104,"lng":2.46386}]}, + {"id":"70645","lines":[{"mode":"metro","line":"7"},{"mode":"metro","line":"14"}],"stops":[{"name":"Maison Blanche","lat":48.82218,"lng":2.35855},{"name":"Maison Blanche","lat":48.82142,"lng":2.35906}]}, + {"id":"70648","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Charenton-Écoles","lat":48.8219,"lng":2.41313}]}, + {"id":"70657","lines":[{"mode":"metro","line":"4"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Porte d'Orléans","lat":48.82341,"lng":2.32558},{"name":"Porte d'Orléans","lat":48.82315,"lng":2.32551}]}, + {"id":"70662","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Jacques-Henri Lartigue","lat":48.82471,"lng":2.26048}]}, + {"id":"70669","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Jean Moulin","lat":48.82471,"lng":2.31883}]}, + {"id":"70671","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Mairie d'Issy","lat":48.82431,"lng":2.27367}]}, + {"id":"70692","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Musée de Sèvres","lat":48.82901,"lng":2.22531}]}, + {"id":"70698","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Pont de Sèvres","lat":48.82975,"lng":2.23103}]}, + {"id":"70721","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Boulogne - Pont de Saint-Cloud","lat":48.84075,"lng":2.22854}]}, + {"id":"70726","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Parc de Saint-Cloud","lat":48.84312,"lng":2.22185}]}, + {"id":"70763","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Les Coteaux","lat":48.85747,"lng":2.2206}]}, + {"id":"70811","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Suresnes-Longchamp","lat":48.86824,"lng":2.22141}]}, + {"id":"70845","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Belvédère","lat":48.876,"lng":2.22596}]}, + {"id":"70902","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Rueil-Malmaison","lat":48.88757,"lng":2.17258}]}, + {"id":"70940","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Nanterre-Ville","lat":48.89513,"lng":2.19536}]}, + {"id":"70945","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Nanterre-Préfecture","lat":48.89575,"lng":2.22321}]}, + {"id":"70956","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Nanterre-Université","lat":48.90101,"lng":2.21356}]}, + {"id":"70969","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Jacqueline Auriol","lat":48.9108,"lng":2.23399}]}, + {"id":"70972","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Victor Basch","lat":48.91405,"lng":2.22957}]}, + {"id":"70975","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Parc Pierre Lagravère","lat":48.91751,"lng":2.22481}]}, + {"id":"70983","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Didot","lat":48.82592,"lng":2.31338}]}, + {"id":"70990","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Corentin Celton","lat":48.82709,"lng":2.27941}]}, + {"id":"71001","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Tolbiac","lat":48.82643,"lng":2.35725}]}, + {"id":"71006","lines":[{"mode":"metro","line":"13"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Porte de Vanves","lat":48.82741,"lng":2.30664},{"name":"Porte de Vanves","lat":48.8278,"lng":2.30553}]}, + {"id":"71007","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Brancion","lat":48.82869,"lng":2.3008}]}, + {"id":"71013","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Corvisart","lat":48.82979,"lng":2.35041}]}, + {"id":"71016","lines":[{"mode":"rer","line":"C"},{"mode":"tram","line":"2"}],"stops":[{"name":"Issy-Val-de-Seine","lat":48.8306,"lng":2.26407},{"name":"Issy-Val-de-Seine","lat":48.82989,"lng":2.26318}]}, + {"id":"71018","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Georges Brassens","lat":48.82983,"lng":2.29566}]}, + {"id":"71026","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Glacière","lat":48.83113,"lng":2.34351}]}, + {"id":"71030","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Alésia","lat":48.82819,"lng":2.32716}]}, + {"id":"71033","lines":[{"mode":"metro","line":"5"},{"mode":"metro","line":"6"},{"mode":"metro","line":"7"}],"stops":[{"name":"Place d'Italie","lat":48.83093,"lng":2.35612},{"name":"Place d'Italie","lat":48.83143,"lng":2.35564},{"name":"Place d'Italie","lat":48.83097,"lng":2.35558}]}, + {"id":"71034","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Billancourt","lat":48.83174,"lng":2.23744}]}, + {"id":"71039","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Plaisance","lat":48.83183,"lng":2.31412}]}, + {"id":"71041","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Saint-Jacques","lat":48.8329,"lng":2.33714}]}, + {"id":"71042","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Suzanne Lenglen","lat":48.83331,"lng":2.27685}]}, + {"id":"71043","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Nationale","lat":48.83322,"lng":2.36286}]}, + {"id":"71045","lines":[{"mode":"metro","line":"12"},{"mode":"tram","line":"2"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Porte de Versailles","lat":48.83187,"lng":2.28721},{"name":"Porte de Versailles","lat":48.83245,"lng":2.28802},{"name":"Porte de Versailles","lat":48.83268,"lng":2.28807}]}, + {"id":"71054","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Marcel Sembat","lat":48.83363,"lng":2.24358}]}, + {"id":"71055","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Desnouettes","lat":48.83436,"lng":2.28401}]}, + {"id":"71056","lines":[{"mode":"metro","line":"4"},{"mode":"metro","line":"6"},{"mode":"rer","line":"B"}],"stops":[{"name":"Denfert-Rochereau","lat":48.83307,"lng":2.3332},{"name":"Denfert-Rochereau","lat":48.83345,"lng":2.33174},{"name":"Denfert-Rochereau","lat":48.83429,"lng":2.33285}]}, + {"id":"71063","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Campo Formio","lat":48.83515,"lng":2.35857}]}, + {"id":"71073","lines":[{"mode":"metro","line":"8"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Balard","lat":48.83612,"lng":2.27812},{"name":"Balard","lat":48.8363,"lng":2.27833}]}, + {"id":"71075","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Convention","lat":48.8371,"lng":2.29623}]}, + {"id":"71076","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Les Gobelins","lat":48.83593,"lng":2.35254}]}, + {"id":"71084","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Porte de Saint-Cloud","lat":48.83796,"lng":2.25712}]}, + {"id":"71086","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Lourmel","lat":48.83873,"lng":2.28214}]}, + {"id":"71088","lines":[{"mode":"metro","line":"4"},{"mode":"metro","line":"6"}],"stops":[{"name":"Raspail","lat":48.83896,"lng":2.33075}]}, + {"id":"71091","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Vaugirard","lat":48.83952,"lng":2.30111}]}, + {"id":"71092","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Saint-Marcel","lat":48.83843,"lng":2.36097}]}, + {"id":"71100","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Censier-Daubenton","lat":48.84023,"lng":2.35161}]}, + {"id":"71106","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Port Royal","lat":48.84003,"lng":2.3371}]}, + {"id":"71108","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Edgar Quinet","lat":48.84066,"lng":2.32638}]}, + {"id":"71113","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Volontaires","lat":48.84151,"lng":2.30795}]}, + {"id":"71117","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Vavin","lat":48.84222,"lng":2.32901}]}, + {"id":"71124","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Exelmans","lat":48.84241,"lng":2.25961}]}, + {"id":"71125","lines":[{"mode":"metro","line":"6"},{"mode":"metro","line":"12"}],"stops":[{"name":"Pasteur","lat":48.84268,"lng":2.31281},{"name":"Pasteur","lat":48.84287,"lng":2.31245}]}, + {"id":"71132","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Commerce","lat":48.84479,"lng":2.29395}]}, + {"id":"71133","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Place Monge","lat":48.84287,"lng":2.35222}]}, + {"id":"71135","lines":[{"mode":"metro","line":"5"},{"mode":"metro","line":"10"},{"mode":"rer","line":"C"}],"stops":[{"name":"Gare d'Austerlitz","lat":48.84236,"lng":2.36558},{"name":"Gare d'Austerlitz","lat":48.84244,"lng":2.36518},{"name":"Paris Austerlitz","lat":48.84253,"lng":2.36543}]}, + {"id":"71137","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Félix Faure","lat":48.84275,"lng":2.29191}]}, + {"id":"71138","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Notre-Dame-des-Champs","lat":48.84494,"lng":2.32878}]}, + {"id":"71139","lines":[{"mode":"metro","line":"4"},{"mode":"metro","line":"6"},{"mode":"metro","line":"12"},{"mode":"metro","line":"13"}],"stops":[{"name":"Montparnasse-Bienvenüe","lat":48.84233,"lng":2.32098},{"name":"Montparnasse Bienvenüe","lat":48.84374,"lng":2.32435},{"name":"Montparnasse-Bienvenüe","lat":48.8421,"lng":2.32128},{"name":"Montparnasse-Bienvenüe","lat":48.84378,"lng":2.3244}]}, + {"id":"71141","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Chardon Lagache","lat":48.8452,"lng":2.26644}]}, + {"id":"71144","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Sèvres-Lecourbe","lat":48.84563,"lng":2.30953}]}, + {"id":"71147","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Boulogne - Jean Jaurès","lat":48.84201,"lng":2.23889}]}, + {"id":"71148","lines":[{"mode":"metro","line":"7"},{"mode":"metro","line":"10"}],"stops":[{"name":"Jussieu","lat":48.84595,"lng":2.35505},{"name":"Jussieu","lat":48.84615,"lng":2.35492}]}, + {"id":"71150","lines":[{"mode":"metro","line":"10"},{"mode":"rer","line":"C"}],"stops":[{"name":"Javel - André Citroën","lat":48.84616,"lng":2.2784},{"name":"Javel","lat":48.84716,"lng":2.27804}]}, + {"id":"71153","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Cardinal Lemoine","lat":48.84666,"lng":2.35162}]}, + {"id":"71156","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Charles Michels","lat":48.84653,"lng":2.286}]}, + {"id":"71157","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Ségur","lat":48.84707,"lng":2.3074}]}, + {"id":"71158","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Avenue Émile Zola","lat":48.847,"lng":2.29502}]}, + {"id":"71159","lines":[{"mode":"metro","line":"10"},{"mode":"metro","line":"13"}],"stops":[{"name":"Duroc","lat":48.84705,"lng":2.31642},{"name":"Duroc","lat":48.84699,"lng":2.31701}]}, + {"id":"71161","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Luxembourg","lat":48.84543,"lng":2.33976}]}, + {"id":"71162","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Mirabeau","lat":48.84713,"lng":2.27276}]}, + {"id":"71166","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Église d'Auteuil","lat":48.84715,"lng":2.26914}]}, + {"id":"71167","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Cambronne","lat":48.84751,"lng":2.30297}]}, + {"id":"71169","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Porte d'Auteuil","lat":48.84797,"lng":2.25774}]}, + {"id":"71170","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Vaneau","lat":48.84882,"lng":2.32126}]}, + {"id":"71184","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Saint-Placide","lat":48.84688,"lng":2.32679}]}, + {"id":"71190","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Maubert-Mutualité","lat":48.85009,"lng":2.34821}]}, + {"id":"71197","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Dupleix","lat":48.85043,"lng":2.29361}]}, + {"id":"71199","lines":[{"mode":"metro","line":"6"},{"mode":"metro","line":"8"},{"mode":"metro","line":"10"}],"stops":[{"name":"La Motte Picquet-Grenelle","lat":48.84958,"lng":2.29863},{"name":"La Motte Picquet-Grenelle","lat":48.84874,"lng":2.29893}]}, + {"id":"71201","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Sully-Morland","lat":48.85162,"lng":2.36152}]}, + {"id":"71202","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Saint-Sulpice","lat":48.85107,"lng":2.3307}]}, + {"id":"71203","lines":[{"mode":"metro","line":"10"},{"mode":"metro","line":"12"}],"stops":[{"name":"Sèvres-Babylone","lat":48.85176,"lng":2.32682},{"name":"Sèvres-Babylone","lat":48.85103,"lng":2.3261}]}, + {"id":"71204","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Saint-François-Xavier","lat":48.85144,"lng":2.31441}]}, + {"id":"71206","lines":[{"mode":"metro","line":"9"},{"mode":"metro","line":"10"}],"stops":[{"name":"Michel Ange-Auteuil","lat":48.8479,"lng":2.26408},{"name":"Michel Ange-Auteuil","lat":48.84793,"lng":2.26422}]}, + {"id":"71212","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Avenue du Président Kennedy","lat":48.85365,"lng":2.27949}]}, + {"id":"71216","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Saint-Germain-des-Prés","lat":48.85372,"lng":2.33378}]}, + {"id":"71217","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Pont Marie","lat":48.85358,"lng":2.35716}]}, + {"id":"71222","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Saint-Paul","lat":48.85524,"lng":2.36096}]}, + {"id":"71223","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Cité","lat":48.85494,"lng":2.34732}]}, + {"id":"71224","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"École Militaire","lat":48.85489,"lng":2.3064}]}, + {"id":"71227","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Rue du Bac","lat":48.8552,"lng":2.32582}]}, + {"id":"71229","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Boulainvilliers","lat":48.85697,"lng":2.27479}]}, + {"id":"71232","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Varenne","lat":48.85687,"lng":2.31506}]}, + {"id":"71237","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Passy","lat":48.85745,"lng":2.28581}]}, + {"id":"71242","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"La Tour Maubourg","lat":48.85773,"lng":2.31057}]}, + {"id":"71243","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Ranelagh","lat":48.85549,"lng":2.27011}]}, + {"id":"71253","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Pont Neuf","lat":48.85855,"lng":2.34223}]}, + {"id":"71264","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"4"},{"mode":"metro","line":"7"},{"mode":"metro","line":"11"},{"mode":"metro","line":"14"}],"stops":[{"name":"Châtelet","lat":48.85959,"lng":2.34648},{"name":"Châtelet","lat":48.85763,"lng":2.34804},{"name":"Châtelet","lat":48.85972,"lng":2.34677},{"name":"Châtelet","lat":48.85694,"lng":2.34815},{"name":"Châtelet","lat":48.85929,"lng":2.3458}]}, + {"id":"71269","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Musée d'Orsay","lat":48.86035,"lng":2.32676}]}, + {"id":"71270","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Solférino","lat":48.8585,"lng":2.32317}]}, + {"id":"71271","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Pont de l'Alma","lat":48.86266,"lng":2.30099}]}, + {"id":"71273","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Rambuteau","lat":48.8612,"lng":2.3535}]}, + {"id":"71274","lines":[{"mode":"metro","line":"8"},{"mode":"metro","line":"13"},{"mode":"rer","line":"C"}],"stops":[{"name":"Invalides","lat":48.86288,"lng":2.31251},{"name":"Invalides","lat":48.86003,"lng":2.31392},{"name":"Invalides","lat":48.86038,"lng":2.31489}]}, + {"id":"71277","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Filles du Calvaire","lat":48.86326,"lng":2.36666}]}, + {"id":"71284","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Tuileries","lat":48.86434,"lng":2.33013}]}, + {"id":"71285","lines":[{"mode":"metro","line":"6"},{"mode":"metro","line":"9"}],"stops":[{"name":"Trocadéro","lat":48.86361,"lng":2.28758},{"name":"Trocadéro","lat":48.86307,"lng":2.28624}]}, + {"id":"71286","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Avenue Henri Martin","lat":48.86546,"lng":2.27216}]}, + {"id":"71290","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Alma-Marceau","lat":48.86477,"lng":2.30099}]}, + {"id":"71292","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Rue de la Pompe","lat":48.86394,"lng":2.27844}]}, + {"id":"71293","lines":[{"mode":"metro","line":"3"},{"mode":"metro","line":"11"}],"stops":[{"name":"Arts et Métiers","lat":48.86532,"lng":2.35673},{"name":"Arts et Métiers","lat":48.86559,"lng":2.35674}]}, + {"id":"71294","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Iéna","lat":48.86479,"lng":2.29416}]}, + {"id":"71296","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Temple","lat":48.86651,"lng":2.3608}]}, + {"id":"71297","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"7"}],"stops":[{"name":"Palais Royal - Musée du Louvre","lat":48.86283,"lng":2.33688},{"name":"Palais Royal - Musée du Louvre","lat":48.86235,"lng":2.3365}]}, + {"id":"71298","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"8"},{"mode":"metro","line":"12"}],"stops":[{"name":"Concorde","lat":48.86617,"lng":2.32318},{"name":"Concorde","lat":48.86682,"lng":2.32208},{"name":"Concorde","lat":48.86629,"lng":2.32294}]}, + {"id":"71301","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Boissière","lat":48.86698,"lng":2.29006}]}, + {"id":"71305","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"13"}],"stops":[{"name":"Champs-Élysées – Clemenceau","lat":48.8676,"lng":2.31389},{"name":"Champs-Élysées – Clemenceau","lat":48.86791,"lng":2.31355}]}, + {"id":"71311","lines":[{"mode":"metro","line":"3"},{"mode":"metro","line":"5"},{"mode":"metro","line":"8"},{"mode":"metro","line":"9"},{"mode":"metro","line":"11"}],"stops":[{"name":"République","lat":48.868,"lng":2.36507},{"name":"République","lat":48.86703,"lng":2.36425},{"name":"République","lat":48.86783,"lng":2.36374},{"name":"République","lat":48.8676,"lng":2.36276},{"name":"République","lat":48.86778,"lng":2.36293}]}, + {"id":"71313","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Quatre Septembre","lat":48.86964,"lng":2.33623}]}, + {"id":"71315","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Victor Hugo","lat":48.8698,"lng":2.28534}]}, + {"id":"71318","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"9"}],"stops":[{"name":"Franklin D. Roosevelt","lat":48.8687,"lng":2.30951},{"name":"Franklin D. Roosevelt","lat":48.86944,"lng":2.30861}]}, + {"id":"71320","lines":[{"mode":"metro","line":"8"},{"mode":"metro","line":"9"}],"stops":[{"name":"Bonne Nouvelle","lat":48.87059,"lng":2.34849}]}, + {"id":"71321","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Avenue Foch","lat":48.86983,"lng":2.27481}]}, + {"id":"71322","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Jacques Bonsergent","lat":48.8704,"lng":2.3614}]}, + {"id":"71324","lines":[{"mode":"metro","line":"8"},{"mode":"metro","line":"12"},{"mode":"metro","line":"14"}],"stops":[{"name":"Madeleine","lat":48.87067,"lng":2.32575},{"name":"Madeleine","lat":48.86963,"lng":2.32627},{"name":"Madeleine","lat":48.87023,"lng":2.32516}]}, + {"id":"71326","lines":[{"mode":"metro","line":"8"},{"mode":"metro","line":"9"}],"stops":[{"name":"Grands Boulevards","lat":48.87126,"lng":2.34456}]}, + {"id":"71328","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"George V","lat":48.87202,"lng":2.30056}]}, + {"id":"71330","lines":[{"mode":"metro","line":"8"},{"mode":"metro","line":"9"}],"stops":[{"name":"Richelieu-Drouot","lat":48.87169,"lng":2.33846},{"name":"Richelieu-Drouot","lat":48.87219,"lng":2.33886}]}, + {"id":"71333","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Château d'Eau","lat":48.87262,"lng":2.35615}]}, + {"id":"71334","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Saint-Philippe du Roule","lat":48.87205,"lng":2.31002}]}, + {"id":"71337","lines":[{"mode":"metro","line":"3"},{"mode":"metro","line":"7"},{"mode":"metro","line":"8"}],"stops":[{"name":"Opéra","lat":48.87131,"lng":2.33149},{"name":"Opéra","lat":48.87048,"lng":2.33119},{"name":"Opéra","lat":48.86994,"lng":2.33259}]}, + {"id":"71346","lines":[{"mode":"metro","line":"9"},{"mode":"metro","line":"13"}],"stops":[{"name":"Miromesnil","lat":48.87373,"lng":2.31474},{"name":"Miromesnil","lat":48.87377,"lng":2.31446}]}, + {"id":"71347","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"2"},{"mode":"metro","line":"6"},{"mode":"rer","line":"A"}],"stops":[{"name":"Charles De Gaulle-Étoile","lat":48.8742,"lng":2.29477},{"name":"Charles De Gaulle-Étoile","lat":48.87475,"lng":2.29556},{"name":"Charles De Gaulle-Étoile","lat":48.87515,"lng":2.2959},{"name":"Charles De Gaulle-Étoile","lat":48.87481,"lng":2.2958}]}, + {"id":"71348","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Argentine","lat":48.87534,"lng":2.29013}]}, + {"id":"71350","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Notre-Dame-de-Lorette","lat":48.87605,"lng":2.3375}]}, + {"id":"71351","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Cadet","lat":48.87601,"lng":2.34457}]}, + {"id":"71355","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Trinité d'Estienne d'Orves","lat":48.87633,"lng":2.33311}]}, + {"id":"71359","lines":[{"mode":"metro","line":"4"},{"mode":"metro","line":"5"},{"mode":"metro","line":"7"}],"stops":[{"name":"Gare de l'Est","lat":48.87613,"lng":2.35805},{"name":"Gare de l'Est","lat":48.87599,"lng":2.35788},{"name":"Gare de l'Est","lat":48.87627,"lng":2.35806}]}, + {"id":"71363","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Poissonnière","lat":48.8771,"lng":2.34878}]}, + {"id":"71366","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Saint-Georges","lat":48.87817,"lng":2.33786}]}, + {"id":"71367","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Ternes","lat":48.87797,"lng":2.29831}]}, + {"id":"71370","lines":[{"mode":"metro","line":"3"},{"mode":"metro","line":"12"},{"mode":"metro","line":"13"},{"mode":"metro","line":"14"}],"stops":[{"name":"Saint-Lazare","lat":48.87567,"lng":2.32784},{"name":"Saint-Lazare","lat":48.87515,"lng":2.32438},{"name":"Saint-Lazare","lat":48.87572,"lng":2.32422},{"name":"Saint-Lazare","lat":48.87542,"lng":2.32548}]}, + {"id":"71376","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Courcelles","lat":48.87933,"lng":2.30407}]}, + {"id":"71379","lines":[{"mode":"metro","line":"1"},{"mode":"rer","line":"C"},{"mode":"rer","line":"E"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Neuilly Porte Maillot","lat":48.87791,"lng":2.28443},{"name":"Porte Maillot","lat":48.87793,"lng":2.28298},{"name":"Porte Maillot","lat":48.87755,"lng":2.28316},{"name":"Porte Maillot","lat":48.87794,"lng":2.28391}]}, + {"id":"71382","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Liège","lat":48.87963,"lng":2.32702}]}, + {"id":"71397","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Anny Flore","lat":48.88115,"lng":2.28551}]}, + {"id":"71403","lines":[{"mode":"metro","line":"2"},{"mode":"metro","line":"3"}],"stops":[{"name":"Villiers","lat":48.88109,"lng":2.31472}]}, + {"id":"71407","lines":[{"mode":"metro","line":"7"},{"mode":"metro","line":"7bis"}],"stops":[{"name":"Louis Blanc","lat":48.88119,"lng":2.36438},{"name":"Louis Blanc","lat":48.88095,"lng":2.36499}]}, + {"id":"71408","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Rome","lat":48.88233,"lng":2.3218}]}, + {"id":"71409","lines":[{"mode":"metro","line":"2"},{"mode":"metro","line":"12"}],"stops":[{"name":"Pigalle","lat":48.88225,"lng":2.33716},{"name":"Pigalle","lat":48.88223,"lng":2.33823}]}, + {"id":"71410","lines":[{"mode":"metro","line":"4"},{"mode":"metro","line":"5"},{"mode":"rer","line":"B"},{"mode":"rer","line":"D"}],"stops":[{"name":"Gare du Nord","lat":48.8802,"lng":2.35763},{"name":"Gare du Nord","lat":48.88176,"lng":2.35757},{"name":"Gare du Nord","lat":48.87979,"lng":2.35579}]}, + {"id":"71416","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Malesherbes","lat":48.88289,"lng":2.30905}]}, + {"id":"71419","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Anvers","lat":48.88297,"lng":2.34467}]}, + {"id":"71420","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Blanche","lat":48.88356,"lng":2.33338}]}, + {"id":"71422","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Puteaux","lat":48.88333,"lng":2.23387}]}, + {"id":"71423","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Wagram","lat":48.88381,"lng":2.30467}]}, + {"id":"71426","lines":[{"mode":"metro","line":"2"},{"mode":"metro","line":"4"}],"stops":[{"name":"Barbès-Rochechouart","lat":48.8838,"lng":2.35061},{"name":"Barbès-Rochechouart","lat":48.88342,"lng":2.34967}]}, + {"id":"71432","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Abbesses","lat":48.88459,"lng":2.33795}]}, + {"id":"71434","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"La Chapelle","lat":48.8844,"lng":2.3604}]}, + {"id":"71435","lines":[{"mode":"metro","line":"2"},{"mode":"metro","line":"13"}],"stops":[{"name":"Place de Clichy","lat":48.8838,"lng":2.32724},{"name":"Place de Clichy","lat":48.884,"lng":2.32853}]}, + {"id":"71442","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Pont de Neuilly","lat":48.88471,"lng":2.26052}]}, + {"id":"71450","lines":[{"mode":"metro","line":"3"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de Champerret","lat":48.88597,"lng":2.29214},{"name":"Porte de Champerret","lat":48.88601,"lng":2.29353}]}, + {"id":"71453","lines":[{"mode":"metro","line":"3"},{"mode":"rer","line":"C"}],"stops":[{"name":"Pereire-Levallois","lat":48.8849,"lng":2.29772},{"name":"Pereire-Levallois","lat":48.88604,"lng":2.29928}]}, + {"id":"71469","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Château Rouge","lat":48.88671,"lng":2.34956}]}, + {"id":"71470","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Square Sainte-Odile","lat":48.88738,"lng":2.29697}]}, + {"id":"71474","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"La Fourche","lat":48.88718,"lng":2.32578}]}, + {"id":"71475","lines":[{"mode":"metro","line":"14"}],"stops":[{"name":"Pont Cardinet","lat":48.88956,"lng":2.31518}]}, + {"id":"71485","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Esplanade de la Défense","lat":48.88814,"lng":2.24979}]}, + {"id":"71492","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Lamarck-Caulaincourt","lat":48.88967,"lng":2.33876}]}, + {"id":"71505","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte d'Asnières","lat":48.88974,"lng":2.30328}]}, + {"id":"71510","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Marx Dormoy","lat":48.89049,"lng":2.36}]}, + {"id":"71511","lines":[{"mode":"metro","line":"4"},{"mode":"metro","line":"12"}],"stops":[{"name":"Marcadet-Poissonniers","lat":48.89144,"lng":2.34963},{"name":"Marcadet-Poissonniers","lat":48.89147,"lng":2.34982}]}, + {"id":"71517","lines":[{"mode":"metro","line":"1"},{"mode":"rer","line":"A"},{"mode":"rer","line":"E"},{"mode":"tram","line":"2"}],"stops":[{"name":"La Défense","lat":48.89169,"lng":2.2386},{"name":"La Défense","lat":48.89219,"lng":2.23702},{"name":"La Défense","lat":48.89212,"lng":2.23721},{"name":"La Défense","lat":48.89286,"lng":2.23957}]}, + {"id":"71519","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Anatole France","lat":48.89208,"lng":2.28501}]}, + {"id":"71521","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Jules Joffrin","lat":48.89242,"lng":2.34476}]}, + {"id":"71528","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Guy Môquet","lat":48.89221,"lng":2.32722}]}, + {"id":"71545","lines":[{"mode":"metro","line":"13"},{"mode":"metro","line":"14"},{"mode":"rer","line":"C"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de Clichy","lat":48.89498,"lng":2.31279},{"name":"Porte de Clichy","lat":48.89417,"lng":2.3156},{"name":"Porte de Clichy","lat":48.89462,"lng":2.31423},{"name":"Porte de Clichy","lat":48.89425,"lng":2.31377}]}, + {"id":"71556","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Simplon","lat":48.89434,"lng":2.34743}]}, + {"id":"71557","lines":[{"mode":"metro","line":"14"}],"stops":[{"name":"Olympiades","lat":48.82727,"lng":2.36803}]}, + {"id":"71558","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Liberté","lat":48.82662,"lng":2.40585}]}, + {"id":"71570","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Baron Le Roy","lat":48.83039,"lng":2.39345}]}, + {"id":"71572","lines":[{"mode":"metro","line":"14"},{"mode":"rer","line":"C"}],"stops":[{"name":"Bibliothèque François Mitterrand","lat":48.82999,"lng":2.37575},{"name":"Bibliothèque François Mitterrand","lat":48.82888,"lng":2.3777}]}, + {"id":"71576","lines":[{"mode":"metro","line":"14"}],"stops":[{"name":"Cour Saint-Émilion","lat":48.83334,"lng":2.38663}]}, + {"id":"71579","lines":[{"mode":"metro","line":"8"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Porte de Charenton","lat":48.83199,"lng":2.39835},{"name":"Porte de Charenton","lat":48.83274,"lng":2.40031}]}, + {"id":"71590","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Nogent-sur-Marne","lat":48.83429,"lng":2.47168}]}, + {"id":"71591","lines":[{"mode":"metro","line":"8"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Porte Dorée","lat":48.83479,"lng":2.40538},{"name":"Porte Dorée","lat":48.83552,"lng":2.40666}]}, + {"id":"71595","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Michel Bizot","lat":48.83712,"lng":2.40233}]}, + {"id":"71597","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Quai de la Gare","lat":48.83705,"lng":2.37283}]}, + {"id":"71606","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Dugommier","lat":48.83904,"lng":2.38962}]}, + {"id":"71607","lines":[{"mode":"metro","line":"6"},{"mode":"metro","line":"14"}],"stops":[{"name":"Bercy","lat":48.84039,"lng":2.37991},{"name":"Bercy","lat":48.84,"lng":2.37955}]}, + {"id":"71609","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Nogent-le-Perreux","lat":48.83934,"lng":2.49419}]}, + {"id":"71612","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Montempoivre","lat":48.83986,"lng":2.40887}]}, + {"id":"71614","lines":[{"mode":"metro","line":"6"},{"mode":"metro","line":"8"}],"stops":[{"name":"Daumesnil","lat":48.83945,"lng":2.39618},{"name":"Daumesnil","lat":48.83955,"lng":2.3957}]}, + {"id":"71630","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Fontenay-sous-Bois","lat":48.84379,"lng":2.4635}]}, + {"id":"71631","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Montgallet","lat":48.84407,"lng":2.39048}]}, + {"id":"71632","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Alexandra David-Néel","lat":48.8437,"lng":2.41011}]}, + {"id":"71634","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Château de Vincennes","lat":48.84446,"lng":2.44044}]}, + {"id":"71637","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Quai de la Rapée","lat":48.84648,"lng":2.36591}]}, + {"id":"71639","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Picpus","lat":48.84506,"lng":2.40129}]}, + {"id":"71647","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Saint-Mandé","lat":48.84642,"lng":2.41876}]}, + {"id":"71650","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Bérault","lat":48.84547,"lng":2.42921}]}, + {"id":"71651","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Vincennes","lat":48.84734,"lng":2.43243}]}, + {"id":"71654","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"8"}],"stops":[{"name":"Reuilly-Diderot","lat":48.84718,"lng":2.38524},{"name":"Reuilly-Diderot","lat":48.84713,"lng":2.38703}]}, + {"id":"71663","lines":[{"mode":"metro","line":"1"},{"mode":"tram","line":"3a"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de Vincennes","lat":48.84734,"lng":2.40809},{"name":"Porte de Vincennes","lat":48.84695,"lng":2.40955},{"name":"Porte de Vincennes","lat":48.84739,"lng":2.40966}]}, + {"id":"71673","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"2"},{"mode":"metro","line":"6"},{"mode":"metro","line":"9"},{"mode":"rer","line":"A"}],"stops":[{"name":"Nation","lat":48.84824,"lng":2.39582},{"name":"Nation","lat":48.84847,"lng":2.39506},{"name":"Nation","lat":48.84737,"lng":2.39546},{"name":"Nation","lat":48.84758,"lng":2.39555},{"name":"Nation","lat":48.84894,"lng":2.39588}]}, + {"id":"71693","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Ledru Rollin","lat":48.85152,"lng":2.3751}]}, + {"id":"71694","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Rue des Boulets","lat":48.85217,"lng":2.38963}]}, + {"id":"71697","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Avron","lat":48.85191,"lng":2.398}]}, + {"id":"71703","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Maraîchers","lat":48.85273,"lng":2.40609}]}, + {"id":"71710","lines":[{"mode":"metro","line":"9"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de Montreuil","lat":48.85299,"lng":2.41074},{"name":"Porte de Montreuil","lat":48.85377,"lng":2.41189}]}, + {"id":"71718","lines":[{"mode":"rer","line":"A"},{"mode":"rer","line":"E"}],"stops":[{"name":"Val de Fontenay","lat":48.8541,"lng":2.4887},{"name":"Val de Fontenay","lat":48.85427,"lng":2.48917}]}, + {"id":"71725","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Robespierre","lat":48.85573,"lng":2.42298}]}, + {"id":"71728","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Alexandre Dumas","lat":48.85623,"lng":2.39468}]}, + {"id":"71750","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Voltaire","lat":48.85788,"lng":2.38002}]}, + {"id":"71751","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Philippe Auguste","lat":48.85806,"lng":2.39086}]}, + {"id":"71756","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Croix de Chavaux","lat":48.85778,"lng":2.43454}]}, + {"id":"71777","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Saint-Sébastien-Froissart","lat":48.86117,"lng":2.36721}]}, + {"id":"71781","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Saint-Ambroise","lat":48.86148,"lng":2.37394}]}, + {"id":"71785","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Mairie de Montreuil","lat":48.8623,"lng":2.44152}]}, + {"id":"71787","lines":[{"mode":"metro","line":"2"},{"mode":"metro","line":"3"}],"stops":[{"name":"Père Lachaise","lat":48.86218,"lng":2.38784},{"name":"Père Lachaise","lat":48.86312,"lng":2.38588}]}, + {"id":"71795","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Rue Saint-Maur","lat":48.86429,"lng":2.37974}]}, + {"id":"71799","lines":[{"mode":"metro","line":"3"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de Bagnolet","lat":48.8646,"lng":2.40803},{"name":"Porte de Bagnolet","lat":48.86343,"lng":2.40883}]}, + {"id":"71801","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Parmentier","lat":48.86525,"lng":2.37458}]}, + {"id":"71803","lines":[{"mode":"metro","line":"5"},{"mode":"metro","line":"9"}],"stops":[{"name":"Oberkampf","lat":48.86496,"lng":2.3679},{"name":"Oberkampf","lat":48.86502,"lng":2.36801}]}, + {"id":"71808","lines":[{"mode":"metro","line":"3"},{"mode":"metro","line":"3bis"}],"stops":[{"name":"Gambetta","lat":48.86516,"lng":2.39875},{"name":"Gambetta","lat":48.86498,"lng":2.39691}]}, + {"id":"71815","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Ménilmontant","lat":48.86645,"lng":2.3836}]}, + {"id":"71817","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Gallieni","lat":48.86534,"lng":2.4167}]}, + {"id":"71824","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Séverine","lat":48.86752,"lng":2.40898}]}, + {"id":"71828","lines":[{"mode":"metro","line":"3bis"}],"stops":[{"name":"Pelleport","lat":48.86844,"lng":2.40156}]}, + {"id":"71834","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Couronnes","lat":48.86909,"lng":2.38054}]}, + {"id":"71840","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Goncourt","lat":48.86996,"lng":2.37109}]}, + {"id":"71853","lines":[{"mode":"metro","line":"2"},{"mode":"metro","line":"11"}],"stops":[{"name":"Belleville","lat":48.87185,"lng":2.37659},{"name":"Belleville","lat":48.87228,"lng":2.37673}]}, + {"id":"71860","lines":[{"mode":"metro","line":"3bis"}],"stops":[{"name":"Saint-Fargeau","lat":48.8721,"lng":2.40451}]}, + {"id":"71864","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Pyrénées","lat":48.87388,"lng":2.38532}]}, + {"id":"71868","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Jourdain","lat":48.87511,"lng":2.38942}]}, + {"id":"71870","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Télégraphe","lat":48.87547,"lng":2.39887}]}, + {"id":"71885","lines":[{"mode":"metro","line":"7bis"},{"mode":"metro","line":"11"}],"stops":[{"name":"Place des Fêtes","lat":48.87695,"lng":2.39298},{"name":"Place des Fêtes","lat":48.87681,"lng":2.39306}]}, + {"id":"71889","lines":[{"mode":"metro","line":"3bis"},{"mode":"metro","line":"11"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte des Lilas","lat":48.87711,"lng":2.40673},{"name":"Porte des Lilas","lat":48.87717,"lng":2.40648},{"name":"Porte des Lilas","lat":48.87663,"lng":2.407}]}, + {"id":"71893","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Colonel Fabien","lat":48.87761,"lng":2.37081}]}, + {"id":"71897","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Montreuil - Hôpital","lat":48.87834,"lng":2.45471}]}, + {"id":"71900","lines":[{"mode":"metro","line":"7bis"}],"stops":[{"name":"Buttes-Chaumont","lat":48.87851,"lng":2.38161}]}, + {"id":"71906","lines":[{"mode":"metro","line":"7bis"}],"stops":[{"name":"Botzaris","lat":48.87949,"lng":2.38857}]}, + {"id":"71909","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Mairie des Lilas","lat":48.87976,"lng":2.41648}]}, + {"id":"71911","lines":[{"mode":"metro","line":"7bis"}],"stops":[{"name":"Pré Saint-Gervais","lat":48.88027,"lng":2.39847}]}, + {"id":"71914","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Hôpital Robert Debré","lat":48.87946,"lng":2.40123}]}, + {"id":"71920","lines":[{"mode":"metro","line":"7bis"}],"stops":[{"name":"Bolivar","lat":48.88067,"lng":2.3745}]}, + {"id":"71930","lines":[{"mode":"metro","line":"7bis"}],"stops":[{"name":"Danube","lat":48.88182,"lng":2.39401}]}, + {"id":"71935","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Rosny-sous-Bois","lat":48.86991,"lng":2.48576}]}, + {"id":"71940","lines":[{"mode":"metro","line":"2"},{"mode":"metro","line":"5"},{"mode":"metro","line":"7bis"}],"stops":[{"name":"Jaurès","lat":48.88319,"lng":2.37193},{"name":"Jaurès","lat":48.88247,"lng":2.37016},{"name":"Jaurès","lat":48.88162,"lng":2.37026}]}, + {"id":"71947","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Romainville - Carnot","lat":48.88314,"lng":2.44082}]}, + {"id":"71957","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Laumière","lat":48.88497,"lng":2.37872}]}, + {"id":"71961","lines":[{"mode":"metro","line":"2"},{"mode":"metro","line":"5"},{"mode":"metro","line":"7"}],"stops":[{"name":"Stalingrad","lat":48.8848,"lng":2.36958},{"name":"Stalingrad","lat":48.88433,"lng":2.36737},{"name":"Stalingrad","lat":48.88428,"lng":2.36582}]}, + {"id":"71972","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Ourcq","lat":48.88686,"lng":2.38591}]}, + {"id":"71977","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Riquet","lat":48.88797,"lng":2.37346}]}, + {"id":"71989","lines":[{"mode":"metro","line":"5"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de Pantin","lat":48.88901,"lng":2.39568},{"name":"Porte de Pantin","lat":48.88842,"lng":2.39178}]}, + {"id":"72004","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Hoche","lat":48.89123,"lng":2.40235}]}, + {"id":"72013","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Crimée","lat":48.89091,"lng":2.37709}]}, + {"id":"72024","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Église de Pantin","lat":48.89315,"lng":2.41276}]}, + {"id":"72028","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Les Coquetiers","lat":48.8926,"lng":2.49976}]}, + {"id":"72031","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Corentin Cariou","lat":48.89466,"lng":2.38216}]}, + {"id":"72033","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"La Remise à Jorelle","lat":48.89309,"lng":2.48791}]}, + {"id":"72037","lines":[{"mode":"rer","line":"E"},{"mode":"tram","line":"4"}],"stops":[{"name":"Bondy","lat":48.89395,"lng":2.48021},{"name":"Bondy","lat":48.89381,"lng":2.48203}]}, + {"id":"72039","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Bobigny-Pantin - Raymond Queneau","lat":48.89545,"lng":2.42533}]}, + {"id":"72041","lines":[{"mode":"rer","line":"E"},{"mode":"tram","line":"1"}],"stops":[{"name":"Noisy-le-Sec","lat":48.89592,"lng":2.46033},{"name":"Noisy-le-Sec","lat":48.89665,"lng":2.45836}]}, + {"id":"72052","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Épinettes - Pouchet","lat":48.89755,"lng":2.32491}]}, + {"id":"72057","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Pont de Levallois","lat":48.89715,"lng":2.28041}]}, + {"id":"72061","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Diane Arbus","lat":48.89833,"lng":2.35171}]}, + {"id":"72063","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Faubourg de l'Arche","lat":48.89666,"lng":2.24008}]}, + {"id":"72064","lines":[{"mode":"metro","line":"12"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de la Chapelle","lat":48.89725,"lng":2.35951},{"name":"Porte de la Chapelle","lat":48.89839,"lng":2.35838}]}, + {"id":"72066","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Colette Besson","lat":48.89849,"lng":2.36365}]}, + {"id":"72078","lines":[{"mode":"metro","line":"13"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de Saint-Ouen","lat":48.89737,"lng":2.32894},{"name":"Porte de Saint-Ouen","lat":48.89767,"lng":2.32983}]}, + {"id":"72081","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Angélique Compoint","lat":48.89783,"lng":2.33823}]}, + {"id":"72099","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Les Fauvelles","lat":48.90277,"lng":2.23954}]}, + {"id":"72118","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Mairie de Clichy","lat":48.90323,"lng":2.30601}]}, + {"id":"72126","lines":[{"mode":"metro","line":"14"},{"mode":"rer","line":"C"}],"stops":[{"name":"Saint-Ouen","lat":48.90457,"lng":2.32263},{"name":"Saint-Ouen","lat":48.9045,"lng":2.32174}]}, + {"id":"72128","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Garibaldi","lat":48.90609,"lng":2.33183}]}, + {"id":"72137","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Charlebourg","lat":48.90766,"lng":2.23831}]}, + {"id":"72168","lines":[{"mode":"metro","line":"13"},{"mode":"metro","line":"14"}],"stops":[{"name":"Mairie de Saint-Ouen","lat":48.91196,"lng":2.33392},{"name":"Mairie de Saint-Ouen","lat":48.9132,"lng":2.33466}]}, + {"id":"72203","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Gabriel Péri","lat":48.91638,"lng":2.29469}]}, + {"id":"72206","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Stade de France-Saint-Denis","lat":48.91757,"lng":2.35039}]}, + {"id":"72211","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"La Plaine-Stade de France","lat":48.91801,"lng":2.36247}]}, + {"id":"72217","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Carrefour Pleyel","lat":48.91901,"lng":2.34261}]}, + {"id":"72225","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Les Grésillons","lat":48.92087,"lng":2.31384}]}, + {"id":"72240","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Les Agnettes","lat":48.9231,"lng":2.28628}]}, + {"id":"72267","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Asnières Quatre Routes","lat":48.92725,"lng":2.27434}]}, + {"id":"72285","lines":[{"mode":"metro","line":"13"},{"mode":"tram","line":"8"}],"stops":[{"name":"Saint-Denis - Porte de Paris","lat":48.92994,"lng":2.35604},{"name":"Saint-Denis - Porte de Paris","lat":48.92982,"lng":2.35729}]}, + {"id":"72286","lines":[{"mode":"metro","line":"13"},{"mode":"tram","line":"1"}],"stops":[{"name":"Les Courtilles","lat":48.93021,"lng":2.28372},{"name":"Les Courtilles","lat":48.93076,"lng":2.28417}]}, + {"id":"72292","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Pierre De Geyter","lat":48.93205,"lng":2.3527}]}, + {"id":"72300","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Le Luth","lat":48.93181,"lng":2.28866}]}, + {"id":"72303","lines":[{"mode":"rer","line":"C"},{"mode":"tram","line":"1"}],"stops":[{"name":"Gennevilliers","lat":48.9329,"lng":2.30794},{"name":"Gennevilliers","lat":48.93345,"lng":2.30709}]}, + {"id":"72305","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Le Village","lat":48.93336,"lng":2.2946}]}, + {"id":"72307","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Parc des Chanteraines","lat":48.93394,"lng":2.31326}]}, + {"id":"72311","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Chemin des Reniers","lat":48.93461,"lng":2.3215}]}, + {"id":"72313","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"La Noue","lat":48.93499,"lng":2.32695}]}, + {"id":"72314","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Timbaud","lat":48.93339,"lng":2.30063}]}, + {"id":"72319","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"L'Ile-Saint-Denis","lat":48.93577,"lng":2.33957}]}, + {"id":"72322","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Cimetière de Saint-Denis","lat":48.93639,"lng":2.36364}]}, + {"id":"72323","lines":[{"mode":"rer","line":"D"},{"mode":"tram","line":"1"},{"mode":"tram","line":"8"}],"stops":[{"name":"Saint-Denis","lat":48.93527,"lng":2.34786},{"name":"Saint-Denis","lat":48.93616,"lng":2.34647},{"name":"Saint-Denis","lat":48.93446,"lng":2.3456}]}, + {"id":"72324","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Mairie de Villeneuve-la-Garenne","lat":48.93561,"lng":2.33433}]}, + {"id":"72326","lines":[{"mode":"metro","line":"13"},{"mode":"tram","line":"1"}],"stops":[{"name":"Basilique de Saint-Denis","lat":48.93678,"lng":2.35937},{"name":"Basilique de Saint-Denis","lat":48.93806,"lng":2.36079}]}, + {"id":"72327","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Théâtre Gérard Philipe","lat":48.93751,"lng":2.3504}]}, + {"id":"72333","lines":[{"mode":"tram","line":"1"},{"mode":"tram","line":"5"}],"stops":[{"name":"Marché de Saint-Denis","lat":48.93895,"lng":2.35633},{"name":"Marché de Saint-Denis","lat":48.93859,"lng":2.35585}]}, + {"id":"72335","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Paul Éluard","lat":48.93969,"lng":2.34521}]}, + {"id":"72355","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Roger Sémat","lat":48.94482,"lng":2.35701}]}, + {"id":"72358","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Saint-Denis - Université","lat":48.94587,"lng":2.36464}]}, + {"id":"72364","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Blumenthal","lat":48.94781,"lng":2.33918}]}, + {"id":"72365","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Guynemer","lat":48.94821,"lng":2.35746}]}, + {"id":"72366","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"César","lat":48.94958,"lng":2.34252}]}, + {"id":"72367","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Les Mobiles","lat":48.94994,"lng":2.32883}]}, + {"id":"72371","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Petit Pierrefitte","lat":48.95268,"lng":2.35798}]}, + {"id":"72372","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Les Béatus","lat":48.9516,"lng":2.32076}]}, + {"id":"72376","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Rose Bertin","lat":48.95317,"lng":2.3154}]}, + {"id":"72378","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Jean Vilar","lat":48.95273,"lng":2.34346}]}, + {"id":"72379","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Lacépède","lat":48.95588,"lng":2.31153}]}, + {"id":"72381","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Gilbert Bonnemaison","lat":48.95544,"lng":2.30787}]}, + {"id":"72382","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Épinay-Orgemont","lat":48.9555,"lng":2.29559}]}, + {"id":"72383","lines":[{"mode":"rer","line":"C"},{"mode":"tram","line":"8"},{"mode":"tram","line":"11"}],"stops":[{"name":"Épinay-sur-Seine","lat":48.9535,"lng":2.30235},{"name":"Épinay-sur-Seine","lat":48.95456,"lng":2.30231},{"name":"Épinay-sur-Seine","lat":48.95583,"lng":2.30199}]}, + {"id":"72387","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Pablo Neruda","lat":48.95667,"lng":2.34311}]}, + {"id":"72396","lines":[{"mode":"tram","line":"8"},{"mode":"tram","line":"11"}],"stops":[{"name":"Villetaneuse-Université","lat":48.95932,"lng":2.34205},{"name":"Villetaneuse - Université","lat":48.96001,"lng":2.34237}]}, + {"id":"72397","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Suzanne Valadon","lat":48.95912,"lng":2.35874}]}, + {"id":"72408","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Mairie de Pierrefitte","lat":48.9635,"lng":2.36021}]}, + {"id":"72414","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Alcide d'Orbigny","lat":48.96562,"lng":2.3633}]}, + {"id":"72421","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Pantin","lat":48.89811,"lng":2.40036}]}, + {"id":"72424","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Canal Saint-Denis","lat":48.89873,"lng":2.38054}]}, + {"id":"72430","lines":[{"mode":"metro","line":"7"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de la Villette","lat":48.89743,"lng":2.38658},{"name":"Porte de la Villette","lat":48.89739,"lng":2.38551}]}, + {"id":"72434","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte d'Aubervilliers","lat":48.8986,"lng":2.36904}]}, + {"id":"72438","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Petit Noisy","lat":48.90042,"lng":2.46546}]}, + {"id":"72460","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Aubervilliers Pantin - Quatre Chemins","lat":48.90378,"lng":2.39222}]}, + {"id":"72472","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Libération","lat":48.90693,"lng":2.43862}]}, + {"id":"72477","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Hôtel de Ville de Bobigny","lat":48.90654,"lng":2.44399}]}, + {"id":"72478","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Pont de Bondy","lat":48.9047,"lng":2.46997}]}, + {"id":"72479","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Auguste Delaune","lat":48.9067,"lng":2.46052}]}, + {"id":"72486","lines":[{"mode":"metro","line":"5"},{"mode":"tram","line":"1"}],"stops":[{"name":"Bobigny Pablo Picasso","lat":48.90637,"lng":2.44919},{"name":"Bobigny Pablo Picasso","lat":48.90654,"lng":2.45001}]}, + {"id":"72490","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Jean Rostand","lat":48.90781,"lng":2.45436}]}, + {"id":"72491","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Aimé Césaire","lat":48.90808,"lng":2.37774}]}, + {"id":"72517","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Escadrille Normandie-Niémen","lat":48.9127,"lng":2.43542}]}, + {"id":"72524","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Mairie d'Aubervilliers","lat":48.91371,"lng":2.38074}]}, + {"id":"72526","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Gaston Roulaud","lat":48.91409,"lng":2.43128}]}, + {"id":"72534","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Hôpital Avicenne","lat":48.91584,"lng":2.42501}]}, + {"id":"72549","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Drancy-Avenir","lat":48.91778,"lng":2.41769}]}, + {"id":"72553","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Maurice Lachâtre","lat":48.91918,"lng":2.41384}]}, + {"id":"72598","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"La Courneuve-Aubervilliers","lat":48.92435,"lng":2.38639}]}, + {"id":"72622","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Hôtel de Ville de La Courneuve","lat":48.9274,"lng":2.39247}]}, + {"id":"72637","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Six Routes","lat":48.92997,"lng":2.38476}]}, + {"id":"72641","lines":[{"mode":"rer","line":"B"},{"mode":"tram","line":"11"}],"stops":[{"name":"Le Bourget","lat":48.93029,"lng":2.42339},{"name":"Le Bourget","lat":48.93074,"lng":2.42579}]}, + {"id":"72646","lines":[{"mode":"rer","line":"B"},{"mode":"tram","line":"4"}],"stops":[{"name":"Aulnay-sous-Bois","lat":48.9322,"lng":2.49551},{"name":"Aulnay-sous-Bois","lat":48.93196,"lng":2.49712}]}, + {"id":"72648","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Le Blanc-Mesnil","lat":48.93231,"lng":2.47567}]}, + {"id":"72652","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Drancy","lat":48.93271,"lng":2.45477}]}, + {"id":"72787","lines":[{"mode":"rer","line":"D"},{"mode":"tram","line":"11"}],"stops":[{"name":"Pierrefitte Stains","lat":48.96013,"lng":2.36726},{"name":"Pierrefitte Stains","lat":48.96376,"lng":2.3721}]}, + {"id":"72796","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Butte Pinson","lat":48.9736,"lng":2.36607}]}, + {"id":"72881","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Boissy-Saint-Léger","lat":48.75219,"lng":2.50456}]}, + {"id":"72929","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Sucy-Bonneuil","lat":48.77171,"lng":2.50748}]}, + {"id":"72998","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"La Varenne-Chennevières","lat":48.79538,"lng":2.51342}]}, + {"id":"73042","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Champigny","lat":48.80699,"lng":2.51031}]}, + {"id":"73096","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Les Yvris-Noisy-le-Grand","lat":48.82315,"lng":2.57984}]}, + {"id":"73097","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Villiers-sur-Marne - Le Plessis-Trévise","lat":48.82327,"lng":2.54268}]}, + {"id":"73098","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Les Boullereaux-Champigny","lat":48.82485,"lng":2.51186}]}, + {"id":"73163","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Noisy-Champs","lat":48.84288,"lng":2.58009}]}, + {"id":"73166","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Bry-sur-Marne","lat":48.84519,"lng":2.52562}]}, + {"id":"73190","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Neuilly-Plaisance","lat":48.85291,"lng":2.51431}]}, + {"id":"73266","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Le Chénay-Gagny","lat":48.87716,"lng":2.55288}]}, + {"id":"73278","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Gagny","lat":48.88373,"lng":2.52543}]}, + {"id":"73297","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Le Raincy-Villemomble-Montfermeil","lat":48.88887,"lng":2.51316}]}, + {"id":"73312","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Allée de la Tour Rendez-Vous","lat":48.89714,"lng":2.50641}]}, + {"id":"73329","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Hôpital de Montfermeil","lat":48.90096,"lng":2.57084}]}, + {"id":"73334","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Les Pavillons-sous-Bois","lat":48.90268,"lng":2.51194}]}, + {"id":"73350","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Romain Rolland","lat":48.90474,"lng":2.54792}]}, + {"id":"73354","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"République Marx Dormoy","lat":48.90663,"lng":2.52405}]}, + {"id":"73360","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Gargan","lat":48.90727,"lng":2.51651}]}, + {"id":"73362","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Léon Blum","lat":48.90853,"lng":2.53229}]}, + {"id":"73364","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Maurice Audin","lat":48.90842,"lng":2.53917}]}, + {"id":"73411","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Freinville-Sevran","lat":48.9265,"lng":2.51912}]}, + {"id":"73439","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Sevran-Livry","lat":48.93628,"lng":2.53471}]}, + {"id":"73482","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Vert-Galant","lat":48.94418,"lng":2.56645}]}, + {"id":"73491","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Sevran-Beaudottes","lat":48.94689,"lng":2.52478}]}, + {"id":"73547","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Villepinte","lat":48.96271,"lng":2.51264}]}, + {"id":"73568","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Parc des Expositions","lat":48.97342,"lng":2.51436}]}, + {"id":"73596","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Aéroport Charles De Gaulle 1","lat":49.00976,"lng":2.56123}]}, + {"id":"73602","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"Mareil-Marly","lat":48.88099,"lng":2.07918}]}, + {"id":"73604","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Achères-Ville","lat":48.97067,"lng":2.07773}]}, + {"id":"73605","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Conflans-Fin-d'Oise","lat":48.99033,"lng":2.07395}]}, + {"id":"73612","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Henri Farman","lat":48.83475,"lng":2.27124}]}, + {"id":"73613","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Les Milons","lat":48.84985,"lng":2.22123}]}, + {"id":"73617","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Europe","lat":48.87877,"lng":2.32227}]}, + {"id":"73618","lines":[{"mode":"metro","line":"4"},{"mode":"metro","line":"10"}],"stops":[{"name":"Odéon","lat":48.85194,"lng":2.34061},{"name":"Odéon","lat":48.8523,"lng":2.33973}]}, + {"id":"73619","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Cluny-La Sorbonne","lat":48.85105,"lng":2.34427}]}, + {"id":"73620","lines":[{"mode":"metro","line":"4"},{"mode":"rer","line":"B"},{"mode":"rer","line":"C"}],"stops":[{"name":"Saint-Michel","lat":48.85353,"lng":2.34529},{"name":"Saint-Michel","lat":48.85212,"lng":2.34402},{"name":"Saint-Michel","lat":48.85317,"lng":2.34329}]}, + {"id":"73621","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Bréguet-Sabin","lat":48.85614,"lng":2.37021}]}, + {"id":"73622","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Chemin Vert","lat":48.8571,"lng":2.36818}]}, + {"id":"73623","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Maryse Bastié","lat":48.82435,"lng":2.37766}]}, + {"id":"73624","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Le Vert-de-Maisons","lat":48.78847,"lng":2.43224}]}, + {"id":"73625","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Maisons-Alfort-Alfortville","lat":48.80205,"lng":2.42648}]}, + {"id":"73626","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"14"},{"mode":"rer","line":"A"},{"mode":"rer","line":"D"}],"stops":[{"name":"Gare de Lyon","lat":48.84377,"lng":2.37392},{"name":"Gare de Lyon","lat":48.84587,"lng":2.37377},{"name":"Gare de Lyon","lat":48.84349,"lng":2.37392},{"name":"Gare de Lyon","lat":48.84414,"lng":2.37326}]}, + {"id":"73627","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Grand-Bourg","lat":48.64849,"lng":2.43533}]}, + {"id":"73630","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Louvre-Rivoli","lat":48.86081,"lng":2.34111}]}, + {"id":"73633","lines":[{"mode":"metro","line":"4"},{"mode":"metro","line":"8"},{"mode":"metro","line":"9"}],"stops":[{"name":"Strasbourg-Saint-Denis","lat":48.8694,"lng":2.35382},{"name":"Strasbourg-Saint-Denis","lat":48.86963,"lng":2.35447}]}, + {"id":"73634","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Sentier","lat":48.86751,"lng":2.34655}]}, + {"id":"73635","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Bourse","lat":48.86865,"lng":2.34138}]}, + {"id":"73636","lines":[{"mode":"metro","line":"3"},{"mode":"metro","line":"4"}],"stops":[{"name":"Réaumur Sébastopol","lat":48.86673,"lng":2.35283},{"name":"Réaumur Sébastopol","lat":48.8665,"lng":2.35136}]}, + {"id":"73639","lines":[{"mode":"metro","line":"10"}],"stops":[{"name":"Mabillon","lat":48.85302,"lng":2.3355}]}, + {"id":"73640","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Rennes","lat":48.84846,"lng":2.32778}]}, + {"id":"73641","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Falguière","lat":48.84447,"lng":2.31785}]}, + {"id":"73642","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Assemblée Nationale","lat":48.86079,"lng":2.321}]}, + {"id":"73647","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Charonne","lat":48.85509,"lng":2.3847}]}, + {"id":"73648","lines":[{"mode":"metro","line":"5"}],"stops":[{"name":"Richard Lenoir","lat":48.85987,"lng":2.37191}]}, + {"id":"73650","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Bel-Air","lat":48.84134,"lng":2.40092}]}, + {"id":"73651","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Chevaleret","lat":48.83495,"lng":2.36813}]}, + {"id":"73653","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Mouton-Duvernet","lat":48.83111,"lng":2.32971}]}, + {"id":"73654","lines":[{"mode":"tram","line":"2"}],"stops":[{"name":"Porte d'Issy","lat":48.8317,"lng":2.28147}]}, + {"id":"73656","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Kléber","lat":48.87126,"lng":2.29324}]}, + {"id":"73657","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Jasmin","lat":48.8525,"lng":2.26818}]}, + {"id":"73658","lines":[{"mode":"metro","line":"9"},{"mode":"metro","line":"10"}],"stops":[{"name":"Michel Ange-Molitor","lat":48.84506,"lng":2.26175},{"name":"Michel Ange-Molitor","lat":48.84525,"lng":2.26162}]}, + {"id":"73661","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Brochant","lat":48.89059,"lng":2.32017}]}, + {"id":"73667","lines":[{"mode":"metro","line":"3"}],"stops":[{"name":"Louise Michel","lat":48.88871,"lng":2.28808}]}, + {"id":"73669","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Adrienne Bolland","lat":48.8721,"lng":2.40862}]}, + {"id":"73671","lines":[{"mode":"metro","line":"2"}],"stops":[{"name":"Monceau","lat":48.88033,"lng":2.30877}]}, + {"id":"73675","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Gaîté","lat":48.83863,"lng":2.32249}]}, + {"id":"73688","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Haussmann-Saint-Lazare","lat":48.87503,"lng":2.32672}]}, + {"id":"73689","lines":[{"mode":"metro","line":"7"},{"mode":"metro","line":"9"}],"stops":[{"name":"Chaussée d'Antin - La Fayette","lat":48.87325,"lng":2.33395},{"name":"Chaussée d'Antin - La Fayette","lat":48.8728,"lng":2.33446}]}, + {"id":"73690","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Saint-Augustin","lat":48.87448,"lng":2.322}]}, + {"id":"73692","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Cosmonautes","lat":48.93173,"lng":2.37823}]}, + {"id":"73693","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Danton","lat":48.92271,"lng":2.40662}]}, + {"id":"73695","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Fort d'Aubervilliers","lat":48.91495,"lng":2.40441}]}, + {"id":"73696","lines":[{"mode":"metro","line":"7"},{"mode":"tram","line":"1"}],"stops":[{"name":"La Courneuve 8 Mai 1945","lat":48.92079,"lng":2.4106}]}, + {"id":"73697","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Stade Géo André","lat":48.92438,"lng":2.40207}]}, + {"id":"73699","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Aéroport Charles De Gaulle 2-TGV","lat":49.00469,"lng":2.57059}]}, + {"id":"73709","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"L'Abbaye","lat":48.9218,"lng":2.51633}]}, + {"id":"73721","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Versailles Château","lat":48.80022,"lng":2.12904}]}, + {"id":"73731","lines":[{"mode":"rer","line":"C"},{"mode":"tram","line":"13"}],"stops":[{"name":"Saint-Cyr","lat":48.79901,"lng":2.07475},{"name":"Saint-Cyr","lat":48.79871,"lng":2.07185}]}, + {"id":"73753","lines":[{"mode":"metro","line":"14"},{"mode":"tram","line":"7"}],"stops":[{"name":"Porte de Thiais","lat":48.75957,"lng":2.3664},{"name":"Chevilly - Larue","lat":48.75955,"lng":2.36649}]}, + {"id":"73764","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Théâtre La Piscine","lat":48.76379,"lng":2.27275}]}, + {"id":"73766","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Malakoff - Plateau de Vanves","lat":48.82258,"lng":2.29846}]}, + {"id":"73787","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"La Ferme","lat":48.90967,"lng":2.43749}]}, + {"id":"73788","lines":[{"mode":"metro","line":"7"},{"mode":"metro","line":"14"}],"stops":[{"name":"Pyramides","lat":48.86574,"lng":2.33444},{"name":"Pyramides","lat":48.86591,"lng":2.33419}]}, + {"id":"73794","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Les Halles","lat":48.86252,"lng":2.34579}]}, + {"id":"73798","lines":[{"mode":"rer","line":"E"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Rosa Parks","lat":48.89658,"lng":2.37382},{"name":"Rosa Parks","lat":48.89755,"lng":2.37338}]}, + {"id":"73799","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Ella Fitzgerald","lat":48.89743,"lng":2.39497}]}, + {"id":"73800","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Delphine Seyrig","lat":48.89388,"lng":2.39774}]}, + {"id":"73801","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Butte du Chapeau Rouge","lat":48.88502,"lng":2.39675}]}, + {"id":"73802","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Marie de Miribel","lat":48.8587,"lng":2.40969}]}, + {"id":"73844","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Champ de Mars","lat":48.85646,"lng":2.2898}]}, + {"id":"73918","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"L’Onde","lat":48.78199,"lng":2.18049}]}, + {"id":"73921","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Baudelaire","lat":48.94213,"lng":2.35673}]}, + {"id":"73922","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Joncherolles","lat":48.95558,"lng":2.35832}]}, + {"id":"73923","lines":[{"mode":"tram","line":"5"}],"stops":[{"name":"Jacques Prévert","lat":48.97088,"lng":2.366}]}, + {"id":"74001","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Faidherbe Chaligny","lat":48.85018,"lng":2.38409}]}, + {"id":"74002","lines":[{"mode":"rer","line":"C"},{"mode":"tram","line":"3a"}],"stops":[{"name":"Pont du Garigliano","lat":48.83808,"lng":2.27117},{"name":"Pont du Garigliano","lat":48.83909,"lng":2.27046}]}, + {"id":"74008","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Hélène Boucher","lat":48.73999,"lng":2.35947}]}, + {"id":"74009","lines":[{"mode":"tram","line":"7"}],"stops":[{"name":"Porte de l'Essonne","lat":48.71451,"lng":2.37175}]}, + {"id":"74041","lines":[{"mode":"metro","line":"12"}],"stops":[{"name":"Front Populaire","lat":48.90657,"lng":2.36592}]}, + {"id":"74265","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Sceaux","lat":48.78136,"lng":2.29765}]}, + {"id":"74368","lines":[{"mode":"tram","line":"8"}],"stops":[{"name":"Delaunay-Belleville","lat":48.94556,"lng":2.34731}]}, + {"id":"411281","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Lycée Henri Sellier","lat":48.9161,"lng":2.51506}]}, + {"id":"411284","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Rougemont-Chanteloup","lat":48.93101,"lng":2.51488}]}, + {"id":"411419","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"La Borne Blanche","lat":49.12621,"lng":2.50701}]}, + {"id":"411422","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Orry-la-Ville-Coye-la-Forêt","lat":49.13829,"lng":2.49018}]}, + {"id":"411429","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Chantilly-Gouvieux","lat":49.18709,"lng":2.45955}]}, + {"id":"411441","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Creil","lat":49.26394,"lng":2.46891}]}, + {"id":"411486","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Malesherbes","lat":48.29359,"lng":2.40113}]}, + {"id":"412687","lines":[{"mode":"metro","line":"13"}],"stops":[{"name":"Pernety","lat":48.83423,"lng":2.31862}]}, + {"id":"412697","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Noisy-le-Grand-Mont d'Est","lat":48.84084,"lng":2.5499}]}, + {"id":"412992","lines":[{"mode":"metro","line":"8"}],"stops":[{"name":"Boucicaut","lat":48.84109,"lng":2.28795}]}, + {"id":"415852","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"11"}],"stops":[{"name":"Hôtel de Ville","lat":48.85748,"lng":2.35158},{"name":"Hôtel de Ville","lat":48.85745,"lng":2.35101}]}, + {"id":"416744","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Dewoitine","lat":48.78362,"lng":2.21443}]}, + {"id":"420144","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Vélizy 2","lat":48.78497,"lng":2.22001}]}, + {"id":"420463","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Georges Millandy","lat":48.7835,"lng":2.23098}]}, + {"id":"422420","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Viroflay Rive Droite","lat":48.8057,"lng":2.16907}]}, + {"id":"422455","lines":[{"mode":"tram","line":"6"}],"stops":[{"name":"Robert Wagner","lat":48.78512,"lng":2.1788}]}, + {"id":"422776","lines":[{"mode":"rer","line":"D"}],"stops":[{"name":"Essonnes-Robinson","lat":48.60556,"lng":2.46246}]}, + {"id":"423879","lines":[{"mode":"tram","line":"6"},{"mode":"tram","line":"10"}],"stops":[{"name":"Hôpital Béclère","lat":48.78721,"lng":2.25317},{"name":"Hôpital Béclère","lat":48.7867,"lng":2.25446}]}, + {"id":"424396","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Ivry-sur-Seine","lat":48.81399,"lng":2.39169}]}, + {"id":"424926","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Honoré de Balzac","lat":48.89631,"lng":2.3188}]}, + {"id":"425723","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Vallée aux Loups","lat":48.76638,"lng":2.25489}]}, + {"id":"425730","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Cité-Jardin","lat":48.76556,"lng":2.25958}]}, + {"id":"425779","lines":[{"mode":"metro","line":"1"}],"stops":[{"name":"Les Sablons","lat":48.88102,"lng":2.27224}]}, + {"id":"426083","lines":[{"mode":"tram","line":"11"}],"stops":[{"name":"Stains La Cerisaie","lat":48.95473,"lng":2.3918}]}, + {"id":"426093","lines":[{"mode":"tram","line":"11"}],"stops":[{"name":"Dugny - La Courneuve","lat":48.94393,"lng":2.41112}]}, + {"id":"426280","lines":[{"mode":"metro","line":"11"},{"mode":"rer","line":"E"}],"stops":[{"name":"Rosny-Bois-Perrier","lat":48.88275,"lng":2.48051},{"name":"Rosny-Bois-Perrier","lat":48.88253,"lng":2.48121}]}, + {"id":"427076","lines":[{"mode":"tram","line":"3a"}],"stops":[{"name":"Avenue de France","lat":48.82557,"lng":2.38117}]}, + {"id":"427230","lines":[{"mode":"rer","line":"B"}],"stops":[{"name":"Robinson","lat":48.78024,"lng":2.28143}]}, + {"id":"461505","lines":[{"mode":"metro","line":"13"},{"mode":"tram","line":"6"}],"stops":[{"name":"Châtillon-Montrouge","lat":48.81075,"lng":2.30168},{"name":"Châtillon-Montrouge","lat":48.8106,"lng":2.30036}]}, + {"id":"461959","lines":[{"mode":"tram","line":"1"}],"stops":[{"name":"Hôpital Delafontaine","lat":48.93359,"lng":2.37108}]}, + {"id":"462934","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Ozoir-la-Ferrière","lat":48.77078,"lng":2.69025}]}, + {"id":"463564","lines":[{"mode":"metro","line":"1"},{"mode":"metro","line":"5"},{"mode":"metro","line":"8"}],"stops":[{"name":"Bastille","lat":48.85344,"lng":2.36874},{"name":"Bastille","lat":48.85344,"lng":2.36951},{"name":"Bastille","lat":48.85201,"lng":2.36867}]}, + {"id":"473829","lines":[{"mode":"metro","line":"6"}],"stops":[{"name":"Bir-Hakeim","lat":48.85394,"lng":2.28933}]}, + {"id":"474149","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Château-Landon","lat":48.87844,"lng":2.36197}]}, + {"id":"474150","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"La Muette","lat":48.85805,"lng":2.27415}]}, + {"id":"474151","lines":[{"mode":"rer","line":"A"},{"mode":"rer","line":"B"},{"mode":"rer","line":"D"}],"stops":[{"name":"Châtelet-Les Halles","lat":48.86182,"lng":2.34701}]}, + {"id":"474152","lines":[{"mode":"metro","line":"2"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte Dauphine","lat":48.87147,"lng":2.27708},{"name":"Porte Dauphine","lat":48.87018,"lng":2.27463}]}, + {"id":"477278","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Clichy - Montfermeil","lat":48.90457,"lng":2.55501}]}, + {"id":"477285","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Notre-Dame-des-Anges","lat":48.90092,"lng":2.55607}]}, + {"id":"477288","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Clichy-sous-Bois Mairie","lat":48.90787,"lng":2.54638}]}, + {"id":"478505","lines":[{"mode":"rer","line":"C"},{"mode":"rer","line":"D"}],"stops":[{"name":"Juvisy","lat":48.68957,"lng":2.38383},{"name":"Juvisy","lat":48.68976,"lng":2.38285}]}, + {"id":"478648","lines":[{"mode":"tram","line":"4"}],"stops":[{"name":"Arboretum","lat":48.89792,"lng":2.56171}]}, + {"id":"478733","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Magenta","lat":48.88078,"lng":2.35873}]}, + {"id":"478855","lines":[{"mode":"rer","line":"C"}],"stops":[{"name":"Étampes","lat":48.43692,"lng":2.15953}]}, + {"id":"478860","lines":[{"mode":"metro","line":"14"}],"stops":[{"name":"Villejuif - Gustave Roussy","lat":48.79305,"lng":2.34937}]}, + {"id":"478883","lines":[{"mode":"metro","line":"4"},{"mode":"tram","line":"3b"}],"stops":[{"name":"Porte de Clignancourt","lat":48.8974,"lng":2.34485},{"name":"Porte de Clignancourt","lat":48.89804,"lng":2.34508}]}, + {"id":"478885","lines":[{"mode":"metro","line":"7"}],"stops":[{"name":"Le Peletier","lat":48.87497,"lng":2.34058}]}, + {"id":"478926","lines":[{"mode":"rer","line":"A"}],"stops":[{"name":"Auber","lat":48.87216,"lng":2.32993}]}, + {"id":"479068","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Étienne Marcel","lat":48.86397,"lng":2.34962}]}, + {"id":"479928","lines":[{"mode":"metro","line":"9"}],"stops":[{"name":"Buzenval","lat":48.85198,"lng":2.40193}]}, + {"id":"480872","lines":[{"mode":"tram","line":"10"}],"stops":[{"name":"Malabry","lat":48.76932,"lng":2.24965}]}, + {"id":"480927","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"Camp des Loges","lat":48.91392,"lng":2.08168}]}, + {"id":"480950","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"L'Etang - Les Sablons","lat":48.87215,"lng":2.06871}]}, + {"id":"480951","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"Bailly","lat":48.83705,"lng":2.07444}]}, + {"id":"480952","lines":[{"mode":"tram","line":"13"}],"stops":[{"name":"Les Portes de Saint-Cyr","lat":48.80662,"lng":2.07723}]}, + {"id":"482368","lines":[{"mode":"metro","line":"3"},{"mode":"metro","line":"9"}],"stops":[{"name":"Havre-Caumartin","lat":48.87351,"lng":2.32754},{"name":"Havre-Caumartin","lat":48.87371,"lng":2.3277}]}, + {"id":"483315","lines":[{"mode":"metro","line":"4"}],"stops":[{"name":"Bagneux - Lucie Aubrac","lat":48.80363,"lng":2.31805}]}, + {"id":"486959","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Champlan","lat":48.70855,"lng":2.28012}]}, + {"id":"486992","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Massy Europe","lat":48.72343,"lng":2.27264}]}, + {"id":"486996","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Coteaux de l'Orge","lat":48.66532,"lng":2.36262}]}, + {"id":"486998","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Bois de Saint-Eutrope","lat":48.63396,"lng":2.39534}]}, + {"id":"486999","lines":[{"mode":"tram","line":"12"}],"stops":[{"name":"Traité de Rome","lat":48.62508,"lng":2.40758}]}, + {"id":"488087","lines":[{"mode":"rer","line":"E"}],"stops":[{"name":"Nanterre - La Folie","lat":48.89759,"lng":2.22374}]}, + {"id":"490779","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Serge Gainsbourg","lat":48.88132,"lng":2.42719}]}, + {"id":"490782","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"La Dhuys","lat":48.87799,"lng":2.46547}]}, + {"id":"490783","lines":[{"mode":"metro","line":"11"}],"stops":[{"name":"Coteaux Beauclair","lat":48.88224,"lng":2.46736}]}, + {"id":"490784","lines":[{"mode":"metro","line":"14"}],"stops":[{"name":"Saint-Denis – Pleyel","lat":48.91747,"lng":2.34654}]}, + {"id":"490918","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Thérèse Pierre","lat":48.88342,"lng":2.28803}]}, + {"id":"490919","lines":[{"mode":"tram","line":"3b"}],"stops":[{"name":"Anna de Noailles","lat":48.87457,"lng":2.27826}]} + ] +} diff --git a/apps/api/src/transit/idfm.test.ts b/apps/api/src/transit/idfm.test.ts new file mode 100644 index 00000000..d3fc953d --- /dev/null +++ b/apps/api/src/transit/idfm.test.ts @@ -0,0 +1,98 @@ +import { describe, expect, it } from 'vitest'; +import { datasetOf, idfmLicence, MIN_STATIONS, serializeDataset, stationsOf } from './idfm'; +import { TransitDataset, transitDataset } from './stations'; + +/** Records shaped like the export, with fields the import drops. */ +const record = (zone: number, name: string, resCom: string, mode: string, lat = 48.86) => ({ + id_ref_zdc: zone, + nom_gares: name, + res_com: resCom, + mode, + geo_point_2d: { lat, lon: 2.34 }, + exploitant: 'RATP', + picto: { url: 'https://example.com/picto.svg' }, +}); + +const meta = (license = idfmLicence) => ({ + metas: { default: { license, modified: '2025-12-10T14:10:02+00:00' } }, +}); + +describe('stationsOf', () => { + it('groups the lines of a connection zone, metro first, in their natural order', () => { + const stations = stationsOf([ + record(2, 'Gare du Nord', 'RER B', 'RER'), + record(2, 'Gare du Nord', 'METRO 5', 'METRO'), + record(2, 'Gare du Nord', 'METRO 4', 'METRO', 48.861), + record(2, 'Gare du Nord', 'RER B', 'RER'), + record(1, 'Porte de Versailles', 'TRAM 3a', 'TRAMWAY'), + record(1, 'Porte de Versailles', 'METRO 12', 'METRO'), + ]); + expect(stations.map((station) => station.id)).toEqual(['1', '2']); + expect(stations[0]?.lines).toEqual([ + { mode: 'metro', line: '12' }, + { mode: 'tram', line: '3a' }, + ]); + expect(stations[1]?.lines.map((line) => line.line)).toEqual(['4', '5', 'B']); + expect(stations[1]?.stops).toEqual([ + { name: 'Gare du Nord', lat: 48.86, lng: 2.34 }, + { name: 'Gare du Nord', lat: 48.861, lng: 2.34 }, + ]); + }); + + it('keeps the metro, the RER and the tram only, and cleans invisible characters', () => { + const stations = stationsOf([ + record(1, 'Porte de Champerret\u200b', 'METRO 3', 'METRO'), + record(2, 'Juvisy', 'TRAIN R', 'TRAIN'), + record(3, 'Orly', 'VAL ORLYVAL', 'VAL'), + record(4, 'Tram 14', 'TRAM 14', 'TRAM'), + ]); + expect(stations.map((station) => station.stops[0]?.name)).toEqual([ + 'Porte de Champerret', + 'Tram 14', + ]); + }); + + it('refuses a record outside the region, with markup, or with an odd line', () => { + expect(() => stationsOf([record(1, 'Nulle part', 'METRO 1', 'METRO', 40)])).toThrow(); + expect(() => stationsOf([record(1, 'Gare', 'METRO 1', 'METRO')])).toThrow(); + expect(() => stationsOf([record(1, 'Gare', 'METRO 1; drop', 'METRO')])).toThrow(); + expect(() => stationsOf({ records: [] })).toThrow(); + }); +}); + +describe('datasetOf', () => { + const many = Array.from({ length: MIN_STATIONS }, (_, index) => + record(index + 1, `Station ${index + 1}`, 'METRO 1', 'METRO'), + ); + + it('attributes the dataset to IDFM with the date of its last update', () => { + expect(datasetOf(many, meta()).source).toMatchObject({ + publisher: 'Île-de-France Mobilités', + licence: 'Licence Ouverte v2.0 (Etalab)', + modifiedOn: '2025-12-10', + }); + }); + + it('refuses another licence, or an export too short to be whole', () => { + expect(() => datasetOf(many, meta('Open Database License (ODbL)'))).toThrow(/licence/); + expect(() => datasetOf(many.slice(1), meta())).toThrow(/truncated/); + }); + + it('serializes one station per line, read back as written', () => { + const dataset = datasetOf(many, meta()); + const text = serializeDataset(dataset); + expect(text.split('\n')).toHaveLength(MIN_STATIONS + 6); + expect(TransitDataset.parse(JSON.parse(text))).toEqual(dataset); + }); +}); + +describe('the reference file', () => { + it('is a whole import of IDFM, under the Licence Ouverte', () => { + expect(transitDataset.stations.length).toBeGreaterThanOrEqual(MIN_STATIONS); + expect(transitDataset.source.licence).toBe(idfmLicence); + const abbesses = transitDataset.stations.find((station) => + station.stops.some((stop) => stop.name === 'Abbesses'), + ); + expect(abbesses?.lines).toEqual([{ mode: 'metro', line: '12' }]); + }); +}); diff --git a/apps/api/src/transit/idfm.ts b/apps/api/src/transit/idfm.ts new file mode 100644 index 00000000..f44cf70c --- /dev/null +++ b/apps/api/src/transit/idfm.ts @@ -0,0 +1,135 @@ +/** + * Import of the dataset « Gares et stations du réseau ferré d'Île-de-France (par ligne) » of + * Île-de-France Mobilités (`emplacement-des-gares-idf`): one record per station and line, kept + * for the metro, the RER and the tram, grouped by connection zone. Every record is validated + * before it enters the reference file; an unexpected licence or a short export is refused. + */ +import { roundedPoint } from '@widoo/orchestration'; +import { z } from 'zod'; +import { + stationModes, + TransitDataset, + TransitStation, + type StationMode, + type TransitLine, +} from './stations'; + +const catalogue = + 'https://data.iledefrance-mobilites.fr/api/explore/v2.1/catalog/datasets/emplacement-des-gares-idf'; +/** Fixed addresses: the import never fetches a URL it is given. */ +export const idfmMetaUrl = catalogue; +export const idfmExportUrl = `${catalogue}/exports/json`; + +export const idfmLicence = 'Licence Ouverte v2.0 (Etalab)'; + +const source = { + publisher: 'Île-de-France Mobilités', + dataset: "Gares et stations du réseau ferré d'Île-de-France (par ligne)", + url: 'https://data.iledefrance-mobilites.fr/explore/dataset/emplacement-des-gares-idf/', + licence: idfmLicence, + licenceUrl: 'https://www.etalab.gouv.fr/licence-ouverte-open-licence/', +} as const; + +/** Below that many stations, the export is taken for truncated and the file is kept as it is. */ +export const MIN_STATIONS = 500; + +const modeOf: Record = { + METRO: 'metro', + RER: 'rer', + TRAMWAY: 'tram', + TRAM: 'tram', +}; + +/** The fields read from a record, the others are dropped; coordinates within the region. */ +const IdfmRecord = z.object({ + id_ref_zdc: z.number().int().positive(), + nom_gares: z.string(), + res_com: z.string(), + mode: z.string(), + geo_point_2d: z.object({ + lat: z.number().min(48).max(49.5), + lon: z.number().min(1.4).max(3.6), + }), +}); +type IdfmRecord = z.infer; + +/** What the catalogue says of the dataset: its licence and its last update. */ +export const IdfmMeta = z.object({ + metas: z.object({ + default: z.object({ license: z.string(), modified: z.iso.datetime({ offset: true }) }), + }), +}); + +/** Without invisible characters: a zero-width space follows `Porte de Champerret`. */ +const cleanName = (name: string) => + name + .normalize('NFC') + .replace(/\p{Cf}/gu, '') + .replace(/\s+/g, ' ') + .trim(); + +/** `METRO 12` → `12`, `RER A` → `A`, `TRAM 3a` → `3a`. */ +const lineOf = (record: IdfmRecord): TransitLine | null => { + const mode = modeOf[record.mode]; + return mode ? { mode, line: record.res_com.replace(/^\S+\s+/, '').trim() } : null; +}; + +const compareLines = (a: TransitLine, b: TransitLine) => + stationModes.indexOf(a.mode) - stationModes.indexOf(b.mode) || + a.line.localeCompare(b.line, 'fr', { numeric: true }); + +/** A record of any mode: only those of `modeOf` are read further. */ +const AnyRecord = z.looseObject({ mode: z.string() }); + +type Zone = { + lines: Map; + stops: Map; +}; + +/** The stations of the export, sorted by id, their lines in the order of `stationModes`. */ +export function stationsOf(records: unknown): TransitStation[] { + const byZone = new Map(); + for (const record of z.array(AnyRecord).parse(records)) { + if (!modeOf[record.mode]) continue; + const parsed = IdfmRecord.parse(record); + const line = lineOf(parsed); + if (!line) continue; + const zone = byZone.get(parsed.id_ref_zdc) ?? { lines: new Map(), stops: new Map() }; + byZone.set(parsed.id_ref_zdc, zone); + zone.lines.set(`${line.mode}:${line.line}`, line); + const point = roundedPoint({ lat: parsed.geo_point_2d.lat, lng: parsed.geo_point_2d.lon }); + const stop = { name: cleanName(parsed.nom_gares), ...point }; + zone.stops.set(`${stop.name}:${stop.lat}:${stop.lng}`, stop); + } + return [...byZone] + .sort(([a], [b]) => a - b) + .map(([id, zone]) => + TransitStation.parse({ + id: String(id), + lines: [...zone.lines.values()].sort(compareLines), + stops: [...zone.stops.values()], + }), + ); +} + +/** The reference file of an export and its catalogue entry. */ +export function datasetOf(records: unknown, meta: unknown): TransitDataset { + const { license, modified } = IdfmMeta.parse(meta).metas.default; + if (license !== idfmLicence) { + throw new Error(`The licence of the dataset changed (${license}): check it before importing`); + } + const stations = stationsOf(records); + if (stations.length < MIN_STATIONS) { + throw new Error(`Only ${stations.length} stations in the export: truncated, not imported`); + } + return TransitDataset.parse({ + source: { ...source, modifiedOn: modified.slice(0, 10) }, + stations, + }); +} + +/** The file as it is versioned: one station per line, so that a refresh diffs station by station. */ +export function serializeDataset(dataset: TransitDataset): string { + const stations = dataset.stations.map((station) => ` ${JSON.stringify(station)}`); + return `{\n "source": ${JSON.stringify(dataset.source)},\n "stations": [\n${stations.join(',\n')}\n ]\n}\n`; +} diff --git a/apps/api/src/transit/import-cli.ts b/apps/api/src/transit/import-cli.ts new file mode 100644 index 00000000..100f20d0 --- /dev/null +++ b/apps/api/src/transit/import-cli.ts @@ -0,0 +1,41 @@ +/** + * `pnpm --filter @widoo/api transit:import`: refreshes `generated/stations.json` from the open + * data of Île-de-France Mobilités, to commit through a pull request. Downloads the export and the + * catalogue entry from their fixed addresses; `--file --meta ` + * reads files downloaded by hand instead. Run it after an update of the dataset (its catalogue + * gives the date), at least once a quarter. + */ +import { readFile, writeFile } from 'node:fs/promises'; +import { parseArgs } from 'node:util'; +import { datasetOf, idfmExportUrl, idfmMetaUrl, serializeDataset } from './idfm'; + +const outputPath = new URL('./generated/stations.json', import.meta.url); +/** The export weighs about 1.2 MB: anything far larger is not the dataset expected. */ +const MAX_BYTES = 20 * 1024 * 1024; + +async function download(url: string): Promise { + const response = await fetch(url, { signal: AbortSignal.timeout(60_000), redirect: 'error' }); + if (!response.ok) throw new Error(`${url} answered ${response.status}`); + const body = await response.text(); + if (body.length > MAX_BYTES) throw new Error(`${url} answered more than ${MAX_BYTES} bytes`); + return JSON.parse(body); +} + +const readJson = async (path: string): Promise => JSON.parse(await readFile(path, 'utf8')); + +const { values } = parseArgs({ + options: { file: { type: 'string' }, meta: { type: 'string' } }, +}); +if ((values.file === undefined) !== (values.meta === undefined)) { + throw new Error('--file and --meta go together'); +} +const [records, meta] = await Promise.all( + values.file && values.meta + ? [readJson(values.file), readJson(values.meta)] + : [download(idfmExportUrl), download(idfmMetaUrl)], +); +const dataset = datasetOf(records, meta); +await writeFile(outputPath, serializeDataset(dataset)); +console.info( + `${dataset.stations.length} stations written, dataset of ${dataset.source.modifiedOn}`, +); diff --git a/apps/api/src/transit/stations.ts b/apps/api/src/transit/stations.ts new file mode 100644 index 00000000..19ccf177 --- /dev/null +++ b/apps/api/src/transit/stations.ts @@ -0,0 +1,50 @@ +/** + * Stations of the rail network of Île-de-France (metro, RER, tram), from the open data of + * Île-de-France Mobilités under the Licence Ouverte v2.0 (Etalab): the reference file + * `generated/stations.json`, written by `pnpm --filter @widoo/api transit:import` and versioned, + * so that the API never calls the network for it. Validated at load: a broken file fails loudly. + */ +import { HttpsUrl, Latitude, Longitude, plainText } from '@widoo/shared'; +import { z } from 'zod'; +import raw from './generated/stations.json'; + +/** Modes of the access (D-050): the metro, the RER and the tram, in the order lines are listed. */ +export const stationModes = ['metro', 'rer', 'tram'] as const; +export type StationMode = (typeof stationModes)[number]; + +/** A line as the operator names it: `12`, `3bis`, `A`, `3a`. */ +export const TransitLine = z.object({ + mode: z.enum(stationModes), + line: z.string().regex(/^[0-9A-Za-z]{1,5}$/), +}); +export type TransitLine = z.infer; + +/** Where a line stops: its name may differ from one line to another of the same station. */ +const TransitStop = z.object({ name: plainText(80), lat: Latitude, lng: Longitude }); + +/** A connection zone of IDFM (`id_ref_zdc`): every line that stops there, and their stops. */ +export const TransitStation = z.object({ + id: z.string().regex(/^\d+$/), + lines: z.array(TransitLine).min(1), + stops: z.array(TransitStop).min(1), +}); +export type TransitStation = z.infer; + +/** What the attribution of the Licence Ouverte asks: the source and its last update. */ +export const TransitSource = z.object({ + publisher: z.literal('Île-de-France Mobilités'), + dataset: z.string().min(1), + url: HttpsUrl, + licence: z.literal('Licence Ouverte v2.0 (Etalab)'), + licenceUrl: HttpsUrl, + /** Last update of the dataset at IDFM, as its catalogue gives it. */ + modifiedOn: z.iso.date(), +}); + +export const TransitDataset = z.object({ + source: TransitSource, + stations: z.array(TransitStation).min(1), +}); +export type TransitDataset = z.infer; + +export const transitDataset: TransitDataset = TransitDataset.parse(raw);