+205
@@ -0,0 +1,205 @@
|
|||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/webstorm+all,node
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=webstorm+all,node
|
||||||
|
|
||||||
|
### Node ###
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
### WebStorm+all ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### WebStorm+all Patch ###
|
||||||
|
# Ignores the whole .idea folder and all .iml files
|
||||||
|
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
modules.xml
|
||||||
|
.idea/misc.xml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
# Sonarlint plugin
|
||||||
|
.idea/sonarlint
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/webstorm+all,node
|
||||||
|
|
||||||
|
dist/
|
||||||
+173
@@ -0,0 +1,173 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||||
|
~ All rights reserved.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Editor</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
height: 0;
|
||||||
|
user-select: none; /* standard syntax */
|
||||||
|
-webkit-user-select: none; /* webkit (safari, chrome) browsers */
|
||||||
|
-moz-user-select: none; /* mozilla browsers */
|
||||||
|
-khtml-user-select: none; /* webkit (konqueror) browsers */
|
||||||
|
-ms-user-select: none; /* IE10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
margin: 8px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<table id="stops">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<label><textarea id="inp" rows="3"></textarea></label>
|
||||||
|
</div>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Number.prototype.mod = function (n) {
|
||||||
|
return ((this % n) + n) % n;
|
||||||
|
};
|
||||||
|
let stops = [];
|
||||||
|
let i = 0;
|
||||||
|
let canvas = document.getElementById("canvas");
|
||||||
|
canvas.focus()
|
||||||
|
let ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
let tbl = document.getElementById("stops");
|
||||||
|
let inp = document.getElementById("inp");
|
||||||
|
inp.addEventListener('input', () => {
|
||||||
|
if (inp.value === null || inp.value === '')
|
||||||
|
return;
|
||||||
|
stops = inp.value.split('\n').map(l => {
|
||||||
|
return {
|
||||||
|
name: l,
|
||||||
|
x: null,
|
||||||
|
y: null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
inp.value = ''
|
||||||
|
i = 0;
|
||||||
|
canvas.focus();
|
||||||
|
draw()
|
||||||
|
})
|
||||||
|
let map = new Image();
|
||||||
|
map.onload = () => {
|
||||||
|
canvas.width = map.width;
|
||||||
|
canvas.height = map.height
|
||||||
|
draw()
|
||||||
|
|
||||||
|
canvas.addEventListener('contextmenu', ev => {
|
||||||
|
// right
|
||||||
|
ev.preventDefault();
|
||||||
|
stops.splice(i, 0, {
|
||||||
|
name: null,
|
||||||
|
x: ev.offsetX,
|
||||||
|
y: ev.offsetY
|
||||||
|
})
|
||||||
|
|
||||||
|
i = (i + 1).mod(stops.length)
|
||||||
|
draw();
|
||||||
|
return false;
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
canvas.addEventListener('click', ev => {
|
||||||
|
// left
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
stops[i].x = ev.offsetX
|
||||||
|
stops[i].y = ev.offsetY
|
||||||
|
|
||||||
|
|
||||||
|
i = (i + 1).mod(stops.length)
|
||||||
|
draw();
|
||||||
|
return false;
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
document.addEventListener('keydown', e => {
|
||||||
|
if (e.which === 38) {
|
||||||
|
// up
|
||||||
|
i = (i - 1).mod(stops.length)
|
||||||
|
draw()
|
||||||
|
e.preventDefault()
|
||||||
|
} else if (e.which === 40) {
|
||||||
|
// down
|
||||||
|
i = (i + 1).mod(stops.length)
|
||||||
|
draw()
|
||||||
|
e.preventDefault()
|
||||||
|
} else if (e.which === 13) {
|
||||||
|
// enter
|
||||||
|
let output = stops.map(s => `${s.x},${s.y}` + (s.name ? `,,${s.name}` : '')).join('\n')
|
||||||
|
|
||||||
|
console.log(output)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
map.src = "map.png";
|
||||||
|
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.drawImage(map, 0, 0)
|
||||||
|
tbl.getElementsByTagName('tbody')[0].innerHTML = "";
|
||||||
|
let last = null;
|
||||||
|
stops.forEach((s, index) => {
|
||||||
|
const tr = document.createElement('tr');
|
||||||
|
const tdName = document.createElement('td');
|
||||||
|
tdName.innerText = s.name ? s.name : '-';
|
||||||
|
const tdI = document.createElement('td');
|
||||||
|
if (index === i)
|
||||||
|
tdI.innerText = '<';
|
||||||
|
|
||||||
|
|
||||||
|
tr.append(tdName, tdI);
|
||||||
|
|
||||||
|
tbl.getElementsByTagName('tbody')[0].append(tr)
|
||||||
|
|
||||||
|
if (s.y === null && s.x === null)
|
||||||
|
return;
|
||||||
|
if (s.name !== null) {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.arc(s.x, s.y, 5, 0, 2 * Math.PI);
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle = index === i ? '#f00' : '#000'
|
||||||
|
ctx.stroke()
|
||||||
|
ctx.closePath()
|
||||||
|
ctx.fillStyle = '#000'
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.fillText(s.name, s.x, s.y - 10);
|
||||||
|
}
|
||||||
|
if (last) {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(last.x, last.y)
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.lineTo(s.x, s.y)
|
||||||
|
ctx.strokeStyle = '#000'
|
||||||
|
ctx.stroke()
|
||||||
|
ctx.closePath()
|
||||||
|
}
|
||||||
|
last = s;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||||
|
~ All rights reserved.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#buttons {
|
||||||
|
position: fixed;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings {
|
||||||
|
position: fixed;
|
||||||
|
margin: 8px;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bus {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
margin: 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="buttons">
|
||||||
|
<button id="minus">-1</button>
|
||||||
|
<button id="play">></button>
|
||||||
|
<button id="real">></button>
|
||||||
|
<button id="plus">+1</button>
|
||||||
|
<button disabled id="display" style="width: 70px">00:00</button>
|
||||||
|
</div>
|
||||||
|
<table id="bus">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 200px">Next Stop</th>
|
||||||
|
<th>Line</th>
|
||||||
|
<th>Offset</th>
|
||||||
|
<th>Delay</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div id="settings">
|
||||||
|
<label style="display: grid">
|
||||||
|
<input id="mpm" max="10000" min="1" type="number" value="180"> Minutes per Minute
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input id="showMap" type="checkbox"> Show Map?
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Generated
+10198
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "realtime-oepnv-tracker",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "webpack --config webpack/prod.js",
|
||||||
|
"start": "webpack-dev-server --config webpack/base.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.11.6",
|
||||||
|
"@babel/preset-env": "^7.11.5",
|
||||||
|
"babel-loader": "^8.1.0",
|
||||||
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
|
"csv-loader": "^3.0.3",
|
||||||
|
"favicons-webpack-plugin": "^4.2.0",
|
||||||
|
"file-loader": "^6.1.0",
|
||||||
|
"html-webpack-plugin": "^4.5.0",
|
||||||
|
"raw-loader": "^4.0.1",
|
||||||
|
"source-map": "^0.7.3",
|
||||||
|
"terser-webpack-plugin": "^4.2.2",
|
||||||
|
"ts-loader": "^8.0.4",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"webpack": "^4.44.2",
|
||||||
|
"webpack-cli": "^3.3.12",
|
||||||
|
"webpack-dev-server": "^3.11.0",
|
||||||
|
"webpack-merge": "^5.1.4",
|
||||||
|
"workbox-webpack-plugin": "^5.1.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Line from "./Line";
|
||||||
|
import Coordinate from "./Coordinate";
|
||||||
|
|
||||||
|
export default class Bus {
|
||||||
|
readonly offset: number;
|
||||||
|
readonly line: Line;
|
||||||
|
|
||||||
|
public delay: number = 0;
|
||||||
|
public td: HTMLTableDataCellElement;
|
||||||
|
|
||||||
|
constructor(line: Line, offset: number = 0) {
|
||||||
|
this.line = line;
|
||||||
|
this.offset = offset
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(ctx: CanvasRenderingContext2D, s: number) {
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const progress = (this.offset + s - this.delay).mod(this.line.duration);
|
||||||
|
|
||||||
|
|
||||||
|
let position = this.getCurrentPosition(progress);
|
||||||
|
ctx.beginPath()
|
||||||
|
if (this.delay === 0)
|
||||||
|
ctx.fillStyle = '#0f0'
|
||||||
|
else if (this.delay > 0 && this.delay < 5)
|
||||||
|
ctx.fillStyle = '#ff0'
|
||||||
|
else {
|
||||||
|
ctx.fillStyle = '#f00'
|
||||||
|
ctx.fillText("+" + this.delay, position.x, position.y - 3);
|
||||||
|
}
|
||||||
|
ctx.arc(position.x, position.y, 3, 0, 2 * Math.PI);
|
||||||
|
ctx.fill()
|
||||||
|
ctx.closePath()
|
||||||
|
|
||||||
|
if (this.td)
|
||||||
|
this.td.innerText = this.getNextStop(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNextStop(progress: number = 0): string {
|
||||||
|
let br = 0
|
||||||
|
for (; br < this.line.stopBreaks.length && progress > this.line.stopBreaks[br]; br++) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.line.stops[br].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCurrentPosition(progress: number = 0): Coordinate {
|
||||||
|
let br = 0
|
||||||
|
for (; br < this.line.breaks.length && progress > this.line.breaks[br]; br++) {
|
||||||
|
}
|
||||||
|
|
||||||
|
let top = this.line.coords[br];
|
||||||
|
// @ts-ignore
|
||||||
|
let bottomBr = (br - 1).mod(this.line.breaks.length);
|
||||||
|
let bottom = this.line.coords[bottomBr];
|
||||||
|
|
||||||
|
if (progress < this.line.breaks[0])
|
||||||
|
return this.line.coords[0];
|
||||||
|
if (!bottom) {
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
||||||
|
let percentToBr = (progress - this.line.breaks[bottomBr]) / (this.line.breaks[br] - this.line.breaks[bottomBr]);
|
||||||
|
return new Coordinate(
|
||||||
|
bottom.x + percentToBr * (top.x - bottom.x),
|
||||||
|
bottom.y + percentToBr * (top.y - bottom.y)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class Coordinate {
|
||||||
|
|
||||||
|
readonly x: number;
|
||||||
|
readonly y: number;
|
||||||
|
|
||||||
|
constructor(x: number, y: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
distance(other: Coordinate): number {
|
||||||
|
if (this.equals(other))
|
||||||
|
return 0;
|
||||||
|
if (!other)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return Math.sqrt(Math.pow(other.x - this.x, 2) + Math.pow(other.y - this.y, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
equals(other: Coordinate = null): boolean {
|
||||||
|
if (!other)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return this.x === other.x && this.y === other.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Stop from "./Stop";
|
||||||
|
import Coordinate from "./Coordinate";
|
||||||
|
|
||||||
|
export default class Line {
|
||||||
|
readonly name: String;
|
||||||
|
readonly stops: Stop[] = [];
|
||||||
|
|
||||||
|
readonly breaks: number[] = [];
|
||||||
|
readonly stopBreaks: number[] = [];
|
||||||
|
readonly duration: number = 0;
|
||||||
|
readonly coords: Coordinate[];
|
||||||
|
readonly color: string = '#000';
|
||||||
|
|
||||||
|
constructor(name: String, stops: string, color: string) {
|
||||||
|
this.color = color
|
||||||
|
this.name = name;
|
||||||
|
let coords = stops.split("\n").map(e => {
|
||||||
|
let a = e.split(',');
|
||||||
|
return a.length > 2 ? new Stop(Number.parseFloat(a[0]), Number.parseFloat(a[1]), Number.parseFloat(a[2]), a[3]) : new Coordinate(Number.parseFloat(a[0]), Number.parseFloat(a[1]));
|
||||||
|
});
|
||||||
|
this.coords = coords;
|
||||||
|
|
||||||
|
let lastStop = <Stop>coords[0];
|
||||||
|
let length = 0;
|
||||||
|
let breaks: number[] = [];
|
||||||
|
for (let i = 0; i < coords.length; i++) {
|
||||||
|
if (coords[i] instanceof Stop) {
|
||||||
|
let stop = <Stop>coords[i];
|
||||||
|
this.stops.push(stop)
|
||||||
|
this.stopBreaks.push(stop.minute)
|
||||||
|
|
||||||
|
const td = stop.minute - lastStop.minute
|
||||||
|
if (coords[i - 1])
|
||||||
|
length += coords[i - 1].distance(stop)
|
||||||
|
breaks.map(e => lastStop.minute + (e / length) * td).forEach(e => this.breaks.push(e));
|
||||||
|
|
||||||
|
|
||||||
|
this.breaks.push(stop.minute)
|
||||||
|
|
||||||
|
length = 0;
|
||||||
|
breaks = [];
|
||||||
|
|
||||||
|
lastStop = stop
|
||||||
|
} else {
|
||||||
|
let len = coords[i - 1].distance(coords[i]);
|
||||||
|
length += len;
|
||||||
|
breaks.push(length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.duration = lastStop.minute
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
|
let last: Coordinate = null;
|
||||||
|
this.coords.forEach(c => {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
if (c instanceof Stop) {
|
||||||
|
let stop: Stop = c;
|
||||||
|
ctx.arc(c.x, c.y, 5, 0, 2 * Math.PI);
|
||||||
|
ctx.strokeStyle = this.color
|
||||||
|
ctx.fillStyle = '#000'
|
||||||
|
ctx.fillText(stop.name, c.x, c.y - 10);
|
||||||
|
}
|
||||||
|
ctx.stroke()
|
||||||
|
ctx.closePath()
|
||||||
|
if (last) {
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(last.x, last.y)
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.lineTo(c.x, c.y)
|
||||||
|
ctx.strokeStyle = this.color
|
||||||
|
ctx.stroke()
|
||||||
|
ctx.closePath()
|
||||||
|
}
|
||||||
|
last = c;
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Coordinate from "./Coordinate";
|
||||||
|
|
||||||
|
export default class Stop extends Coordinate {
|
||||||
|
readonly name: string;
|
||||||
|
readonly minute: number;
|
||||||
|
|
||||||
|
constructor(x: number, y: number, minute: number, name: string) {
|
||||||
|
super(x, y);
|
||||||
|
this.minute = minute;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Number.prototype.mod = function (n) {
|
||||||
|
return ((this % n) + n) % n;
|
||||||
|
};
|
||||||
|
|
||||||
|
import mapSrc from "../map.png";
|
||||||
|
import Bus from "./Bus";
|
||||||
|
import Line from "./Line";
|
||||||
|
import stops201 from './lines/201.csv';
|
||||||
|
import stops204 from './lines/204.csv';
|
||||||
|
import stops205 from './lines/205.csv';
|
||||||
|
import stops206 from './lines/206.csv';
|
||||||
|
|
||||||
|
let line201 = new Line("201", stops201, '#fbce18');
|
||||||
|
let line204 = new Line("204", stops204, '#99207e');
|
||||||
|
let line205 = new Line("205", stops205, '#009fe3');
|
||||||
|
let line206 = new Line("206", stops206, '#1c60b2');
|
||||||
|
console.log(line201);
|
||||||
|
|
||||||
|
let canvas = document.getElementById("canvas");
|
||||||
|
let btnMinus = document.getElementById("minus");
|
||||||
|
let btnPlay = document.getElementById("play");
|
||||||
|
let btnReal = document.getElementById("real");
|
||||||
|
let btnPlus = document.getElementById("plus");
|
||||||
|
let btnDisplay = document.getElementById("display");
|
||||||
|
let tblBus = document.getElementById("bus");
|
||||||
|
let chkMap = document.getElementById("showMap");
|
||||||
|
let inpMpm = document.getElementById("mpm");
|
||||||
|
|
||||||
|
chkMap.addEventListener('change', () => run(0));
|
||||||
|
inpMpm.addEventListener('change', () => mpm = parseInt(inpMpm.value))
|
||||||
|
let ctx = canvas.getContext("2d");
|
||||||
|
let map = new Image();
|
||||||
|
map.onload = () => {
|
||||||
|
canvas.width = map.width;
|
||||||
|
canvas.height = map.height
|
||||||
|
run(0)
|
||||||
|
}
|
||||||
|
map.src = mapSrc;
|
||||||
|
|
||||||
|
let isRunning = false;
|
||||||
|
let realIsRunning = false;
|
||||||
|
let interval;
|
||||||
|
let progress = 0;
|
||||||
|
let mpm = parseInt(inpMpm.value);
|
||||||
|
|
||||||
|
let busses = [
|
||||||
|
new Bus(line201),
|
||||||
|
new Bus(line201, 30),
|
||||||
|
new Bus(line201, 60),
|
||||||
|
new Bus(line201, 90),
|
||||||
|
new Bus(line204),
|
||||||
|
new Bus(line204, 30),
|
||||||
|
new Bus(line204, 60),
|
||||||
|
new Bus(line204, 90),
|
||||||
|
new Bus(line205),
|
||||||
|
new Bus(line205, 30),
|
||||||
|
new Bus(line205, 60),
|
||||||
|
new Bus(line206),
|
||||||
|
new Bus(line206, 30),
|
||||||
|
new Bus(line206, 60),
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
busses.forEach(b => {
|
||||||
|
const tr = document.createElement('tr');
|
||||||
|
const tdNext = document.createElement('td');
|
||||||
|
b.td = tdNext;
|
||||||
|
const tdLine = document.createElement('td');
|
||||||
|
tdLine.innerText = b.line.name
|
||||||
|
const tdOffset = document.createElement('td');
|
||||||
|
tdOffset.innerText = b.offset.toString()
|
||||||
|
const tdDelay = document.createElement('td');
|
||||||
|
const inputDelay = document.createElement('input')
|
||||||
|
inputDelay.type = 'number'
|
||||||
|
inputDelay.min = '0'
|
||||||
|
inputDelay.max = '120'
|
||||||
|
inputDelay.value = b.delay.toString()
|
||||||
|
inputDelay.addEventListener('change', () => b.delay = parseInt(inputDelay.value))
|
||||||
|
tdDelay.append(inputDelay)
|
||||||
|
|
||||||
|
tr.append(tdNext, tdLine, tdOffset, tdDelay);
|
||||||
|
|
||||||
|
tblBus.getElementsByTagName('tbody')[0].append(tr)
|
||||||
|
})
|
||||||
|
|
||||||
|
function run(s) {
|
||||||
|
if (s instanceof Date) {
|
||||||
|
const hours = s.getHours()
|
||||||
|
const minutes = s.getMinutes();
|
||||||
|
const seconds = s.getSeconds();
|
||||||
|
|
||||||
|
progress = (60 * hours) + minutes + (seconds / 60);
|
||||||
|
} else
|
||||||
|
progress += s;
|
||||||
|
const mprogress = progress.mod(60);
|
||||||
|
const hours = Math.floor(progress / 60);
|
||||||
|
const minutes = Math.floor(mprogress);
|
||||||
|
const seconds = Math.floor((mprogress * 60).mod(60))
|
||||||
|
btnDisplay.innerText = (hours > 9 ? hours : "0" + hours) + ":" + (minutes > 9 ? minutes : "0" + minutes) + ":" + (seconds > 9 ? seconds : "0" + seconds)
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
if (chkMap.checked)
|
||||||
|
ctx.drawImage(map, 0, 0)
|
||||||
|
else {
|
||||||
|
line201.draw(ctx)
|
||||||
|
line204.draw(ctx)
|
||||||
|
line205.draw(ctx)
|
||||||
|
line206.draw(ctx)
|
||||||
|
}
|
||||||
|
busses.forEach(b => b.draw(ctx, progress))
|
||||||
|
}
|
||||||
|
|
||||||
|
btnMinus.addEventListener('click', () => {
|
||||||
|
run(-1);
|
||||||
|
})
|
||||||
|
|
||||||
|
btnPlus.addEventListener('click', () => {
|
||||||
|
run(1);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
btnPlay.addEventListener('click', () => {
|
||||||
|
if (realIsRunning) {
|
||||||
|
clearInterval(interval)
|
||||||
|
btnReal.innerText = '>'
|
||||||
|
realIsRunning = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRunning) {
|
||||||
|
clearInterval(interval)
|
||||||
|
btnPlay.innerText = '>'
|
||||||
|
} else {
|
||||||
|
interval = setInterval(() => {
|
||||||
|
run(mpm / 600);
|
||||||
|
}, 100)
|
||||||
|
btnPlay.innerText = '||'
|
||||||
|
}
|
||||||
|
|
||||||
|
isRunning = !isRunning;
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
btnReal.addEventListener('click', () => {
|
||||||
|
if (isRunning) {
|
||||||
|
clearInterval(interval)
|
||||||
|
btnPlay.innerText = '>'
|
||||||
|
isRunning = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realIsRunning) {
|
||||||
|
clearInterval(interval)
|
||||||
|
btnReal.innerText = '>'
|
||||||
|
} else {
|
||||||
|
interval = setInterval(() => {
|
||||||
|
run(new Date());
|
||||||
|
}, 100)
|
||||||
|
btnReal.innerText = '||'
|
||||||
|
}
|
||||||
|
|
||||||
|
realIsRunning = !realIsRunning;
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
navigator.serviceWorker.register('./service-worker.js').catch(() => {
|
||||||
|
}).then();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
1567,455,11,Roland-Center
|
||||||
|
1553,448
|
||||||
|
1558,431
|
||||||
|
1559,408,12,Obervielander Str.
|
||||||
|
1569,391
|
||||||
|
1566,364
|
||||||
|
1553,351,13,Carl-Hurtzig-Str.
|
||||||
|
1517,303,14,Zum Huchtinger Bhf.
|
||||||
|
1455,310,15,Harriersand
|
||||||
|
1386,325,16,Mittelshuchtinger Dorfstr.
|
||||||
|
1337,332,16,Brüsseler Str.
|
||||||
|
1261,335,17,Varrelgraben
|
||||||
|
1135,338
|
||||||
|
1099,332,19,Kleiner Tannenweg
|
||||||
|
1023,324,20,Bahnhof Heidkrug
|
||||||
|
972,346,21,Sommerweg
|
||||||
|
927,370,22,Masurenstr.
|
||||||
|
892,386,22,Tannenbergstr.
|
||||||
|
845,383,23,Feldstr.
|
||||||
|
794,380,23,Hamburger Weg
|
||||||
|
731,379,24,Am Fuhrenkamp
|
||||||
|
693,382,25,Syker Str.
|
||||||
|
685,372
|
||||||
|
682,349
|
||||||
|
663,339
|
||||||
|
652,341,27,Otto-Jenzok-Str.
|
||||||
|
638,345
|
||||||
|
640,358
|
||||||
|
624,359
|
||||||
|
627,367,28,Museum
|
||||||
|
529,410,33,Bahnhof / ZOB
|
||||||
|
529,410,39,Bahnhof / ZOB
|
||||||
|
501,431,40,Post
|
||||||
|
503,443,43,Markt
|
||||||
|
553,491,45,Parkstr.
|
||||||
|
581,492,47,Arthur-Fitger-Str.
|
||||||
|
612,507,48,Uferweg
|
||||||
|
658,574,49,Karlsbader Str.
|
||||||
|
690,617,50,Amundsenstr.
|
||||||
|
725,662,51,Schollendamm
|
||||||
|
762,715,52,Riedeweg
|
||||||
|
793,746,53,Helgolandstr.
|
||||||
|
766,777,53,Nachtigalstr.
|
||||||
|
739,808,54,Wissmannstr.
|
||||||
|
724,819,55,Bahnhof Annenheide
|
||||||
|
703,864,56,Steller Str.
|
||||||
|
689,915,57,Annenheide
|
||||||
|
689,915,60,Annenheide
|
||||||
|
714,947
|
||||||
|
742,920,61,Feldhusstr.
|
||||||
|
748,907
|
||||||
|
703,864,62,Steller Str.
|
||||||
|
724,819,63,Bahnhof Annenheide
|
||||||
|
739,808,64,Wissmannstr.
|
||||||
|
793,746,65,Helgolandstr.
|
||||||
|
762,715,66,Riedeweg
|
||||||
|
725,662,67,Schollendamm
|
||||||
|
690,617,68,Amundsenstr.
|
||||||
|
658,574,69,Karlsbader Str.
|
||||||
|
612,507,70,Uferweg
|
||||||
|
581,492,71,Arthur-Fitger-Str.
|
||||||
|
553,491,73,Parkstr.
|
||||||
|
503,443,74,Markt
|
||||||
|
501,431,75,Post
|
||||||
|
529,410,78,Bahnhof / ZOB
|
||||||
|
529,410,84,Bahnhof / ZOB
|
||||||
|
627,367,87,Museum
|
||||||
|
624,359
|
||||||
|
640,358
|
||||||
|
638,345
|
||||||
|
652,341,88,Otto-Jenzok-Str.
|
||||||
|
663,339
|
||||||
|
682,349
|
||||||
|
685,372
|
||||||
|
693,382,89,Syker Str.
|
||||||
|
731,379,91,Am Fuhrenkamp
|
||||||
|
794,380,91,Hamburger Weg
|
||||||
|
845,383,92,Feldstr.
|
||||||
|
892,386,93,Tannenbergstr.
|
||||||
|
927,370,94,Masurenstr.
|
||||||
|
972,346,95,Sommerweg
|
||||||
|
1023,324,96,Bahnhof Heidkrug
|
||||||
|
1099,332,97,Kleiner Tannenweg
|
||||||
|
1135,338
|
||||||
|
1261,335,98,Varrelgraben
|
||||||
|
1337,332,99,Brüsseler Str.
|
||||||
|
1386,325,100,Mittelshuchtinger Dorfstr.
|
||||||
|
1455,310,101,Harriersand
|
||||||
|
1517,303,101,Zum Huchtinger Bhf.
|
||||||
|
1553,351,102,Carl-Hurtzig-Str.
|
||||||
|
1566,364
|
||||||
|
1569,391
|
||||||
|
1559,408,103,Obervielander Str.
|
||||||
|
1558,431
|
||||||
|
1553,448
|
||||||
|
1567,455,106,Roland-Center
|
||||||
|
1567,455,120,Roland-Center
|
||||||
|
@@ -0,0 +1,81 @@
|
|||||||
|
1567,455,23,Roland-Center
|
||||||
|
1542,478,24,Willakedamm
|
||||||
|
1525,509,24,Hohenhorster Weg
|
||||||
|
1473,575,25,Rotterdamer Str.
|
||||||
|
1457,600,26,Friedhof Huchting
|
||||||
|
1441,637,28,Varreler Landstr.
|
||||||
|
1419,631,28,Henstedter Str.
|
||||||
|
1341,606,29,Meenheit
|
||||||
|
1284,642,30,Grüne Str.
|
||||||
|
1188,682,32,Schulstr.
|
||||||
|
1106,675,33,Johann-Eilers-Weg
|
||||||
|
1040,661,34,Mühle Unger
|
||||||
|
992,597,35,Pultern
|
||||||
|
922,513,37,Langenwischstr.
|
||||||
|
882,488,38,An der Riede
|
||||||
|
826,457,39,Kieler Weg
|
||||||
|
784,437,40,Lüneburger Str.
|
||||||
|
759,422,41,Leipziger Weg
|
||||||
|
728,405,41,Pestalozziweg
|
||||||
|
694,384,43,Syker Str.
|
||||||
|
623,399,44,Heinrichstr.
|
||||||
|
530,409,48,Bahnhof / ZOB
|
||||||
|
530,409,54,Bahnhof / ZOB
|
||||||
|
506,429,55,Post
|
||||||
|
506,441,58,Markt
|
||||||
|
555,489,60,Parkstr.
|
||||||
|
582,536,62,Max-Planck-Str.
|
||||||
|
588,572,63,Breslauer Str.
|
||||||
|
540,617,64,Lethestr.
|
||||||
|
506,641,64,Elbinger Str.
|
||||||
|
467,696,65,Brendelweg
|
||||||
|
495,718,66,Ziethenweg
|
||||||
|
474,755,66,Graf-von-Galen-Str.
|
||||||
|
447,800,67,Könzgenstr.
|
||||||
|
428,825,68,Letterhausstr.
|
||||||
|
376,830,69,Schlutterdamm
|
||||||
|
327,937,70,Holzkamper Damm
|
||||||
|
332,1036,71,Abernettistr.
|
||||||
|
293,1055,72,Boelckestr.
|
||||||
|
254,1075,73,Adelheide Kaserne
|
||||||
|
254,1075,74,Adelheide Kaserne
|
||||||
|
293,1055,75,Boelckestr.
|
||||||
|
332,1036,76,Abernettistr.
|
||||||
|
327,937,77,Holzkamper Damm
|
||||||
|
376,830,78,Schlutterdamm
|
||||||
|
428,825,79,Letterhausstr.
|
||||||
|
447,800,80,Könzgenstr.
|
||||||
|
474,755,81,Graf-von-Galen-Str.
|
||||||
|
495,718,81,Ziethenweg
|
||||||
|
467,696,83,Brendelweg
|
||||||
|
506,641,83,Elbinger Str.
|
||||||
|
540,617,84,Lethestr.
|
||||||
|
588,572,85,Breslauer Str.
|
||||||
|
582,536,87,Max-Planck-Str.
|
||||||
|
555,489,88,Parkstr.
|
||||||
|
506,441,89,Markt
|
||||||
|
506,429,90,Post
|
||||||
|
530,409,93,Bahnhof / ZOB
|
||||||
|
530,409,99,Bahnhof / ZOB
|
||||||
|
623,399,101,Heinrichstr.
|
||||||
|
694,384,102,Syker Str.
|
||||||
|
728,405,103,Pestalozziweg
|
||||||
|
759,422,104,Leipziger Weg
|
||||||
|
784,437,105,Lüneburger Str.
|
||||||
|
826,457,106,Kieler Weg
|
||||||
|
882,488,106,An der Riede
|
||||||
|
922,513,107,Langenwischstr.
|
||||||
|
992,597,109,Pultern
|
||||||
|
1040,661,110,Mühle Unger
|
||||||
|
1106,675,111,Johann-Eilers-Weg
|
||||||
|
1188,682,112,Schulstr.
|
||||||
|
1284,642,114,Grüne Str.
|
||||||
|
1341,606,115,Meenheit
|
||||||
|
1419,631,116,Henstedter Str.
|
||||||
|
1441,637,117,Varreler Landstr.
|
||||||
|
1457,600,118,Friedhof Huchting
|
||||||
|
1473,575,119,Rotterdamer Str.
|
||||||
|
1525,509,120,Hohenhorster Weg
|
||||||
|
1542,478,121,Willakedamm
|
||||||
|
1567,455,124,Roland-Center
|
||||||
|
1567,455,150,Roland-Center
|
||||||
|
@@ -0,0 +1,59 @@
|
|||||||
|
515,881,1,Gothaer Str.
|
||||||
|
467,864,1,Dresdener Str.
|
||||||
|
475,826,2,Blücherweg
|
||||||
|
498,787,3,Jenaer Str.
|
||||||
|
529,750,4,Overbergstr.
|
||||||
|
553,770,5,Jägerstr.
|
||||||
|
588,717,6,Danziger Str.
|
||||||
|
583,682,7,Görlitzer Str.
|
||||||
|
576,654,8,Liegnitzer Str.
|
||||||
|
567,628,9,Königsberger Str.
|
||||||
|
583,571,10,Breslauer Str.
|
||||||
|
578,536,12,Max-Planck-Str.
|
||||||
|
548,492,13,Parkstr.
|
||||||
|
499,446,14,Markt
|
||||||
|
498,432,15,Post
|
||||||
|
529,401,18,Bahnhof / ZOB
|
||||||
|
529,401,24,Bahnhof / ZOB
|
||||||
|
564,357,26,Am Wollepark
|
||||||
|
558,318,27,Thüringer Str.
|
||||||
|
546,260,27,Schönemoorer Str.
|
||||||
|
555,227,28,Hansastr.
|
||||||
|
574,187,29,Nordstr.
|
||||||
|
532,190,30,Lessingstr.
|
||||||
|
526,140,31,Andersenstr.
|
||||||
|
532,106,31,Kleistweg
|
||||||
|
543,78,32,Gorch-Fock-Str.
|
||||||
|
590,83,33,Friedhof
|
||||||
|
644,75,33,Brauereiweg
|
||||||
|
658,47,34,Bungerhof
|
||||||
|
658,47,50,Bungerhof
|
||||||
|
644,75,51,Brauereiweg
|
||||||
|
590,83,52,Friedhof
|
||||||
|
543,78,53,Gorch-Fock-Str.
|
||||||
|
532,106,53,Kleistweg
|
||||||
|
526,140,54,Andersenstr.
|
||||||
|
532,190,55,Lessingstr.
|
||||||
|
574,187,56,Nordstr.
|
||||||
|
555,227,57,Hansastr.
|
||||||
|
546,260,58,Schönemoorer Str.
|
||||||
|
558,318,59,Thüringer Str.
|
||||||
|
564,357,60,Am Wollepark
|
||||||
|
529,401,63,Bahnhof / ZOB
|
||||||
|
529,401,69,Bahnhof / ZOB
|
||||||
|
498,432,70,Post
|
||||||
|
499,446,73,Markt
|
||||||
|
548,492,75,Parkstr.
|
||||||
|
578,536,77,Max-Planck-Str.
|
||||||
|
583,571,78,Breslauer Str.
|
||||||
|
567,628,79,Königsberger Str.
|
||||||
|
576,654,80,Liegnitzer Str.
|
||||||
|
583,682,81,Görlitzer Str.
|
||||||
|
588,717,81,Danziger Str.
|
||||||
|
553,770,82,Jägerstr.
|
||||||
|
529,750,83,Overbergstr.
|
||||||
|
498,787,84,Jenaer Str.
|
||||||
|
525,828,86,Chemnitzer Str.
|
||||||
|
539,875,86,Altenburger Str.
|
||||||
|
515,881,88,Gothaer Str.
|
||||||
|
515,881,90,Gothaer Str.
|
||||||
|
@@ -0,0 +1,71 @@
|
|||||||
|
658,47,5,Bungerhof
|
||||||
|
644,75,6,Brauereiweg
|
||||||
|
636,85
|
||||||
|
590,83,7,Friedhof
|
||||||
|
543,78,8,Gorch-Fock-Str.
|
||||||
|
498,75,8,Hebbelweg
|
||||||
|
468,68
|
||||||
|
463,75,9,Friedensstr.
|
||||||
|
470,134,10,Scheffelstr.
|
||||||
|
480,198,11,Dwostr.
|
||||||
|
482,216
|
||||||
|
518,239,12,Rilkeweg
|
||||||
|
546,260,13,Schönemoorer Str.
|
||||||
|
554,319,14,Thüringer Str.
|
||||||
|
560,359,15,Am Wollepark
|
||||||
|
566,386
|
||||||
|
563,392
|
||||||
|
548,396
|
||||||
|
527,398,18,Bahnhof / ZOB J
|
||||||
|
527,398,24,Bahnhof / ZOB J
|
||||||
|
509,402
|
||||||
|
493,415
|
||||||
|
494,432,25,Post
|
||||||
|
496,445,28,Markt A
|
||||||
|
474,456
|
||||||
|
450,463
|
||||||
|
442,471,30,Holbeinstr./Graft Therme
|
||||||
|
412,533
|
||||||
|
411,530,32,Berufsschule
|
||||||
|
362,505,33,Feuerbachstr.
|
||||||
|
336,491,34,Schumannstr.
|
||||||
|
299,519,35,Brahmsstr.
|
||||||
|
242,520,36,Mozartstr.
|
||||||
|
220,514,37,Josef-Hospital
|
||||||
|
199,569,38,Hanse-Wissenschaftsk.
|
||||||
|
199,569,48,Hanse-Wissenschaftsk.
|
||||||
|
220,514,49,Josef-Hospital
|
||||||
|
242,520,50,Mozartstr.
|
||||||
|
299,519,51,Brahmsstr.
|
||||||
|
336,491,52,Schumannstr.
|
||||||
|
362,505,53,Feuerbachstr.
|
||||||
|
411,530,54,Berufsschule
|
||||||
|
412,533
|
||||||
|
442,471,55,Holbeinstr./Graft Therme
|
||||||
|
450,463
|
||||||
|
474,456
|
||||||
|
496,445,59,Markt C1
|
||||||
|
494,432,60,Post
|
||||||
|
493,415
|
||||||
|
509,402
|
||||||
|
527,398,63,Bahnhof / ZOB D
|
||||||
|
527,398,69,Bahnhof / ZOB D
|
||||||
|
548,396
|
||||||
|
563,392
|
||||||
|
566,386
|
||||||
|
560,359,71,Am Wollepark
|
||||||
|
554,319,72,Thüringer Str.
|
||||||
|
546,260,73,Schönemoorer Str.
|
||||||
|
518,239,74,Rilkeweg
|
||||||
|
482,216
|
||||||
|
480,198,74,Dwostr.
|
||||||
|
470,134,75,Scheffelstr.
|
||||||
|
463,75,76,Friedensstr.
|
||||||
|
468,68
|
||||||
|
498,75,77,Hebbelweg
|
||||||
|
543,78,77,Gorch-Fock-Str.
|
||||||
|
590,83,78,Friedhof
|
||||||
|
636,85
|
||||||
|
644,75,78,Brauereiweg
|
||||||
|
658,47,79,Bungerhof
|
||||||
|
658,47,90,Bungerhof
|
||||||
|
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"module": "es6",
|
||||||
|
"target": "es5",
|
||||||
|
"jsx": "react",
|
||||||
|
"allowJs": true,
|
||||||
|
"moduleResolution": "node"
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const webpack = require("webpack");
|
||||||
|
const path = require("path");
|
||||||
|
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
|
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: "development",
|
||||||
|
devtool: "eval-source-map",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: "babel-loader"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(ogg|mp3|wav|mpe?g|gif|png|jpe?g|svg|xml)$/i,
|
||||||
|
use: "file-loader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.csv$/i,
|
||||||
|
use: 'raw-loader',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CleanWebpackPlugin({
|
||||||
|
root: path.resolve(__dirname, "../")
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./index.html"
|
||||||
|
})
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>.
|
||||||
|
* All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const {merge} = require('webpack-merge');
|
||||||
|
const path = require("path");
|
||||||
|
const base = require("./base");
|
||||||
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
|
const WorkboxPlugin = require('workbox-webpack-plugin');
|
||||||
|
const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = merge(base, {
|
||||||
|
mode: "production",
|
||||||
|
output: {
|
||||||
|
filename: "bundle.min.js"
|
||||||
|
},
|
||||||
|
devtool: false,
|
||||||
|
performance: {
|
||||||
|
maxEntrypointSize: 900000,
|
||||||
|
maxAssetSize: 900000
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimizer: [
|
||||||
|
new TerserPlugin({
|
||||||
|
terserOptions: {
|
||||||
|
output: {
|
||||||
|
comments: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new WorkboxPlugin.GenerateSW({
|
||||||
|
clientsClaim: true,
|
||||||
|
skipWaiting: true,
|
||||||
|
}),
|
||||||
|
new FaviconsWebpackPlugin({
|
||||||
|
logo: './map.png',
|
||||||
|
publicPath: "./",
|
||||||
|
favicons: {
|
||||||
|
manifestRelativePaths: true,
|
||||||
|
start_url: "../",
|
||||||
|
appName: 'RT Delbus',
|
||||||
|
appDescription: 'Real-Time Delbus',
|
||||||
|
appShortName: "rt-delbus",
|
||||||
|
developerName: 'Syma.Dev',
|
||||||
|
developerURL: "https://syma.dev",
|
||||||
|
background: '#000',
|
||||||
|
theme_color: '#333',
|
||||||
|
logging: false,
|
||||||
|
icons: {
|
||||||
|
android: true,
|
||||||
|
appleIcon: true,
|
||||||
|
appleStartup: false,
|
||||||
|
coast: false,
|
||||||
|
favicons: true,
|
||||||
|
firefox: true,
|
||||||
|
windows: true,
|
||||||
|
yandex: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user