From 80cbd705fe2c6f70c61d2acb0b0251580b1ac2e3 Mon Sep 17 00:00:00 2001 From: Lukas Winkler Date: Mon, 15 Aug 2022 15:54:18 +0200 Subject: [PATCH] allow multiple areas --- assets/favicons/derstandard.at.png | Bin 0 -> 481 bytes commands/fetch.ts | 14 +++-- commands/stats.ts | 4 +- custom.d.ts | 2 + data/Oberösterreich.json | 31 ++++++++++ data/{data.json => Wien.json} | 0 data/schema.json | 2 +- interfaces.ts | 8 +-- missing.md | 6 ++ package-lock.json | 11 ++++ package.json | 3 +- src/areaData.ts | 41 ++++++++++++ src/features.ts | 46 ++++++++++++++ src/main.ts | 3 + src/map.ts | 96 ++++++++++++----------------- src/popups.ts | 3 +- src/router.ts | 10 +++ tsconfig.json | 4 +- utils/geo.ts | 7 +++ 19 files changed, 221 insertions(+), 70 deletions(-) create mode 100644 assets/favicons/derstandard.at.png create mode 100644 data/Oberösterreich.json rename data/{data.json => Wien.json} (100%) create mode 100644 src/areaData.ts create mode 100644 src/features.ts create mode 100644 src/main.ts create mode 100644 src/router.ts diff --git a/assets/favicons/derstandard.at.png b/assets/favicons/derstandard.at.png new file mode 100644 index 0000000000000000000000000000000000000000..17bc14d06dff851fcf1a7f206a54edb354048ddf GIT binary patch literal 481 zcmV<70UrK|P);R1a z|Nr{3-1)8A`@rMyH>CX0>+A@R_JGF!{r>;<{O~fO_m9o-MXT-{nDuqR^l!fLNv-V; zlK%4f{p9og-SGF6&-RAM_Jzpxe8lrzw)v^q^k%y9QnBwRo&N3j{_OVt==J>A?)=v5 z_@LDInbGh*sPH?d?G}~o6qWqX>H5Fo_KC{xDxdBooNYj*5&!@J4@pEpR5;7U(${W- zFaU+&bBI&Q9tkTz_Gr4^|M5y}43SL5O{IPk5`rItZSYrYZxbbNzu)#bxZcm}{D+dkMb!Z*1W;z)8O#EAd`(n4=tbp0nf z2g=t4-EkvXhbm~dxH|HKUdW}Mq)XEE^(urphM-sS!7c7ki~znP6X$$4g5L5-X%!iP z7Sw8#(hRgvZio|_!I)SiV>9q`Xcw|DgFG9@AS*M7$3358lr2YL&7^zv{J@KK<2Ib;UoiW`HxWn1MMz XkGKuAXYW9H00000NkvXXu0mjfED-t! literal 0 HcmV?d00001 diff --git a/commands/fetch.ts b/commands/fetch.ts index ecac0df..c616408 100644 --- a/commands/fetch.ts +++ b/commands/fetch.ts @@ -3,8 +3,8 @@ import type {Crossing, OSMNodeSource, OSMWaySource, OverPassNode, OverPassRespon import {nodeData, wayData} from "./overpass"; import {lineLengthInM} from "../utils/geo"; -async function runfetch() { - const data: Crossing[] = JSON.parse(fs.readFileSync("../data/data.json", 'utf8')); +async function runfetch(filename: string) { + const data: Crossing[] = JSON.parse(fs.readFileSync(filename, 'utf8')); function firstLastNodeCoords(nodeMap: { [id: number]: OverPassNode }, nodeList: number[]) { const coords: number[][] = [] @@ -75,8 +75,14 @@ async function runfetch() { length: lineLengthInM(coords[0], coords[1]) } // crossings[i] = d - fs.writeFileSync("../data/data.json", JSON.stringify(data, null, 2)) + fs.writeFileSync(filename, JSON.stringify(data, null, 2)) } } -runfetch() +fs.readdirSync("../data/").forEach(file => { + if (file === "schema.json") { + return + } + console.info(file) + runfetch("../data/" + file) +}) diff --git a/commands/stats.ts b/commands/stats.ts index c0f1e84..b7377f2 100644 --- a/commands/stats.ts +++ b/commands/stats.ts @@ -2,10 +2,10 @@ import * as fs from "fs"; import type {Crossing} from "../interfaces"; function runstats() { - const data: Crossing[] = JSON.parse(fs.readFileSync("../data/data.json", 'utf8')); + const data: Crossing[] = JSON.parse(fs.readFileSync("../data/Wien.json", 'utf8')); - const bezirke = data.map(c => c.bezirk) + const bezirke = data.map(c => c.bezirk!) const counts: { [key: number]: number } = {}; for (const num of bezirke) { diff --git a/custom.d.ts b/custom.d.ts index 05642bf..449cca0 100644 --- a/custom.d.ts +++ b/custom.d.ts @@ -1,3 +1,5 @@ +import "vite/client" + declare module "*.svg" { const content: string; export default content; diff --git a/data/Oberösterreich.json b/data/Oberösterreich.json new file mode 100644 index 0000000..9206687 --- /dev/null +++ b/data/Oberösterreich.json @@ -0,0 +1,31 @@ +[ + { + "id": 1, + "name": "Nordico", + "type": "prideFlag", + "sources": [ + { + "type": "official", + "url": "https://www.linz.at/medienservice/2022/202206_115655.php", + "date": "2022-06-22" + } + ], + "geosource": { + "type": "OSMway", + "wayID": 140435401 + }, + "geo": { + "coords": [ + [ + 14.2911588, + 48.3037437 + ], + [ + 14.2911232, + 48.3038253 + ] + ], + "length": 9.45 + } + } +] diff --git a/data/data.json b/data/Wien.json similarity index 100% rename from data/data.json rename to data/Wien.json diff --git a/data/schema.json b/data/schema.json index 247234e..7f60c17 100644 --- a/data/schema.json +++ b/data/schema.json @@ -68,6 +68,7 @@ "news", "official", "proposal", + "photo", "streetview", "in person" ] @@ -153,7 +154,6 @@ }, "required": [ "id", - "bezirk", "name", "type", "sources", diff --git a/interfaces.ts b/interfaces.ts index 606f260..8c183c9 100644 --- a/interfaces.ts +++ b/interfaces.ts @@ -19,7 +19,7 @@ export interface OSMNodeSource { } export interface Source { - type: "news" | "official" | "proposal" | "streetview" | "in person" + type: "news" | "official" | "proposal" | "photo" | "streetview" | "in person" date: string url?: string } @@ -32,9 +32,9 @@ export interface GeoData { export interface Crossing { id: number name: string - bezirk: number - comment?:string - set?:string + bezirk?: number + comment?: string + set?: string type: FlagType sources: Source[] geosource: OSMWaySource | OSMNodeSource | RawCoordSource, diff --git a/missing.md b/missing.md index 4bb42a7..1a8ffca 100644 --- a/missing.md +++ b/missing.md @@ -5,3 +5,9 @@ Mariahilfer Straße Unbekannter Trans-Pride-Streifen in Rudolfsheim-Fünfhaus https://www.wien.gv.at/bezirke/rudolfsheim-fuenfhaus/politik/sitzungen/pdf/20210701-antrag-pride-schutzweg.pdf + + +Linz: +https://www.weekend.at/bundesland/oberoesterreich/neue-regenbogen-zebrastreifen-linz +https://www.linzwiki.at/wiki/Datei:Regenbogen-Zebrastreifen_Mozartkreuzung.jpg/ +https://www.linz.at/medienservice/2022/202206_115655.php diff --git a/package-lock.json b/package-lock.json index 2d3e1eb..08fa864 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "axios": "^0.27.2", "hint.css": "^2.7.0", "micromodal": "^0.4.10", + "navigo": "^8.11.1", "node-fetch": "^3.2.10", "ol": "^6.14.1", "ts-node": "^10.9.1" @@ -1132,6 +1133,11 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/navigo": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/navigo/-/navigo-8.11.1.tgz", + "integrity": "sha512-e3sc1UzakF+bWquC8/dbPCgo7LgPEW1ekgwb4pmEcl8tOc/I7lML8r7HBql+b0VRk7tJWTZqtkeObwJAVR1pxg==" + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -2320,6 +2326,11 @@ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", "dev": true }, + "navigo": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/navigo/-/navigo-8.11.1.tgz", + "integrity": "sha512-e3sc1UzakF+bWquC8/dbPCgo7LgPEW1ekgwb4pmEcl8tOc/I7lML8r7HBql+b0VRk7tJWTZqtkeObwJAVR1pxg==" + }, "node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", diff --git a/package.json b/package.json index 55f98af..90f7983 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "dev": "vite", "build": "tsc --skipLibCheck && vite build", "preview": "vite preview", - "validate": "ajv validate -s data/schema.json -d data/data.json" + "validate": "ajv validate -s data/schema.json -d data/Wien.json" }, "devDependencies": { "@types/micromodal": "^0.3.3", @@ -22,6 +22,7 @@ "axios": "^0.27.2", "hint.css": "^2.7.0", "micromodal": "^0.4.10", + "navigo": "^8.11.1", "node-fetch": "^3.2.10", "ol": "^6.14.1", "ts-node": "^10.9.1" diff --git a/src/areaData.ts b/src/areaData.ts new file mode 100644 index 0000000..fa1d9f1 --- /dev/null +++ b/src/areaData.ts @@ -0,0 +1,41 @@ +import {Coordinate} from "ol/coordinate"; +import {Extent} from "ol/extent"; +import {View} from "ol"; +import {fromLonLat, transformExtent} from "ol/proj"; + +interface Area { + name: string + center: Coordinate + extent: Extent + zoom: number +} + + +export const areas: { [name: string]: Area } = { + Wien: { + name: "Wien", + zoom: 13, + center: [16.3787, 48.2089], + // https://www.wien.gv.at/statistik/lebensraum/tabellen/stadtgebiet-eckdaten.html + extent: [16.18278, 48.11833, 16.58, 48.32306] + }, + OOE: { + name: "Oberösterreich", + zoom: 9, + // https://doris.ooe.gv.at/service/pdf/mittelpunkte/4.pdf + center: [13.964417, 48.136583], + // https://www.deine-berge.de/Region/Oesterreich/8/Bundesland-Oberoesterreich.html + extent: [12.749244, 47.461112, 14.9921682, 48.7725637] + } +} + +export const Wien = areas.Wien + +export function viewFromArea(area: Area): View { + return new View({ + center: fromLonLat(area.center), + zoom: area.zoom, + extent: transformExtent(area.extent, 'EPSG:4326', 'EPSG:3857'), + constrainOnlyCenter: true + }) +} diff --git a/src/features.ts b/src/features.ts new file mode 100644 index 0000000..d6a2208 --- /dev/null +++ b/src/features.ts @@ -0,0 +1,46 @@ +import {Crossing} from "../interfaces"; +import {Vector as VectorSource} from "ol/source"; +import {transform} from "ol/proj"; +import {Feature} from "ol"; +import {LineString, Point} from "ol/geom"; +import {averageCoords} from "../utils/geo"; + +export type MetaData = { [id: number]: Crossing } + +export function loadData(data: Crossing[], vectorSource: VectorSource): MetaData { + const metaData: MetaData = {} + console.info(data) + data.sort((a, b) => { + /* + put trans flag on top (so they are not covered, + but apart from that keep the drawing order random + */ + if (a.type == "transFlag") { + return 1 + } + if (b.type == "transFlag") { + return -1 + } + return Math.random() - 0.5; + }) + data.forEach(c => { + if (typeof c.geo === "undefined") { + return + } + const points = c.geo.coords.map(coord => transform(coord, 'EPSG:4326', 'EPSG:3857')); + + const featureLine = new Feature({ + geometry: new LineString(points) + }); + const featureDot = new Feature({ + geometry: new Point(averageCoords(points)) + }); + featureLine.setId(c.id) + featureDot.setId(c.id + 10000) + metaData[c.id] = c + vectorSource.addFeature(featureLine); + vectorSource.addFeature(featureDot); + }) + return metaData + +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..fef962a --- /dev/null +++ b/src/main.ts @@ -0,0 +1,3 @@ +import "./style.scss" + +import "./map" diff --git a/src/map.ts b/src/map.ts index 6c55bec..f6ac067 100644 --- a/src/map.ts +++ b/src/map.ts @@ -1,10 +1,6 @@ import Map from "ol/Map"; import {OSM, Vector as VectorSource} from "ol/source"; import TileLayer from "ol/layer/Tile"; -import {Feature, View} from "ol"; -import {fromLonLat, transform, transformExtent} from "ol/proj"; - -import {LineString, Point} from "ol/geom"; import {Vector as VectorLayer} from "ol/layer"; import {Circle, Fill, Icon, Stroke, Style} from "ol/style"; import {Coordinate} from "ol/coordinate"; @@ -12,21 +8,20 @@ import {State} from "ol/render"; import {Line, Vector2d} from "./vectorUtils"; import {drawZebraCrossing, zebraPatterns} from "./zebraUtils"; // @ts-ignore -import importdata from "../data/data.json?inline" -// @ts-ignore -import dataURL from "../data/data.json?url" -import {Crossing} from "../interfaces"; +import dataURL from "../data/Wien.json?url" import prideFlag from "../assets/prideflag.svg" import transFlag from "../assets/transflag.svg" -import {averageCoords} from "../utils/geo"; +import {loadData, MetaData} from "./features"; +// @ts-ignore +import importdata from "../data/Wien.json?inline" +import Navigo from "navigo"; +import {redirect} from "./router"; +import {areas, viewFromArea, Wien} from "./areaData"; +import {Crossing} from "../interfaces"; -import "./style.scss" -import "hint.css/hint.base.css" +const root = import.meta.env.PROD ? "/s/tmp/rainbowroad/" : "/" +export const router = new Navigo(root) - - - -const data = importdata as Crossing[] const map = new Map({ target: 'map', layers: [ @@ -34,51 +29,30 @@ const map = new Map({ source: new OSM({url: "https://maps.lw1.at/tiles/1.0.0/osm/GLOBAL_MERCATOR/{z}/{x}/{y}.png"}) }), ], - view: new View({ - center: fromLonLat([16.3787, 48.2089]), - zoom: 13, - // https://www.wien.gv.at/statistik/lebensraum/tabellen/stadtgebiet-eckdaten.html - extent: transformExtent([16.18278, 48.11833, 16.58, 48.32306], 'EPSG:4326', 'EPSG:3857'), - constrainOnlyCenter: true - }) + view: viewFromArea(Wien) }); -const vectorLine = new VectorSource({ +const vectorSource = new VectorSource({ attributions: ["Rohdaten"] }); -const metaData: { [id: number]: Crossing } = {} -data.sort((a, b) => { - /* - put trans flag on top (so they are not covered, - but apart from that keep the drawing order random - */ - if (a.type == "transFlag") { - return 1 - } - if (b.type == "transFlag") { - return -1 - } - return Math.random() - 0.5; +let metaData: MetaData + +router.on("/Wien", () => { + map.setView(viewFromArea(Wien)) + // @ts-ignore + import("../data/Wien.json?inline").then((data) => { + metaData = loadData(data.default as unknown as Crossing[], vectorSource) + }) }) -data.forEach(c => { - if (typeof c.geo === "undefined") { - return - } - const points = c.geo.coords.map(coord => transform(coord, 'EPSG:4326', 'EPSG:3857')); +router.on("/Ober%C3%B6sterreich", () => { + map.setView(viewFromArea(areas.OOE)) - const featureLine = new Feature({ - geometry: new LineString(points) - }); - const featureDot = new Feature({ - geometry: new Point(averageCoords(points)) - }); - featureLine.setId(c.id) - featureDot.setId(c.id + 10000) - metaData[c.id] = c - vectorLine.addFeature(featureLine); - vectorLine.addFeature(featureDot); + // @ts-ignore + import("../data/Oberösterreich.json?inline").then((data) => { + metaData = loadData(data.default as unknown as Crossing[], vectorSource) + }) }) - - +redirect(router, "/", "/Wien") +router.resolve() const greenLine = new Style({ fill: new Fill({color: '#00FF00'}), stroke: new Stroke({color: '#00FF00', width: 2}) @@ -128,7 +102,7 @@ const circleStyle = new Style({ }), }) const vectorLineLayer = new VectorLayer({ - source: vectorLine, + source: vectorSource, style: function (feature, resolution) { const zoom = map.getView().getZoomForResolution(resolution); if (!zoom) { @@ -153,6 +127,18 @@ const vectorLineLayer = new VectorLayer({ map.addLayer(vectorLineLayer); +// window.bla = function () { +// const view = map.getView() +// map.setView( +// new View({ +// center: fromLonLat([15.3787, 47.2089]), +// zoom: 13, +// // https://www.wien.gv.at/statistik/lebensraum/tabellen/stadtgebiet-eckdaten.html +// extent: transformExtent([15.18278, 47.11833, 15.58, 47.32306], 'EPSG:4326', 'EPSG:3857'), +// constrainOnlyCenter: true +// })) +// } + import("./popups").then(popups => { popups.initPopups(map, metaData); }) diff --git a/src/popups.ts b/src/popups.ts index 5b14b3b..70e0825 100644 --- a/src/popups.ts +++ b/src/popups.ts @@ -3,6 +3,7 @@ import {createDateWithDisclaimer, displaySources} from "./text"; import type Map from "ol/Map"; import {Crossing} from "../interfaces"; import {createElement} from "./domutils"; +import "hint.css/hint.base.css" export function initPopups(map: Map, metaData: { [id: number]: Crossing }) { const container = document.getElementById('popup')!; @@ -24,7 +25,7 @@ export function initPopups(map: Map, metaData: { [id: number]: Crossing }) { closer.blur(); return false; }; - map.on('singleclick', function (event) { + map.on('singleclick', event => { map.forEachFeatureAtPixel(event.pixel, feature => { const coordinate = event.coordinate; let id = Number(feature.getId()) diff --git a/src/router.ts b/src/router.ts new file mode 100644 index 0000000..67d9338 --- /dev/null +++ b/src/router.ts @@ -0,0 +1,10 @@ +import Navigo, {Route} from "navigo"; + +export function redirect(router:Navigo,from:string,to:string){ + router.on(from, () => { + router.navigate(to, { + historyAPIMethod: 'replaceState' + }) + }) + +} diff --git a/tsconfig.json b/tsconfig.json index 27f279d..c3c6e80 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,9 +26,9 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ + "module": "ESNext", /* Specify what module code is generated. */ // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ diff --git a/utils/geo.ts b/utils/geo.ts index 8f752de..3156698 100644 --- a/utils/geo.ts +++ b/utils/geo.ts @@ -21,3 +21,10 @@ export function lineLengthInM(start: number[], end: number[]) { const value = earthRadius * c * 1000 return Math.round(value * 100) / 100 } + +export function averageCoords(coords: number[][]): number[] { + return [ + (coords[0][0] + coords[1][0]) / 2, + (coords[0][1] + coords[1][1]) / 2 + ] +}