Skip to content

Commit 7cd6987

Browse files
committedAug 22, 2021
✨ Initial configuration for POABus
0 parents  commit 7cd6987

File tree

11 files changed

+3532
-0
lines changed

11 files changed

+3532
-0
lines changed
 

‎.eslintrc

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"parser": "@babel/eslint-parser",
3+
"parserOptions": {
4+
"ecmaVersion": 2020,
5+
"sourceType": "module",
6+
"ecmaFeatures": {
7+
"modules": true
8+
}
9+
},
10+
"rules": {
11+
"semi": ["error", "never"]
12+
}
13+
}

‎.github/workflows/main.yml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Update latest bus data
2+
3+
on:
4+
workflow_dispatch:
5+
schedule:
6+
- cron: "0 5 * * 0"
7+
8+
jobs:
9+
scheduled:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v2
13+
- uses: actions/setup-node@v2
14+
with:
15+
node-version: "16"
16+
- name: "Install dependencies"
17+
run: npm install
18+
- name: "Generate data"
19+
run: npm run data:generate
20+
- name: "Commit data"
21+
run: |-
22+
git config user.name "Automated"
23+
git config user.email "actions@users.noreply.github.com"
24+
git add -A
25+
timestamp=$(date -u)
26+
git commit -m ":camera_flash: Latest data: ${timestamp}" || exit 0
27+
git push

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules/

‎.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v16.7.0

‎.vscode/launch.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"type": "pwa-node",
9+
"request": "launch",
10+
"name": "Launch Program",
11+
"skipFiles": [
12+
"<node_internals>/**"
13+
],
14+
"program": "${workspaceFolder}/scripts/fetchData.js"
15+
}
16+
]
17+
}

‎README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# POA Bus
2+
3+
:construction: WIP

‎babel.config.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"plugins": [
3+
"@babel/plugin-syntax-top-level-await"
4+
]
5+
}

‎data/.gitkeep

Whitespace-only changes.

‎package-lock.json

+3,298
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "poabus",
3+
"version": "1.0.0",
4+
"description": "A bus route visualisation for Porto Alegre's public transportation system",
5+
"type": "module",
6+
"scripts": {
7+
"data:generate": "node scripts/fetchData.js",
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/rafaelaudibert/poabus.git"
13+
},
14+
"author": "Rafael Audibert <me@rafaaudibert.dev>",
15+
"license": "ISC",
16+
"bugs": {
17+
"url": "https://github.com/rafaelaudibert/poabus/issues"
18+
},
19+
"homepage": "https://github.com/rafaelaudibert/poabus#readme",
20+
"dependencies": {
21+
"@turf/circle": "^6.5.0",
22+
"cli-progress": "^3.9.0",
23+
"node-fetch": "^2.6.1",
24+
"p-queue": "^7.1.0"
25+
},
26+
"devDependencies": {
27+
"@babel/core": "^7.15.0",
28+
"@babel/eslint-parser": "^7.15.0",
29+
"@babel/plugin-syntax-top-level-await": "^7.14.5",
30+
"eslint": "^7.32.0"
31+
}
32+
}

‎scripts/fetchData.js

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import fetch from "node-fetch"
2+
import fs from "fs"
3+
import circle from "@turf/circle"
4+
import cliProgress from "cli-progress"
5+
import PQueue from "p-queue"
6+
7+
// Queue used to rate limit our requests
8+
// At most 5 requests at the same time, with at most 5 requests per second
9+
const queue = new PQueue({ concurrency: 5, interval: 1000, intervalCap: 5 })
10+
11+
// create new progress bar
12+
const progressBar = new cliProgress.SingleBar({
13+
format: "Route coordinates",
14+
barCompleteChar: "\u2588",
15+
barIncompleteChar: "\u2591",
16+
hideCursor: true,
17+
})
18+
19+
const promiseStops = fetch(
20+
"http://www.poatransporte.com.br/php/facades/process.php?a=tp&p="
21+
).then((r) => r.json())
22+
const promiseRoutes = fetch(
23+
"http://www.poatransporte.com.br/php/facades/process.php?a=nc&p=%&t=o"
24+
).then((r) => r.json())
25+
// If we want routes for lotations, we can use http://www.poatransporte.com.br/php/facades/process.php?a=nc&p=%&t=l
26+
27+
const [stops, routes] = await Promise.all([promiseStops, promiseRoutes])
28+
console.log("Fetched stops and routes")
29+
30+
// Create an O(1) access to stops using their code
31+
const stopsDict = {}
32+
for (const stop of stops) {
33+
const contour = circle(
34+
{
35+
type: "Feature",
36+
geometry: {
37+
type: "Point",
38+
coordinates: [parseFloat(stop.latitude), parseFloat(stop.longitude)],
39+
},
40+
},
41+
0.015,
42+
{ steps: 3 }
43+
)
44+
45+
stop.contour = contour.geometry.coordinates
46+
stop.usedLevels = new Set()
47+
stopsDict[stop.codigo] = stop
48+
}
49+
console.log("Computed stops data")
50+
51+
// Create an O(1) access to routes using their id, and also fetch their value
52+
const routesDict = {}
53+
progressBar.start(routes.length, 0)
54+
for (const route of routes) {
55+
new Promise(async (resolve, reject) => {
56+
const routeInfo = await queue.add(() =>
57+
fetch(
58+
`http://www.poatransporte.com.br/php/facades/process.php?a=il&p=${route.id}`
59+
).then((r) => r.json())
60+
)
61+
62+
const path = Object.entries(routeInfo)
63+
.filter((obj) => !isNaN(parseInt(obj[0])))
64+
.map((obj) => obj[1])
65+
.map(({ lat, lng }) => [lat, lng])
66+
67+
route.path = path
68+
route.stops = new Set()
69+
routesDict[route.id] = route
70+
71+
// Make sure to increment the progress bar
72+
progressBar.increment()
73+
resolve(true)
74+
})
75+
}
76+
console.log("Scheduled routes data")
77+
78+
// Just make sure every request was fulfilled
79+
await queue.onIdle()
80+
progressBar.stop()
81+
console.log("Computed routes data")
82+
83+
// Fill the table of stops for every route
84+
for (const stop of stops) {
85+
for (const line of stop.linhas) {
86+
const route = routesDict[line.idLinha]
87+
if (route) route.stops.add(stop.codigo)
88+
}
89+
}
90+
console.log("Filled stops table for every route")
91+
92+
// Fill the table of levels for every route
93+
for (const route of routes) {
94+
const routeStops = Array.from(route.stops, (stopId) => stopsDict[stopId])
95+
for (let level = 0; !route.level; level++) {
96+
const hasUsedLevel = routeStops.some((stop) => stop.usedLevels.has(level))
97+
if (!hasUsedLevel) route.level = level
98+
}
99+
100+
// Fill for every stop, that we used this level
101+
for (const stop of routeStops) {
102+
stop.usedLevels.add(route.level)
103+
}
104+
}
105+
console.log("Filled table of levels for every route")
106+
107+
// Compute every missing information to save on the file
108+
const saveableStops = stops.map((stop) => ({
109+
...stop,
110+
level: Math.max(...stop.usedLevels),
111+
usedLevels: undefined,
112+
}))
113+
const saveableRoutes = routes.map((route) => ({
114+
...route,
115+
stops: undefined,
116+
path: route.path.map((coordinate) => [
117+
...coordinate,
118+
(route.level - 1) * 100,
119+
]),
120+
}))
121+
const levels = Object.fromEntries(
122+
saveableStops.map((stop) => [stop.codigo, stop.level])
123+
)
124+
125+
fs.writeFileSync("data/routes.min.json", JSON.stringify(saveableRoutes))
126+
fs.writeFileSync("data/routes.json", JSON.stringify(saveableRoutes, null, 2))
127+
fs.writeFileSync("data/stops.min.json", JSON.stringify(saveableStops))
128+
fs.writeFileSync("data/stops.json", JSON.stringify(saveableStops, null, 2))
129+
fs.writeFileSync("data/levels.min.json", JSON.stringify(levels))
130+
fs.writeFileSync("data/levels.json", JSON.stringify(levels, null, 2))
131+
console.log("Every file was written!")
132+
133+
// Extra information about the files
134+
const maxLevel = Math.max(...Object.values(levels))
135+
console.log(`MAX LEVEL: ${maxLevel}`)

0 commit comments

Comments
 (0)
Please sign in to comment.