diff --git a/src/tam/network.js b/src/tam/network.js index 73a65e7..62bbd57 100644 --- a/src/tam/network.js +++ b/src/tam/network.js @@ -88,7 +88,8 @@ a “ref” tag`); stop.lat ], { name: stop.tags.name, - routes: [[lineRef, routeRef]] + routes: [[lineRef, routeRef]], + successors: [], }); } else { stops[stop.tags.ref].properties.routes.push([ @@ -223,6 +224,7 @@ different sequence of nodes in two or more lines.`); routes: [[lineRef, routeRef]] }); + stops[begin].properties.successors.push(end); segments[id].properties.length = ( 1000 * turfLength(segments[id])); } diff --git a/src/tam/network.json b/src/tam/network.json index 6b79105..a34dc4e 100644 --- a/src/tam/network.json +++ b/src/tam/network.json @@ -13,6 +13,9 @@ "1", 3 ] + ], + "successors": [ + "41163" ] }, "geometry": { @@ -36,6 +39,9 @@ "1", 3 ] + ], + "successors": [ + "41105" ] }, "geometry": { @@ -59,6 +65,9 @@ "1", 3 ] + ], + "successors": [ + "41107" ] }, "geometry": { @@ -82,6 +91,9 @@ "1", 3 ] + ], + "successors": [ + "41109" ] }, "geometry": { @@ -105,6 +117,9 @@ "1", 3 ] + ], + "successors": [ + "41111" ] }, "geometry": { @@ -128,6 +143,9 @@ "1", 3 ] + ], + "successors": [ + "41113" ] }, "geometry": { @@ -151,6 +169,9 @@ "1", 3 ] + ], + "successors": [ + "41115" ] }, "geometry": { @@ -174,6 +195,9 @@ "1", 3 ] + ], + "successors": [ + "41117" ] }, "geometry": { @@ -197,6 +221,9 @@ "1", 3 ] + ], + "successors": [ + "41119" ] }, "geometry": { @@ -220,6 +247,9 @@ "1", 3 ] + ], + "successors": [ + "41121" ] }, "geometry": { @@ -243,6 +273,9 @@ "1", 3 ] + ], + "successors": [ + "41123" ] }, "geometry": { @@ -266,6 +299,9 @@ "1", 3 ] + ], + "successors": [ + "41125" ] }, "geometry": { @@ -289,6 +325,9 @@ "1", 3 ] + ], + "successors": [ + "41127" ] }, "geometry": { @@ -312,6 +351,10 @@ "1", 3 ] + ], + "successors": [ + "41129", + "44202" ] }, "geometry": { @@ -335,6 +378,9 @@ "1", 2 ] + ], + "successors": [ + "41131" ] }, "geometry": { @@ -358,6 +404,10 @@ "1", 2 ] + ], + "successors": [ + "42231", + "41133" ] }, "geometry": { @@ -381,6 +431,9 @@ "1", 2 ] + ], + "successors": [ + "41135" ] }, "geometry": { @@ -404,6 +457,10 @@ "1", 2 ] + ], + "successors": [ + "42245", + "41138" ] }, "geometry": { @@ -431,6 +488,9 @@ "1", 3 ] + ], + "successors": [ + "41139" ] }, "geometry": { @@ -458,6 +518,9 @@ "1", 3 ] + ], + "successors": [ + "41141" ] }, "geometry": { @@ -485,6 +548,9 @@ "1", 3 ] + ], + "successors": [ + "41143" ] }, "geometry": { @@ -516,6 +582,9 @@ "1", 3 ] + ], + "successors": [ + "41145" ] }, "geometry": { @@ -547,6 +616,10 @@ "1", 3 ] + ], + "successors": [ + "43217", + "41147" ] }, "geometry": { @@ -578,6 +651,9 @@ "1", 3 ] + ], + "successors": [ + "41149" ] }, "geometry": { @@ -609,6 +685,10 @@ "1", 3 ] + ], + "successors": [ + "43145", + "41151" ] }, "geometry": { @@ -632,6 +712,9 @@ "1", 3 ] + ], + "successors": [ + "41153" ] }, "geometry": { @@ -655,6 +738,9 @@ "1", 3 ] + ], + "successors": [ + "41161" ] }, "geometry": { @@ -678,7 +764,8 @@ "1", 3 ] - ] + ], + "successors": [] }, "geometry": { "type": "Point", @@ -701,6 +788,9 @@ "1", 3 ] + ], + "successors": [ + "41155" ] }, "geometry": { @@ -724,6 +814,9 @@ "1", 3 ] + ], + "successors": [ + "41103" ] }, "geometry": { @@ -747,6 +840,9 @@ "1", 1 ] + ], + "successors": [ + "41261" ] }, "geometry": { @@ -770,6 +866,9 @@ "1", 1 ] + ], + "successors": [ + "41205" ] }, "geometry": { @@ -793,6 +892,9 @@ "1", 1 ] + ], + "successors": [ + "41207" ] }, "geometry": { @@ -824,6 +926,9 @@ "1", 1 ] + ], + "successors": [ + "41209" ] }, "geometry": { @@ -855,6 +960,10 @@ "1", 1 ] + ], + "successors": [ + "42237", + "41211" ] }, "geometry": { @@ -886,6 +995,9 @@ "1", 1 ] + ], + "successors": [ + "41213" ] }, "geometry": { @@ -917,6 +1029,10 @@ "1", 1 ] + ], + "successors": [ + "42137", + "41215" ] }, "geometry": { @@ -944,6 +1060,9 @@ "1", 1 ] + ], + "successors": [ + "41217" ] }, "geometry": { @@ -971,6 +1090,9 @@ "1", 1 ] + ], + "successors": [ + "41218" ] }, "geometry": { @@ -998,6 +1120,11 @@ "1", 1 ] + ], + "successors": [ + "42243", + "41221", + "43127" ] }, "geometry": { @@ -1021,6 +1148,9 @@ "1", 0 ] + ], + "successors": [ + "41223" ] }, "geometry": { @@ -1044,6 +1174,10 @@ "1", 0 ] + ], + "successors": [ + "42141", + "41225" ] }, "geometry": { @@ -1067,6 +1201,9 @@ "1", 0 ] + ], + "successors": [ + "41227" ] }, "geometry": { @@ -1090,6 +1227,10 @@ "1", 0 ] + ], + "successors": [ + "44202", + "41229" ] }, "geometry": { @@ -1113,6 +1254,9 @@ "1", 1 ] + ], + "successors": [ + "41231" ] }, "geometry": { @@ -1136,6 +1280,9 @@ "1", 1 ] + ], + "successors": [ + "41233" ] }, "geometry": { @@ -1159,6 +1306,9 @@ "1", 1 ] + ], + "successors": [ + "41235" ] }, "geometry": { @@ -1182,6 +1332,9 @@ "1", 1 ] + ], + "successors": [ + "41237" ] }, "geometry": { @@ -1205,6 +1358,9 @@ "1", 1 ] + ], + "successors": [ + "41239" ] }, "geometry": { @@ -1228,6 +1384,9 @@ "1", 1 ] + ], + "successors": [ + "41241" ] }, "geometry": { @@ -1251,6 +1410,9 @@ "1", 1 ] + ], + "successors": [ + "41243" ] }, "geometry": { @@ -1274,6 +1436,9 @@ "1", 1 ] + ], + "successors": [ + "41245" ] }, "geometry": { @@ -1297,6 +1462,9 @@ "1", 1 ] + ], + "successors": [ + "41247" ] }, "geometry": { @@ -1320,6 +1488,9 @@ "1", 1 ] + ], + "successors": [ + "41249" ] }, "geometry": { @@ -1343,6 +1514,9 @@ "1", 1 ] + ], + "successors": [ + "41251" ] }, "geometry": { @@ -1366,6 +1540,9 @@ "1", 1 ] + ], + "successors": [ + "41253" ] }, "geometry": { @@ -1389,6 +1566,9 @@ "1", 1 ] + ], + "successors": [ + "41263" ] }, "geometry": { @@ -1412,7 +1592,8 @@ "1", 1 ] - ] + ], + "successors": [] }, "geometry": { "type": "Point", @@ -1435,6 +1616,9 @@ "1", 1 ] + ], + "successors": [ + "41203" ] }, "geometry": { @@ -1458,6 +1642,9 @@ "1", 1 ] + ], + "successors": [ + "41255" ] }, "geometry": { @@ -1477,6 +1664,9 @@ "2", 0 ] + ], + "successors": [ + "42103" ] }, "geometry": { @@ -1496,6 +1686,9 @@ "2", 0 ] + ], + "successors": [ + "42105" ] }, "geometry": { @@ -1515,6 +1708,9 @@ "2", 0 ] + ], + "successors": [ + "42107" ] }, "geometry": { @@ -1534,6 +1730,9 @@ "2", 0 ] + ], + "successors": [ + "42111" ] }, "geometry": { @@ -1553,6 +1752,9 @@ "2", 0 ] + ], + "successors": [ + "42113" ] }, "geometry": { @@ -1572,6 +1774,9 @@ "2", 0 ] + ], + "successors": [ + "42115" ] }, "geometry": { @@ -1591,6 +1796,9 @@ "2", 0 ] + ], + "successors": [ + "42117" ] }, "geometry": { @@ -1610,6 +1818,9 @@ "2", 0 ] + ], + "successors": [ + "42119" ] }, "geometry": { @@ -1629,6 +1840,9 @@ "2", 0 ] + ], + "successors": [ + "42121" ] }, "geometry": { @@ -1648,6 +1862,9 @@ "2", 0 ] + ], + "successors": [ + "42123" ] }, "geometry": { @@ -1675,6 +1892,9 @@ "2", 0 ] + ], + "successors": [ + "42125" ] }, "geometry": { @@ -1702,6 +1922,11 @@ "2", 0 ] + ], + "successors": [ + "43127", + "42127", + "41221" ] }, "geometry": { @@ -1721,6 +1946,9 @@ "4", 1 ] + ], + "successors": [ + "41138" ] }, "geometry": { @@ -1744,6 +1972,9 @@ "3", 3 ] + ], + "successors": [ + "42131" ] }, "geometry": { @@ -1767,6 +1998,9 @@ "3", 3 ] + ], + "successors": [ + "42133" ] }, "geometry": { @@ -1790,6 +2024,9 @@ "3", 3 ] + ], + "successors": [ + "41147" ] }, "geometry": { @@ -1809,6 +2046,9 @@ "4", 2 ] + ], + "successors": [ + "42139" ] }, "geometry": { @@ -1828,6 +2068,9 @@ "4", 2 ] + ], + "successors": [ + "41225" ] }, "geometry": { @@ -1847,6 +2090,9 @@ "2", 0 ] + ], + "successors": [ + "42143" ] }, "geometry": { @@ -1866,6 +2112,9 @@ "2", 0 ] + ], + "successors": [ + "42145" ] }, "geometry": { @@ -1885,6 +2134,9 @@ "2", 0 ] + ], + "successors": [ + "42147" ] }, "geometry": { @@ -1904,6 +2156,9 @@ "2", 0 ] + ], + "successors": [ + "42149" ] }, "geometry": { @@ -1923,6 +2178,9 @@ "2", 0 ] + ], + "successors": [ + "42151" ] }, "geometry": { @@ -1942,6 +2200,9 @@ "2", 0 ] + ], + "successors": [ + "42153" ] }, "geometry": { @@ -1961,6 +2222,9 @@ "2", 0 ] + ], + "successors": [ + "42155" ] }, "geometry": { @@ -1980,6 +2244,9 @@ "2", 0 ] + ], + "successors": [ + "42157" ] }, "geometry": { @@ -1999,6 +2266,9 @@ "2", 0 ] + ], + "successors": [ + "42159" ] }, "geometry": { @@ -2018,6 +2288,9 @@ "2", 0 ] + ], + "successors": [ + "42163" ] }, "geometry": { @@ -2037,6 +2310,9 @@ "2", 0 ] + ], + "successors": [ + "42165" ] }, "geometry": { @@ -2056,6 +2332,9 @@ "2", 0 ] + ], + "successors": [ + "42167" ] }, "geometry": { @@ -2075,6 +2354,9 @@ "2", 0 ] + ], + "successors": [ + "42169" ] }, "geometry": { @@ -2094,7 +2376,8 @@ "2", 0 ] - ] + ], + "successors": [] }, "geometry": { "type": "Point", @@ -2113,6 +2396,9 @@ "2", 1 ] + ], + "successors": [ + "42203" ] }, "geometry": { @@ -2132,6 +2418,9 @@ "2", 1 ] + ], + "successors": [ + "42205" ] }, "geometry": { @@ -2151,6 +2440,9 @@ "2", 1 ] + ], + "successors": [ + "42207" ] }, "geometry": { @@ -2170,6 +2462,9 @@ "2", 1 ] + ], + "successors": [ + "42211" ] }, "geometry": { @@ -2189,6 +2484,9 @@ "2", 1 ] + ], + "successors": [ + "42213" ] }, "geometry": { @@ -2208,6 +2506,9 @@ "2", 1 ] + ], + "successors": [ + "42215" ] }, "geometry": { @@ -2227,6 +2528,9 @@ "2", 1 ] + ], + "successors": [ + "42217" ] }, "geometry": { @@ -2246,6 +2550,9 @@ "2", 1 ] + ], + "successors": [ + "42219" ] }, "geometry": { @@ -2265,6 +2572,9 @@ "2", 1 ] + ], + "successors": [ + "42221" ] }, "geometry": { @@ -2284,6 +2594,9 @@ "2", 1 ] + ], + "successors": [ + "42223" ] }, "geometry": { @@ -2303,6 +2616,9 @@ "2", 1 ] + ], + "successors": [ + "42225" ] }, "geometry": { @@ -2322,6 +2638,9 @@ "2", 1 ] + ], + "successors": [ + "42227" ] }, "geometry": { @@ -2341,6 +2660,9 @@ "2", 1 ] + ], + "successors": [ + "42229" ] }, "geometry": { @@ -2360,6 +2682,9 @@ "2", 1 ] + ], + "successors": [ + "41133" ] }, "geometry": { @@ -2379,6 +2704,9 @@ "4", 0 ] + ], + "successors": [ + "42233" ] }, "geometry": { @@ -2398,6 +2726,9 @@ "4", 0 ] + ], + "successors": [ + "41143" ] }, "geometry": { @@ -2421,6 +2752,9 @@ "3", 1 ] + ], + "successors": [ + "42239" ] }, "geometry": { @@ -2444,6 +2778,9 @@ "3", 1 ] + ], + "successors": [ + "42241" ] }, "geometry": { @@ -2467,6 +2804,9 @@ "3", 1 ] + ], + "successors": [ + "43127" ] }, "geometry": { @@ -2486,6 +2826,9 @@ "4", 3 ] + ], + "successors": [ + "42245" ] }, "geometry": { @@ -2513,6 +2856,9 @@ "2", 1 ] + ], + "successors": [ + "42247" ] }, "geometry": { @@ -2540,6 +2886,10 @@ "2", 1 ] + ], + "successors": [ + "43133", + "42249" ] }, "geometry": { @@ -2559,6 +2909,9 @@ "2", 1 ] + ], + "successors": [ + "42251" ] }, "geometry": { @@ -2578,6 +2931,9 @@ "2", 1 ] + ], + "successors": [ + "42253" ] }, "geometry": { @@ -2597,6 +2953,9 @@ "2", 1 ] + ], + "successors": [ + "42255" ] }, "geometry": { @@ -2616,6 +2975,9 @@ "2", 1 ] + ], + "successors": [ + "42257" ] }, "geometry": { @@ -2635,6 +2997,9 @@ "2", 1 ] + ], + "successors": [ + "42259" ] }, "geometry": { @@ -2654,6 +3019,9 @@ "2", 1 ] + ], + "successors": [ + "42263" ] }, "geometry": { @@ -2673,6 +3041,9 @@ "2", 1 ] + ], + "successors": [ + "42265" ] }, "geometry": { @@ -2692,6 +3063,9 @@ "2", 1 ] + ], + "successors": [ + "42267" ] }, "geometry": { @@ -2711,6 +3085,9 @@ "2", 1 ] + ], + "successors": [ + "42269" ] }, "geometry": { @@ -2730,7 +3107,8 @@ "2", 1 ] - ] + ], + "successors": [] }, "geometry": { "type": "Point", @@ -2753,6 +3131,9 @@ "3", 3 ] + ], + "successors": [ + "43103" ] }, "geometry": { @@ -2776,6 +3157,9 @@ "3", 3 ] + ], + "successors": [ + "43105" ] }, "geometry": { @@ -2799,6 +3183,9 @@ "3", 3 ] + ], + "successors": [ + "43107" ] }, "geometry": { @@ -2822,6 +3209,9 @@ "3", 3 ] + ], + "successors": [ + "43109" ] }, "geometry": { @@ -2845,6 +3235,9 @@ "3", 3 ] + ], + "successors": [ + "43111" ] }, "geometry": { @@ -2868,6 +3261,9 @@ "3", 3 ] + ], + "successors": [ + "43113" ] }, "geometry": { @@ -2891,6 +3287,9 @@ "3", 3 ] + ], + "successors": [ + "43115" ] }, "geometry": { @@ -2914,6 +3313,9 @@ "3", 3 ] + ], + "successors": [ + "43117" ] }, "geometry": { @@ -2937,6 +3339,9 @@ "3", 3 ] + ], + "successors": [ + "43119" ] }, "geometry": { @@ -2960,6 +3365,9 @@ "3", 3 ] + ], + "successors": [ + "43121" ] }, "geometry": { @@ -2983,6 +3391,9 @@ "3", 3 ] + ], + "successors": [ + "43123" ] }, "geometry": { @@ -3006,6 +3417,9 @@ "3", 3 ] + ], + "successors": [ + "43125" ] }, "geometry": { @@ -3037,6 +3451,9 @@ "1", 3 ] + ], + "successors": [ + "43231" ] }, "geometry": { @@ -3068,6 +3485,9 @@ "1", 1 ] + ], + "successors": [ + "43233" ] }, "geometry": { @@ -3091,6 +3511,9 @@ "4", 3 ] + ], + "successors": [ + "43135" ] }, "geometry": { @@ -3114,6 +3537,9 @@ "4", 3 ] + ], + "successors": [ + "44201" ] }, "geometry": { @@ -3137,6 +3563,9 @@ "4", 3 ] + ], + "successors": [ + "43141" ] }, "geometry": { @@ -3160,6 +3589,9 @@ "4", 3 ] + ], + "successors": [ + "41211" ] }, "geometry": { @@ -3183,6 +3615,9 @@ "3", 3 ] + ], + "successors": [ + "43149" ] }, "geometry": { @@ -3206,6 +3641,10 @@ "3", 3 ] + ], + "successors": [ + "43161", + "43151" ] }, "geometry": { @@ -3225,6 +3664,9 @@ "3", 3 ] + ], + "successors": [ + "43153" ] }, "geometry": { @@ -3244,6 +3686,9 @@ "3", 3 ] + ], + "successors": [ + "43155" ] }, "geometry": { @@ -3263,6 +3708,9 @@ "3", 3 ] + ], + "successors": [ + "43157" ] }, "geometry": { @@ -3282,7 +3730,8 @@ "3", 3 ] - ] + ], + "successors": [] }, "geometry": { "type": "Point", @@ -3301,7 +3750,8 @@ "3", 2 ] - ] + ], + "successors": [] }, "geometry": { "type": "Point", @@ -3320,6 +3770,9 @@ "3", 2 ] + ], + "successors": [ + "43159" ] }, "geometry": { @@ -3339,6 +3792,9 @@ "3", 1 ] + ], + "successors": [ + "43203" ] }, "geometry": { @@ -3358,6 +3814,9 @@ "3", 1 ] + ], + "successors": [ + "43205" ] }, "geometry": { @@ -3377,6 +3836,9 @@ "3", 1 ] + ], + "successors": [ + "43207" ] }, "geometry": { @@ -3396,6 +3858,9 @@ "3", 1 ] + ], + "successors": [ + "43209" ] }, "geometry": { @@ -3419,6 +3884,9 @@ "3", 1 ] + ], + "successors": [ + "43213" ] }, "geometry": { @@ -3442,6 +3910,9 @@ "3", 1 ] + ], + "successors": [ + "41207" ] }, "geometry": { @@ -3465,6 +3936,9 @@ "4", 1 ] + ], + "successors": [ + "43219" ] }, "geometry": { @@ -3488,6 +3962,9 @@ "4", 1 ] + ], + "successors": [ + "44105" ] }, "geometry": { @@ -3511,6 +3988,9 @@ "4", 1 ] + ], + "successors": [ + "43225" ] }, "geometry": { @@ -3534,6 +4014,9 @@ "4", 1 ] + ], + "successors": [ + "42123" ] }, "geometry": { @@ -3565,6 +4048,11 @@ "1", 3 ] + ], + "successors": [ + "42245", + "42129", + "41138" ] }, "geometry": { @@ -3596,6 +4084,10 @@ "1", 1 ] + ], + "successors": [ + "44104", + "43235" ] }, "geometry": { @@ -3619,6 +4111,9 @@ "3", 1 ] + ], + "successors": [ + "43237" ] }, "geometry": { @@ -3642,6 +4137,9 @@ "3", 1 ] + ], + "successors": [ + "43239" ] }, "geometry": { @@ -3665,6 +4163,9 @@ "3", 1 ] + ], + "successors": [ + "43241" ] }, "geometry": { @@ -3688,6 +4189,9 @@ "3", 1 ] + ], + "successors": [ + "43243" ] }, "geometry": { @@ -3711,6 +4215,9 @@ "3", 1 ] + ], + "successors": [ + "43245" ] }, "geometry": { @@ -3734,6 +4241,9 @@ "3", 1 ] + ], + "successors": [ + "43247" ] }, "geometry": { @@ -3757,6 +4267,9 @@ "3", 1 ] + ], + "successors": [ + "43249" ] }, "geometry": { @@ -3780,6 +4293,9 @@ "3", 1 ] + ], + "successors": [ + "43251" ] }, "geometry": { @@ -3803,6 +4319,9 @@ "3", 1 ] + ], + "successors": [ + "43253" ] }, "geometry": { @@ -3826,6 +4345,9 @@ "3", 1 ] + ], + "successors": [ + "43255" ] }, "geometry": { @@ -3849,6 +4371,9 @@ "3", 1 ] + ], + "successors": [ + "43257" ] }, "geometry": { @@ -3872,7 +4397,8 @@ "3", 1 ] - ] + ], + "successors": [] }, "geometry": { "type": "Point", @@ -3891,6 +4417,9 @@ "3", 0 ] + ], + "successors": [ + "43261" ] }, "geometry": { @@ -3910,6 +4439,9 @@ "3", 0 ] + ], + "successors": [ + "43209" ] }, "geometry": { @@ -3933,6 +4465,10 @@ "1", 1 ] + ], + "successors": [ + "41129", + "41229" ] }, "geometry": { @@ -3956,6 +4492,9 @@ "1", 1 ] + ], + "successors": [ + "44102" ] }, "geometry": { @@ -3979,6 +4518,9 @@ "1", 1 ] + ], + "successors": [ + "44103" ] }, "geometry": { @@ -4010,6 +4552,9 @@ "4", 1 ] + ], + "successors": [ + "43223" ] }, "geometry": { @@ -4041,6 +4586,9 @@ "4", 3 ] + ], + "successors": [ + "43139" ] }, "geometry": { @@ -4064,6 +4612,9 @@ "1", 3 ] + ], + "successors": [ + "44203" ] }, "geometry": { @@ -4087,6 +4638,9 @@ "1", 3 ] + ], + "successors": [ + "44204" ] }, "geometry": { @@ -4110,6 +4664,9 @@ "1", 3 ] + ], + "successors": [ + "43125" ] }, "geometry": { diff --git a/src/tam/realtime.js b/src/tam/realtime.js index 3f22979..4bf2e07 100644 --- a/src/tam/realtime.js +++ b/src/tam/realtime.js @@ -13,8 +13,9 @@ let currentCourses = null; * @property {string} id Unique identifier for this course. * @property {string} line Transport line number. * @property {string} finalStop Final stop to which the course is headed. - * @property {Object.} nextPassings Next stations to which - * the vehicle will stop, associated to the passing timestamp. + * @property {Array.} nextPassings Next stations to which + * the vehicle will stop, associated to the passing timestamp, ordered by + * increasing passing timestamp. */ /** @@ -52,10 +53,10 @@ const fetch = async() => { id, line, finalStop, - nextPassings: { [stopId]: arrivalTime } + nextPassings: [[stopId, arrivalTime]] }; } else { - courses[id].nextPassings[stopId] = arrivalTime; + courses[id].nextPassings.push([stopId, arrivalTime]); } } @@ -66,7 +67,7 @@ const fetch = async() => { if (!(course.line in network.lines)) { delete courses[courseId]; } else { - for (const stopId of Object.keys(course.nextPassings)) { + for (const [stopId,] of course.nextPassings) { if (!(stopId in network.stops)) { delete courses[courseId]; break; @@ -75,6 +76,13 @@ const fetch = async() => { } } + // Order next passings by increasing passing time + for (const courseId of Object.keys(courses)) { + courses[courseId].nextPassings.sort( + ([, time1], [, time2]) => time1 - time2 + ); + } + currentCourses = courses; } diff --git a/src/tam/simulation.js b/src/tam/simulation.js index 4b03705..05cfbf4 100644 --- a/src/tam/simulation.js +++ b/src/tam/simulation.js @@ -5,14 +5,38 @@ const network = require("./network.json"); const server = "http://localhost:4321"; +const stops = Object.keys(network.stops); + +const findRoute = (from, to) => { + const queue = [[from, []]]; + + while (queue.length) { + const [head, path] = queue.shift(); + + for (const successor of network.stops[head].properties.successors) { + if (successor === to) { + return path.concat([head, successor]); + } + + if (!path.includes(successor)) { + queue.push([successor, path.concat([head])]); + } + } + } + + return null; +}; + class Course { constructor(data) { this.id = data.id; - this.passings = {}; + this.prevPassings = []; + this.nextPassings = []; this.state = null; // Attributes for the `stopped` state this.currentStop = null; + this.departureTime = 0; // Attributes for the `moving` state this.departureStop = null; @@ -38,76 +62,118 @@ class Course { updateData(data) { this.line = data.line; this.finalStop = data.finalStop; - Object.assign(this.passings, data.nextPassings); + this.nextPassings = data.nextPassings; const now = Date.now(); - // Make sure we’re on the right `stopped`/`moving` state if (this.state === null) { - let previousStop = null; - let departureTime = 0; + // Initialize the course on the first available segment + const index = this.nextPassings.findIndex( + ([, time]) => time >= now + ); - let nextStop = null; - let arrivalTime = Infinity; - - for (const [stopId, time] of Object.entries(this.passings)) { - if (time > now && time < arrivalTime) { - nextStop = stopId; - arrivalTime = time; - } - - if (time < now && time > departureTime) { - previousStop = stopId; - departureTime = time; - } - } - - if (nextStop === null) { + if (index === -1) { return false; } - if (previousStop === null) { - // Teleport to the first known stop - this.arriveToStop(nextStop); + if (index === 0) { + this.arriveToStop(this.nextPassings[index][0]); } else { - - // Teleport to the first known segment - this.arriveToStop(previousStop); - this.moveToStop(nextStop, arrivalTime); + this.arriveToStop(this.nextPassings[index - 1][0]); + this.moveToStop(...this.nextPassings[index]); } } else if (this.state === "moving") { - if (this.passings[this.arrivalStop] <= now) { - // Should already be at the next stop - this.arriveToStop(this.arrivalStop); + const index = this.nextPassings.findIndex( + ([stop, ]) => stop === this.arrivalStop + ); + + if (index === -1 || this.nextPassings[index][1] <= now) { + // Next stop is not announced or in the past, + // move towards it as fast as possible + this.arrivalTime = now; } else { // On the right track, update the arrival time - this.arrivalTime = this.passings[this.arrivalStop]; + this.arrivalTime = this.nextPassings[index][1]; } } else { // (this.state === 'stopped') // Try moving to the next stop - let nextStop = null; - let arrivalTime = Infinity; + const index = this.nextPassings.findIndex( + ([stop, ]) => stop === this.currentStop + ); - for (const [stopId, time] of Object.entries(this.passings)) { - if (time > now && time < arrivalTime) { - nextStop = stopId; - arrivalTime = time; + if (index !== -1) { + if (this.nextPassings[index][1] <= now) { + // Current stop is still announced but in the past + if (index + 1 < this.nextPassings.length) { + // Move to next stop + this.moveToStop(...this.nextPassings[index + 1]); + } else { + // No next stop announced, end of course + return false; + } + } else { + // Cannot move yet, departure is in the future + this.departureTime = this.nextPassings[index][1]; } - } + } else { + // Current stop is not announced, find the first stop + // announced in the future to which is connection is + // possible + let found = false; - if (nextStop === null) { - // This course is finished - return false; - } + for ( + let index = 0; + index < this.nextPassings.length; + ++index + ) { + const [stop, arrivalTime] = this.nextPassings[index]; - if (nextStop !== this.currentStop) { - this.moveToStop(nextStop, arrivalTime); + if (arrivalTime > now) { + const route = findRoute(this.currentStop, stop); + + if (route !== null) { + // Move to the first intermediate stop, guess the + // arrival time based on the final arrival time and + // the relative distance of the stops + const midDistance = network.segments[ + `${route[0]}-${route[1]}` + ].properties.length; + + let totalDistance = midDistance; + + for ( + let midIndex = 1; + midIndex + 1 < route.length; + ++midIndex + ) { + totalDistance += network.segments[ + `${route[midIndex]}-${route[midIndex + 1]}` + ].properties.length; + } + + const midTime = now + (arrivalTime - now) * + midDistance / totalDistance; + + this.moveToStop(route[1], midTime); + found = true; + break; + } + } + } + + if (!found) { + // No valid next stop available + return false; + } } } if (this.state === "moving") { - this.speed = this.computeTheoreticalSpeed(); + const segment = this.currentSegment; + const distance = segment.properties.length - this.traveledDistance; + const duration = this.arrivalTime - Date.now(); + this.speed = this.computeSpeed(distance, duration); } return true; @@ -115,7 +181,6 @@ class Course { tick(time) { if (this.state === "moving") { - // Integrate current speed in travelled distance this.traveledDistance += this.speed * time; const segment = this.currentSegment; @@ -155,10 +220,12 @@ class Course { arriveToStop(stop) { this.state = "stopped"; this.currentStop = stop; + this.departureTime = Date.now(); + this.prevPassings.push([stop, Date.now()]); this.position = ( turfProjection.toMercator(network.stops[stop]) - .geometry.coordinates); - this.history.push(["arriveToStop", stop]); + .geometry.coordinates + ); } /** @@ -168,49 +235,43 @@ class Course { * @returns {undefined} */ moveToStop(stop, arrivalTime) { - if (!(`${this.currentStop}-${stop}` in network.segments)) { + const segmentId = `${this.currentStop}-${stop}`; + + if (!(segmentId in network.segments)) { console.warn(`Course ${this.id} cannot go from stop ${this.currentStop} to stop ${stop}. Teleporting to ${stop}`); this.arriveToStop(stop); return; } + const distance = network.segments[segmentId].properties.length; + const duration = arrivalTime - Date.now(); + + if (this.computeSpeed(distance, duration) === 0) { + // Speed would be too low, better wait for some time + return; + } + this.state = "moving"; this.departureStop = this.currentStop; this.arrivalStop = stop; this.arrivalTime = arrivalTime; this.traveledDistance = 0; this.speed = 0; - this.history.push(["moveToStop", stop, arrivalTime]); - - console.info(`Course ${this.id} leaving stop ${this.currentStop} \ -with initial speed ${this.computeTheoreticalSpeed() * 3600} km/h`); } - /** - * Compute the speed that needs to be maintained to arrive on time. - * @returns {number} Speed in meters per millisecond. - */ - computeTheoreticalSpeed() { - if (this.state !== "moving") { + computeSpeed(distance, duration) { + if (duration <= 0) { + // Late: go to maximum speed + return 50 / 3600; + } + + if (distance / duration <= 10 / 3600) { + // Too slow: pause until speed is sufficient return 0; } - const segment = this.currentSegment; - const remainingTime = this.arrivalTime - Date.now(); - const remainingDistance = ( - segment.properties.length - this.traveledDistance - ); - - if (remainingDistance <= 0) { - return 0; - } - if (remainingTime <= 0) { - // We’re late, go to maximum speed - return 50 / 3600; // 50 km/h - } - - return remainingDistance / remainingTime; + return distance / duration; } } @@ -221,16 +282,12 @@ const updateData = async courses => { for (const [id, data] of Object.entries(dataset)) { if (id in courses) { if (!courses[id].updateData(data)) { - console.info(`Course ${id} is finished.`); delete courses[id]; } } else { const newCourse = new Course(data); - if (!newCourse.updateData(data)) { - console.info(`Ignoring course ${id} which is outdated.`); - } else { - console.info(`Course ${id} starting.`); + if (newCourse.updateData(data)) { courses[id] = newCourse; } }