From 6b7633100271c6a93cd99701e9df30eae8fc100b Mon Sep 17 00:00:00 2001 From: 7d <61975820+iiiii7d@users.noreply.github.com> Date: Thu, 4 Jan 2024 00:35:16 +0800 Subject: [PATCH 1/3] split airportcalc into its own page --- README.md | 2 - airportcalc.html | 31 +++ build.mjs | 8 +- index.html | 13 +- media/ac192.png | Bin 0 -> 10710 bytes media/ac512.png | Bin 0 -> 34699 bytes pnpm-lock.yaml | 12 +- src/airportcalc.ts | 327 ------------------------------- src/airportcalc/airportcalc.ts | 206 +++++++++++++++++++ src/airportcalc/changelog.txt | 5 + src/airportcalc/globals.ts | 53 +++++ src/airportcalc/import-export.ts | 88 +++++++++ src/airportcalc/index.ts | 27 +++ src/map.ts | 5 +- src/{ => map}/globals.ts | 21 +- src/{ => map}/index.ts | 15 +- src/{ => map}/map-cities.ts | 4 +- src/{ => map}/townsearch.ts | 16 +- src/{ => map}/ui.ts | 0 src/{ => map}/waypoint-viewer.ts | 2 +- src/{utils.ts => utils/coord.ts} | 0 src/{ => utils}/geojson.ts | 0 22 files changed, 454 insertions(+), 381 deletions(-) create mode 100644 airportcalc.html create mode 100644 media/ac192.png create mode 100644 media/ac512.png delete mode 100644 src/airportcalc.ts create mode 100644 src/airportcalc/airportcalc.ts create mode 100644 src/airportcalc/changelog.txt create mode 100644 src/airportcalc/globals.ts create mode 100644 src/airportcalc/import-export.ts create mode 100644 src/airportcalc/index.ts rename src/{ => map}/globals.ts (88%) rename src/{ => map}/index.ts (88%) rename src/{ => map}/map-cities.ts (99%) rename src/{ => map}/townsearch.ts (96%) rename src/{ => map}/ui.ts (100%) rename src/{ => map}/waypoint-viewer.ts (96%) rename src/{utils.ts => utils/coord.ts} (100%) rename src/{ => utils}/geojson.ts (100%) diff --git a/README.md b/README.md index 3328416..b0ea1e4 100644 --- a/README.md +++ b/README.md @@ -14,5 +14,3 @@ All data is sourced from staff documents and the server dynamic map. You may nee - megascatterbomb - Airplaneguy9 - Scarycrumb45 - -MRT Map and Airportcalc logo by Cortesi diff --git a/airportcalc.html b/airportcalc.html new file mode 100644 index 0000000..837d655 --- /dev/null +++ b/airportcalc.html @@ -0,0 +1,31 @@ + + + + Airportcalc 2 + + + + + + + + + + + + +
+
+ + + + diff --git a/build.mjs b/build.mjs index a46be20..57639e4 100644 --- a/build.mjs +++ b/build.mjs @@ -10,11 +10,14 @@ import postcssPresetEnv from "postcss-preset-env"; const postcssPlugins = [autoprefixer(), postcssPresetEnv({ stage: 0 })]; let ctx = await esbuild.context({ - entryPoints: ["src/index.ts"], + entryPoints: [ + {in: "src/map/index.ts", out:"out-map"}, + {in: "src/airportcalc/index.ts", out:"out-ac"}, + ], bundle: true, minify: true, sourcemap: true, - outfile: "out/out.js", + outdir: "out", publicPath: process.argv[2] == "prod" ? "https://mrt-map.github.io/map" : undefined, plugins: [ sassPlugin({ @@ -34,6 +37,7 @@ let ctx = await esbuild.context({ }); if (!fs.existsSync("out")) fs.mkdirSync("out"); fs.copyFileSync("./index.html", "./out/index.html"); +fs.copyFileSync("./airportcalc.html", "./out/airportcalc.html"); fs.copyFileSync("./manifest.json", "./out/manifest.json"); fse.copySync("./media", "./out/media"); diff --git a/index.html b/index.html index c6aa188..b3df2be 100644 --- a/index.html +++ b/index.html @@ -12,8 +12,8 @@ - - + + @@ -29,14 +29,5 @@
-
- - diff --git a/media/ac192.png b/media/ac192.png new file mode 100644 index 0000000000000000000000000000000000000000..84ffa0746cf81652b6bbf75cadcc95dadddfb192 GIT binary patch literal 10710 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 UbSpMm2zv`ql2e!cA#D--e`T{^LjV8( literal 0 HcmV?d00001 diff --git a/media/ac512.png b/media/ac512.png new file mode 100644 index 0000000000000000000000000000000000000000..cdd0f634d8ebdb8f61b8b533e1e1f342116fa144 GIT binary patch literal 34699 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.0.0'} hasBin: true dependencies: diff --git a/src/airportcalc.ts b/src/airportcalc.ts deleted file mode 100644 index 0b6c733..0000000 --- a/src/airportcalc.ts +++ /dev/null @@ -1,327 +0,0 @@ -import L from "leaflet"; -import { g } from "./globals"; -import { mapLayers } from "./map-cities"; -import { Feature, GeoJson } from "./geojson"; -import { worldcoord } from "./utils"; -import "leaflet-control-bar"; - -const VERSION = "2.2 (12/5/23)"; -/* -v1: https://github.com/iiiii7d/airportcalc -v2.0: initial release -v2.1: added logo, banner content is now selectable -v2.2: convert to typescript, integrate with rest of refactors -*/ - -const airportcalcGroup = L.layerGroup([]) as L.LayerGroup; - -const downloader = document.getElementById("downloader")! as HTMLAnchorElement; -const importer = document.getElementById("importer")! as HTMLInputElement; -let bottomBar: ControlBar | undefined = undefined; - -let drawFor = "city"; -let prevDisplayTowns = 1; -let notif = ""; - -export function initAirportcalc() { - const map = g().map; - map.pm.setGlobalOptions({ - layerGroup: airportcalcGroup, - //pmIgnore: false, - pathOptions: { - color: "#ff0000", - }, - }); - - map.pm.Toolbar.createCustomControl({ - name: "cityspace", - title: "City Space", - block: "custom", - className: "fas fa-city icon", - toggle: false, - onClick: () => { - map.pm.setGlobalOptions({ - layerGroup: airportcalcGroup, - pathOptions: { - color: "#ff0000", - }, - }); - drawFor = "city"; - }, - }); - map.pm.Toolbar.createCustomControl({ - name: "airportspace", - title: "Airport Space", - block: "custom", - className: "fas fa-plane icon", - toggle: false, - onClick: () => { - map.pm.setGlobalOptions({ - layerGroup: airportcalcGroup, - pathOptions: { - color: "#00dd00", - }, - }); - drawFor = "airport"; - }, - }); - - map.pm.Toolbar.createCustomControl({ - name: "clearall", - title: "Clear All", - block: "custom", - className: "fas fa-times icon", - toggle: false, - onClick: () => { - clear("Are you sure you want to clear all polygons?"); - }, - }); - - map.pm.Toolbar.createCustomControl({ - name: "export", - title: "Export", - block: "custom", - className: "fas fa-file-export icon", - toggle: false, - onClick: () => { - const dataStr = - "data:text/json;charset=utf-8," + - encodeURIComponent(JSON.stringify(exportAirportcalc(), null, 2)); - downloader.href = dataStr; - downloader.download = "city.apc"; - downloader.click(); - showNotif("Polygons exported"); - }, - }); - - map.pm.Toolbar.createCustomControl({ - name: "import", - title: "Import", - block: "custom", - className: "fas fa-file-import icon", - toggle: false, - onClick: () => { - document.getElementById("importer")!.click(); - }, - }); - - bottomBar = L.control.bar("bar", { - position: "bottom", - visible: false, - }); - - map.addControl(bottomBar); - - map.pm.removeControls(); - - setInterval(() => { - if (bottomBar?.isVisible()) { - // eslint-disable-next-line prefer-const - let [cityArea, airportArea, percentage] = calcCityArea(); - if (isNaN(percentage)) percentage = 0; - const newdata = - ` - City area size: ${Math.round(cityArea)}m^2 - | Airport area size: ${Math.round(airportArea)}m^2 - | Percentage: ${Math.round(percentage * 100) / 100}% - | Drawing for: ${drawFor}` + - (notif != "" ? `
${notif}` : ""); - if (bottomBar.getContainer()?.innerHTML != newdata) - bottomBar.setContent(newdata); - } - }, 50); -} - -function clear(prompt_: string) { - if (airportcalcGroup.getLayers().length != 0) { - if (confirm(prompt_)) { - airportcalcGroup.clearLayers(); - showNotif("Polygons cleared"); - } - } -} - -declare module "leaflet" { - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace control { - function bar(id: string, options: object): ControlBar; - } -} - -declare class ControlBar extends L.Control { - hide(): void; - show(): void; - getContainer(): HTMLElement | undefined; - setContent(content: string): void; - isVisible(): boolean; -} - -let airportcalc = false; -export function toggleControls() { - const map = g().map; - if (airportcalc) { - //var conf = true - //if (airportcalcGroup.getLayers().length != 0) conf = confirm("Close without exporting?"); - //if (!conf) return; - map.pm.removeControls(); - map.removeLayer(airportcalcGroup); - g().displayTowns = prevDisplayTowns == 1; - mapLayers(); - map.addControl(g().logo); - bottomBar?.hide(); - } else { - map.pm.addControls({ - position: "bottomleft", - drawCircleMarker: false, - drawPolyline: false, - drawMarker: false, - }); - if (window.localStorage.airportcalc != undefined) { - importAirportcalc( - JSON.parse(window.localStorage.airportcalc as string) as GeoJson - ); - delete window.localStorage.airportcalc; - } - map.addLayer(airportcalcGroup); - prevDisplayTowns = g().displayTowns ? 1 : 0; - g().displayTowns = false; - mapLayers(); - bottomBar?.show(); - map.removeControl(g().logo); - showNotif("Airportcalc " + VERSION); - } - airportcalc = airportcalc ? false : true; -} - -function calcCityArea(): [number, number, number] { - let cityArea = 0; - let airportArea = 0; - (airportcalcGroup.getLayers() as (L.Polygon | L.Circle)[]).forEach((l) => { - let newArea = 0; - if (l instanceof L.Polygon) { - for (let s = 0; s < l.getLatLngs().length; s++) { - let polyArea = 0; - const latlngs = (l.getLatLngs()[s] as L.LatLng[]).map((ll) => - worldcoord([ll.lat, ll.lng]) - ); - //console.log(latlngs) - for (let i = 0; i < latlngs.length; i++) { - const thisLatlng = latlngs[i]; - let nextLatlng = latlngs[i + 1]; - if (i == latlngs.length - 1) nextLatlng = latlngs[0]; - polyArea += - 0.5 * - (thisLatlng[1] + nextLatlng[1]) * - (nextLatlng[0] - thisLatlng[0]); - } - - if (s == 0) newArea += Math.abs(polyArea); - else newArea -= Math.abs(polyArea); - } - } else { - const radius = l.getRadius() * 64; - newArea = Math.PI * radius ** 2; - } - if (l.options.color == "#ff0000") cityArea += Math.abs(newArea); - else airportArea += Math.abs(newArea); - }); - return [cityArea, airportArea, (airportArea / cityArea) * 100]; -} - -function exportAirportcalc(): GeoJson { - const features: Feature[] = []; - (airportcalcGroup.getLayers() as (L.Polygon | L.Circle)[]).forEach((l) => { - const feature: Feature = { - type: "feature", - geometry: { - type: l instanceof L.Circle ? "point" : "polygon", - coordinates: [], - }, - properties: { - space: l.options.color == "#ff0000" ? "city" : "airport", - shape: l.pm.getShape(), - color: l.options.color!, - }, - }; - if (l instanceof L.Circle) { - feature.properties.radius = l.getRadius(); - const latlng = l.getLatLng(); - feature.geometry.coordinates = [latlng.lat, latlng.lng]; - } else { - //console.log(JSON.stringify(l._latlngs)) - const latlngs = l.getLatLngs() as L.LatLng[][]; - feature.geometry.coordinates = latlngs.map((ll) => - ll.map((sll): [number, number] => [sll.lat, sll.lng]) - ); - } - features.push(feature); - }); - return { - type: "FeatureCollection", - features: features, - }; -} - -function importAirportcalc(geojson: GeoJson) { - geojson.features.forEach((f) => { - if (f.properties.shape == "Circle") - airportcalcGroup.addLayer( - L.circle(f.geometry.coordinates as [number, number], { - color: f.properties.color, - radius: f.properties.radius, - }).addTo(g().map) - ); - else if (f.properties.shape == "Rectangle") - airportcalcGroup.addLayer( - L.rectangle( - [ - f.geometry.coordinates[0] as [number, number], - f.geometry.coordinates[2] as [number, number], - ], - { color: f.properties.color } - ).addTo(g().map) - ); - else - airportcalcGroup.addLayer( - L.polygon(f.geometry.coordinates as [number, number][], { - color: f.properties.color, - }).addTo(g().map) - ); - }); -} - -function preImportAirportcalc() { - const importedFile = importer.files?.[0]; - if (importedFile === undefined) return; - - const reader = new FileReader(); - reader.onload = function () { - clear("Do you want to clear all polygons?"); - const fileContent = JSON.parse(reader.result?.toString() ?? "") as GeoJson; - importer.value = ""; - //console.log(fileContent); - importAirportcalc(fileContent); - showNotif("New polygons imported"); - }; - reader.readAsText(importedFile); -} - -importer.oninput = preImportAirportcalc; - -function showNotif(newNotif: string) { - notif = newNotif; - setTimeout(() => { - if (notif == newNotif) notif = ""; - }, 3000); -} - -//map.on("pm:vertexadded pm:centerplaced", e => { -// e.lat = Math.round(e.lat); -// e.lng = Math.round(e.lng); -//}); - -window.addEventListener("beforeunload", () => { - window.localStorage.airportcalc = JSON.stringify(exportAirportcalc()); -}); diff --git a/src/airportcalc/airportcalc.ts b/src/airportcalc/airportcalc.ts new file mode 100644 index 0000000..e790007 --- /dev/null +++ b/src/airportcalc/airportcalc.ts @@ -0,0 +1,206 @@ +import L from "leaflet"; +import "leaflet-control-bar"; +import { g } from "../airportcalc/globals"; +import { worldcoord } from "../utils/coord"; +import { GeoJson } from "../utils/geojson"; +import { exportAirportcalc, importAirportcalc } from "./import-export"; + +const VERSION = "2.2 (12/5/23)"; + +declare module "leaflet" { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace control { + function bar(id: string, options: object): ControlBar; + } +} + +declare class ControlBar extends L.Control { + hide(): void; + show(): void; + getContainer(): HTMLElement | undefined; + setContent(content: string): void; + isVisible(): boolean; +} + +export const airportcalcGroup = L.layerGroup([]) as L.LayerGroup; + +export const bottomBar: ControlBar = L.control.bar("bar", { + position: "bottom", + visible: false, +}); + +let drawFor: "city" | "airport" = "city"; +let notif = ""; + +export function initAirportcalc() { + const map = g().map; + map.pm.setGlobalOptions({ + layerGroup: airportcalcGroup, + //pmIgnore: false, + pathOptions: { + color: "#ff0000", + }, + }); + + map.pm.Toolbar.createCustomControl({ + name: "cityspace", + title: "City Space", + block: "custom", + className: "fas fa-city icon", + toggle: false, + onClick: () => { + map.pm.setGlobalOptions({ + layerGroup: airportcalcGroup, + pathOptions: { + color: "#ff0000", + }, + }); + drawFor = "city"; + }, + }); + map.pm.Toolbar.createCustomControl({ + name: "airportspace", + title: "Airport Space", + block: "custom", + className: "fas fa-plane icon", + toggle: false, + onClick: () => { + map.pm.setGlobalOptions({ + layerGroup: airportcalcGroup, + pathOptions: { + color: "#00dd00", + }, + }); + drawFor = "airport"; + }, + }); + + map.pm.Toolbar.createCustomControl({ + name: "clearall", + title: "Clear All", + block: "custom", + className: "fas fa-times icon", + toggle: false, + onClick: () => { + clear("Are you sure you want to clear all polygons?"); + }, + }); + + map.pm.Toolbar.createCustomControl({ + name: "export", + title: "Export", + block: "custom", + className: "fas fa-file-export icon", + toggle: false, + onClick: () => { + const downloader = document.getElementById("downloader")! as HTMLAnchorElement; + const dataStr = + "data:text/json;charset=utf-8," + + encodeURIComponent(JSON.stringify(exportAirportcalc(), null, 2)); + downloader.href = dataStr; + downloader.download = "city.apc"; + downloader.click(); + showNotif("Polygons exported"); + }, + }); + + map.pm.Toolbar.createCustomControl({ + name: "import", + title: "Import", + block: "custom", + className: "fas fa-file-import icon", + toggle: false, + onClick: () => { + document.getElementById("importer")!.click(); + }, + }); + + map.pm.addControls({ + position: "bottomleft", + drawCircleMarker: false, + drawPolyline: false, + drawMarker: false, + }); + + setInterval(() => { + // eslint-disable-next-line prefer-const + let [cityArea, airportArea, percentage] = calcCityArea(); + if (isNaN(percentage)) percentage = 0; + const newdata = + ` + City area size: ${Math.round(cityArea)}m^2 + | Airport area size: ${Math.round(airportArea)}m^2 + | Percentage: ${Math.round(percentage * 100) / 100}% + | Drawing for: ${drawFor}` + + (notif != "" ? `
${notif}` : ""); + if (bottomBar.getContainer()?.innerHTML != newdata) + bottomBar.setContent(newdata); + }, 50); + + + if (window.localStorage.airportcalc != undefined) { + importAirportcalc( + JSON.parse(window.localStorage.airportcalc as string) as GeoJson + ); + delete window.localStorage.airportcalc; + } + + map.addLayer(airportcalcGroup); + map.addControl(bottomBar); + bottomBar.show(); + showNotif("Airportcalc " + VERSION); +} + +export function clear(prompt_: string) { + if (airportcalcGroup.getLayers().length != 0) { + if (confirm(prompt_)) { + airportcalcGroup.clearLayers(); + showNotif("Polygons cleared"); + } + } +} + + +function calcCityArea(): [number, number, number] { + let cityArea = 0; + let airportArea = 0; + (airportcalcGroup.getLayers() as (L.Polygon | L.Circle)[]).forEach((l) => { + let newArea = 0; + if (l instanceof L.Polygon) { + for (let s = 0; s < l.getLatLngs().length; s++) { + let polyArea = 0; + const latlngs = (l.getLatLngs()[s] as L.LatLng[]).map((ll) => + worldcoord([ll.lat, ll.lng]) + ); + //console.log(latlngs) + for (let i = 0; i < latlngs.length; i++) { + const thisLatlng = latlngs[i]; + let nextLatlng = latlngs[i + 1]; + if (i == latlngs.length - 1) nextLatlng = latlngs[0]; + polyArea += + 0.5 * + (thisLatlng[1] + nextLatlng[1]) * + (nextLatlng[0] - thisLatlng[0]); + } + + if (s == 0) newArea += Math.abs(polyArea); + else newArea -= Math.abs(polyArea); + } + } else { + const radius = l.getRadius() * 64; + newArea = Math.PI * radius ** 2; + } + if (l.options.color == "#ff0000") cityArea += Math.abs(newArea); + else airportArea += Math.abs(newArea); + }); + return [cityArea, airportArea, (airportArea / cityArea) * 100]; +} + +export function showNotif(newNotif: string) { + notif = newNotif; + setTimeout(() => { + if (notif == newNotif) notif = ""; + }, 3000); +} diff --git a/src/airportcalc/changelog.txt b/src/airportcalc/changelog.txt new file mode 100644 index 0000000..8de7c99 --- /dev/null +++ b/src/airportcalc/changelog.txt @@ -0,0 +1,5 @@ +v1: https://github.com/iiiii7d/airportcalc +v2.0: initial release +v2.1: added logo, banner content is now selectable +v2.2: convert to typescript, integrate with rest of refactors +v2.2.1: split into multiple files and separate from main page \ No newline at end of file diff --git a/src/airportcalc/globals.ts b/src/airportcalc/globals.ts new file mode 100644 index 0000000..26007fd --- /dev/null +++ b/src/airportcalc/globals.ts @@ -0,0 +1,53 @@ +import "@geoman-io/leaflet-geoman-free"; +import L, { Control } from "leaflet"; +import "leaflet-easybutton"; + +export class Globals { + map: L.Map; + buttons: Buttons; + + constructor(map: L.Map) { + this.map = map; + this.buttons = new Buttons(this.map); + } +} + +export class Buttons { + guide: Control.EasyButton; + home: Control.EasyButton + + constructor(map: L.Map) { + this.guide = L.easyButton( + "fa-question", + () => { + window.open("https://github.com/mrt-map/map/wiki/City-Map", "_blank"); + }, + "Guide" + ) + .setPosition("topright") + .addTo(map); + this.home = L.easyButton( + "fa-house", + () => { + window.open("./", "_self"); + }, + "Return to MRT City Map" + ) + .setPosition("topright") + .addTo(map); + } +} + +declare global { + interface Window { + acGlobals: Globals; + } +} + +export function g(): Globals { + return window.acGlobals; +} + +export function gb(): Buttons { + return window.acGlobals.buttons; +} diff --git a/src/airportcalc/import-export.ts b/src/airportcalc/import-export.ts new file mode 100644 index 0000000..77864f1 --- /dev/null +++ b/src/airportcalc/import-export.ts @@ -0,0 +1,88 @@ +import L from "leaflet"; +import { g } from "../airportcalc/globals"; +import { Feature, GeoJson } from "../utils/geojson"; +import { airportcalcGroup, clear, showNotif } from "./airportcalc"; + +export const importer = document.getElementById("importer")! as HTMLInputElement; + +export function exportAirportcalc(): GeoJson { + const features: Feature[] = []; + (airportcalcGroup.getLayers() as (L.Polygon | L.Circle)[]).forEach((l) => { + const feature: Feature = { + type: "feature", + geometry: { + type: l instanceof L.Circle ? "point" : "polygon", + coordinates: [], + }, + properties: { + space: l.options.color == "#ff0000" ? "city" : "airport", + shape: l.pm.getShape(), + color: l.options.color!, + }, + }; + if (l instanceof L.Circle) { + feature.properties.radius = l.getRadius(); + const latlng = l.getLatLng(); + feature.geometry.coordinates = [latlng.lat, latlng.lng]; + } else { + //console.log(JSON.stringify(l._latlngs)) + const latlngs = l.getLatLngs() as L.LatLng[][]; + feature.geometry.coordinates = latlngs.map((ll) => ll.map((sll): [number, number] => [sll.lat, sll.lng]) + ); + } + features.push(feature); + }); + return { + type: "FeatureCollection", + features: features, + }; +} +export function importAirportcalc(geojson: GeoJson) { + geojson.features.forEach((f) => { + if (f.properties.shape == "Circle") + airportcalcGroup.addLayer( + L.circle(f.geometry.coordinates as [number, number], { + color: f.properties.color, + radius: f.properties.radius, + }).addTo(g().map) + ); + else if (f.properties.shape == "Rectangle") + airportcalcGroup.addLayer( + L.rectangle( + [ + f.geometry.coordinates[0] as [number, number], + f.geometry.coordinates[2] as [number, number], + ], + { color: f.properties.color } + ).addTo(g().map) + ); + + else + airportcalcGroup.addLayer( + L.polygon(f.geometry.coordinates as [number, number][], { + color: f.properties.color, + }).addTo(g().map) + ); + }); +} +function preImportAirportcalc() { + const importedFile = importer.files?.[0]; + if (importedFile === undefined) return; + + const reader = new FileReader(); + reader.onload = function () { + clear("Do you want to clear all polygons?"); + const fileContent = JSON.parse(reader.result?.toString() ?? "") as GeoJson; + importer.value = ""; + //console.log(fileContent); + importAirportcalc(fileContent); + showNotif("New polygons imported"); + }; + reader.readAsText(importedFile); +} + +importer.oninput = preImportAirportcalc; + +window.addEventListener("beforeunload", () => { + window.localStorage.airportcalc = JSON.stringify(exportAirportcalc()); +}); diff --git a/src/airportcalc/index.ts b/src/airportcalc/index.ts new file mode 100644 index 0000000..aec6101 --- /dev/null +++ b/src/airportcalc/index.ts @@ -0,0 +1,27 @@ +import { initAirportcalc } from "../airportcalc/airportcalc.ts"; +import { initMap } from "../map.ts"; + +import "@fortawesome/fontawesome-free/css/all.min.css"; +import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css"; +import "leaflet-control-bar/src/L.Control.Bar.css"; +import "leaflet-easybutton/src/easy-button.css"; +import "leaflet/dist/leaflet.css"; +import "./../style.css"; + +import L from "leaflet"; +import { Globals } from "./globals.ts"; + +// https://stackoverflow.com/a/58254190 +// @ts-expect-error fix esbuild not making these load by themselves +delete L.Icon.Default.prototype._getIconUrl; +L.Icon.Default.mergeOptions({ + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + iconUrl: require("leaflet/dist/images/marker-icon.png"), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + shadowUrl: require("leaflet/dist/images/marker-shadow.png") +}); + +window.acGlobals = new Globals(initMap()); +initAirportcalc(); diff --git a/src/map.ts b/src/map.ts index 2130f2e..fa49d2a 100644 --- a/src/map.ts +++ b/src/map.ts @@ -1,5 +1,4 @@ import L from "leaflet"; -import { Globals } from "./globals"; //override the default class CustomTileLayer extends L.TileLayer { @@ -39,7 +38,7 @@ const customTileLayer = function ( return new CustomTileLayer(templateUrl, options); }; -export function initMap() { +export function initMap(): L.Map { const map = L.map("map", { crs: L.CRS.Simple, }).setView([0, 0], 8); @@ -64,5 +63,5 @@ export function initMap() { }) .addTo(map); - window.globals = new Globals(map); + return map } diff --git a/src/globals.ts b/src/map/globals.ts similarity index 88% rename from src/globals.ts rename to src/map/globals.ts index 7e6c969..3a0a30e 100644 --- a/src/globals.ts +++ b/src/map/globals.ts @@ -1,9 +1,7 @@ -import { Control } from "leaflet"; -import "leaflet-easybutton"; import "@geoman-io/leaflet-geoman-free"; -import L from "leaflet"; +import L, { Control } from "leaflet"; +import "leaflet-easybutton"; import { mapLayers } from "./map-cities"; -import { toggleControls } from "./airportcalc"; export class Globals { displayTowns = true; //used by certain later scripts to tell certain functions not to do certain things if a search in progress @@ -56,7 +54,7 @@ export class Logo extends L.Control { override onAdd() { const container = L.DomUtil.create("div"); container.innerHTML = - ""; + ""; return container; } override onRemove() { @@ -114,29 +112,30 @@ export class Buttons { this.airportCalc = L.easyButton( "fa-ruler", - toggleControls, + () => { + window.open("./airportcalc.html", "_self"); + }, "Open Airportcalc 2" ) .setPosition("topright") .addTo(map); - this.airportCalc.disable(); } } declare global { interface Window { - globals: Globals; + mapGlobals: Globals; } } export function g(): Globals { - return window.globals; + return window.mapGlobals; } export function gcm(): CityMap { - return window.globals.cityMap; + return window.mapGlobals.cityMap; } export function gb(): Buttons { - return window.globals.buttons; + return window.mapGlobals.buttons; } diff --git a/src/index.ts b/src/map/index.ts similarity index 88% rename from src/index.ts rename to src/map/index.ts index b1df9e3..2430a2d 100644 --- a/src/index.ts +++ b/src/map/index.ts @@ -1,16 +1,16 @@ -import "./ui.ts"; -import { initMap } from "./map.ts"; +import { initMap } from "../map.ts"; import { initMapCities } from "./map-cities.ts"; import { initTownSearch } from "./townsearch.ts"; -import { initAirportcalc } from "./airportcalc.ts"; +import "./ui.ts"; import { initAirways, initWaypoints } from "./waypoint-viewer.ts"; +import { Globals } from "./globals.ts"; -import "./style.css"; -import "leaflet/dist/leaflet.css"; import "@fortawesome/fontawesome-free/css/all.min.css"; import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css"; -import "leaflet-easybutton/src/easy-button.css"; import "leaflet-control-bar/src/L.Control.Bar.css"; +import "leaflet-easybutton/src/easy-button.css"; +import "leaflet/dist/leaflet.css"; +import "./../style.css"; import L from "leaflet"; @@ -26,9 +26,8 @@ L.Icon.Default.mergeOptions({ shadowUrl: require("leaflet/dist/images/marker-shadow.png") }); -initMap(); +window.mapGlobals = new Globals(initMap()); void initMapCities(); void initTownSearch(); -initAirportcalc(); void initWaypoints(); void initAirways(); diff --git a/src/map-cities.ts b/src/map/map-cities.ts similarity index 99% rename from src/map-cities.ts rename to src/map/map-cities.ts index 662894f..2ee652a 100644 --- a/src/map-cities.ts +++ b/src/map/map-cities.ts @@ -1,7 +1,7 @@ +import $ from "jquery"; import L from "leaflet"; -import { mapcoord } from "./utils"; +import { mapcoord } from "../utils/coord"; import { CityMap, Town, g, gb, gcm } from "./globals"; -import $ from "jquery"; export async function initMapCities() { const res = await fetch( diff --git a/src/townsearch.ts b/src/map/townsearch.ts similarity index 96% rename from src/townsearch.ts rename to src/map/townsearch.ts index b1d72f7..ab7ac10 100644 --- a/src/townsearch.ts +++ b/src/map/townsearch.ts @@ -1,9 +1,9 @@ +import $ from "jquery"; import L from "leaflet"; -import { resetOffset } from "./ui"; -import { mapcoord } from "./utils"; -import { mapLayers } from "./map-cities"; +import { mapcoord } from "../utils/coord"; import { g, gcm } from "./globals"; -import $ from "jquery"; +import { mapLayers } from "./map-cities"; +import { resetOffset } from "./ui"; interface Member { Username: string | number; @@ -58,7 +58,7 @@ function townSearch(query: string) { gcm().searchLayer = L.featureGroup() as L.FeatureGroup; //tell other functions not to display towns - window.globals.displayTowns = false; + window.mapGlobals.displayTowns = false; //get members with names (including old names) matching query const relevantNames: string[] = []; for (const member of MRTMembers) { @@ -74,13 +74,13 @@ function townSearch(query: string) { } } //filter towns by search query - const relevantTowns = window.globals.cityMap.towns.filter( + const relevantTowns = window.mapGlobals.cityMap.towns.filter( (t) => t.Name?.toString().toLowerCase().includes(query.toLowerCase()) ?? relevantNames.includes(t.Mayor.toString().toLowerCase()) ); //remove other markers from map - for (const layer of window.globals.cityMap.cityLayers.values()) { + for (const layer of window.mapGlobals.cityMap.cityLayers.values()) { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument map.removeLayer(layer); } @@ -124,7 +124,7 @@ function startSearch() { //console.log(value) if (value == null || value == "") { $(".results__container").css("display", "none"); - window.globals.displayTowns = true; + window.mapGlobals.displayTowns = true; mapLayers(); document.getElementById("search__results")!.innerHTML = ""; } else { diff --git a/src/ui.ts b/src/map/ui.ts similarity index 100% rename from src/ui.ts rename to src/map/ui.ts diff --git a/src/waypoint-viewer.ts b/src/map/waypoint-viewer.ts similarity index 96% rename from src/waypoint-viewer.ts rename to src/map/waypoint-viewer.ts index df3ab7c..2a35030 100644 --- a/src/waypoint-viewer.ts +++ b/src/map/waypoint-viewer.ts @@ -1,5 +1,5 @@ import * as L from "leaflet"; -import { mapcoord } from "./utils"; +import { mapcoord } from "../utils/coord"; import { g } from "./globals"; const params = new URL(document.location.toString()).searchParams; diff --git a/src/utils.ts b/src/utils/coord.ts similarity index 100% rename from src/utils.ts rename to src/utils/coord.ts diff --git a/src/geojson.ts b/src/utils/geojson.ts similarity index 100% rename from src/geojson.ts rename to src/utils/geojson.ts From 0d9ed3e2ab0d1e66611e611f69485ac8bf9c7e7d Mon Sep 17 00:00:00 2001 From: 7d <61975820+iiiii7d@users.noreply.github.com> Date: Thu, 4 Jan 2024 00:37:38 +0800 Subject: [PATCH 2/3] run formatter + touchups --- README.md | 2 ++ airportcalc.html | 4 ++-- build.mjs | 7 +++--- index.html | 4 ++-- src/airportcalc/airportcalc.ts | 18 ++++++++------- src/airportcalc/globals.ts | 18 +++++++-------- src/airportcalc/import-export.ts | 14 +++++++----- src/airportcalc/index.ts | 4 ++-- src/map.ts | 2 +- src/map/index.ts | 2 +- src/map/map-cities.ts | 38 +++++++++++++++----------------- src/map/townsearch.ts | 21 +++++------------- src/map/ui.ts | 4 ++-- src/map/waypoint-viewer.ts | 4 ++-- 14 files changed, 69 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index b0ea1e4..68aba5e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A detailed map of the Minecart Rapid Transit server, inspired by OpenStreetMap and Google Maps +Also includes Airportcalc 2 + All data is sourced from staff documents and the server dynamic map. You may need to hard refresh your page (shift+f5) to get the latest updates. ## Collaborators diff --git a/airportcalc.html b/airportcalc.html index 837d655..6e3efdf 100644 --- a/airportcalc.html +++ b/airportcalc.html @@ -1,4 +1,4 @@ - + Airportcalc 2 @@ -10,7 +10,7 @@ - + diff --git a/build.mjs b/build.mjs index 57639e4..0070c2c 100644 --- a/build.mjs +++ b/build.mjs @@ -11,14 +11,15 @@ const postcssPlugins = [autoprefixer(), postcssPresetEnv({ stage: 0 })]; let ctx = await esbuild.context({ entryPoints: [ - {in: "src/map/index.ts", out:"out-map"}, - {in: "src/airportcalc/index.ts", out:"out-ac"}, + { in: "src/map/index.ts", out: "out-map" }, + { in: "src/airportcalc/index.ts", out: "out-ac" }, ], bundle: true, minify: true, sourcemap: true, outdir: "out", - publicPath: process.argv[2] == "prod" ? "https://mrt-map.github.io/map" : undefined, + publicPath: + process.argv[2] == "prod" ? "https://mrt-map.github.io/map" : undefined, plugins: [ sassPlugin({ async transform(source) { diff --git a/index.html b/index.html index b3df2be..9a9a17f 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + MRT City Map @@ -10,7 +10,7 @@ - + diff --git a/src/airportcalc/airportcalc.ts b/src/airportcalc/airportcalc.ts index e790007..28ea71a 100644 --- a/src/airportcalc/airportcalc.ts +++ b/src/airportcalc/airportcalc.ts @@ -1,11 +1,11 @@ import L from "leaflet"; import "leaflet-control-bar"; -import { g } from "../airportcalc/globals"; +import { g } from "./globals"; import { worldcoord } from "../utils/coord"; import { GeoJson } from "../utils/geojson"; import { exportAirportcalc, importAirportcalc } from "./import-export"; -const VERSION = "2.2 (12/5/23)"; +const VERSION = "2.2.1 (20240104)"; declare module "leaflet" { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -22,7 +22,9 @@ declare class ControlBar extends L.Control { isVisible(): boolean; } -export const airportcalcGroup = L.layerGroup([]) as L.LayerGroup; +export const airportcalcGroup = L.layerGroup([]) as L.LayerGroup< + L.Polygon | L.Circle +>; export const bottomBar: ControlBar = L.control.bar("bar", { position: "bottom", @@ -93,7 +95,9 @@ export function initAirportcalc() { className: "fas fa-file-export icon", toggle: false, onClick: () => { - const downloader = document.getElementById("downloader")! as HTMLAnchorElement; + const downloader = document.getElementById( + "downloader" + )! as HTMLAnchorElement; const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportAirportcalc(), null, 2)); @@ -139,7 +143,6 @@ export function initAirportcalc() { bottomBar.setContent(newdata); }, 50); - if (window.localStorage.airportcalc != undefined) { importAirportcalc( JSON.parse(window.localStorage.airportcalc as string) as GeoJson @@ -162,16 +165,15 @@ export function clear(prompt_: string) { } } - function calcCityArea(): [number, number, number] { let cityArea = 0; let airportArea = 0; - (airportcalcGroup.getLayers() as (L.Polygon | L.Circle)[]).forEach((l) => { + (airportcalcGroup.getLayers() as (L.Polygon | L.Circle)[]).forEach(l => { let newArea = 0; if (l instanceof L.Polygon) { for (let s = 0; s < l.getLatLngs().length; s++) { let polyArea = 0; - const latlngs = (l.getLatLngs()[s] as L.LatLng[]).map((ll) => + const latlngs = (l.getLatLngs()[s] as L.LatLng[]).map(ll => worldcoord([ll.lat, ll.lng]) ); //console.log(latlngs) diff --git a/src/airportcalc/globals.ts b/src/airportcalc/globals.ts index 26007fd..12a4b33 100644 --- a/src/airportcalc/globals.ts +++ b/src/airportcalc/globals.ts @@ -14,7 +14,7 @@ export class Globals { export class Buttons { guide: Control.EasyButton; - home: Control.EasyButton + home: Control.EasyButton; constructor(map: L.Map) { this.guide = L.easyButton( @@ -27,14 +27,14 @@ export class Buttons { .setPosition("topright") .addTo(map); this.home = L.easyButton( - "fa-house", - () => { - window.open("./", "_self"); - }, - "Return to MRT City Map" - ) - .setPosition("topright") - .addTo(map); + "fa-house", + () => { + window.open("./", "_self"); + }, + "Return to MRT City Map" + ) + .setPosition("topright") + .addTo(map); } } diff --git a/src/airportcalc/import-export.ts b/src/airportcalc/import-export.ts index 77864f1..af2c134 100644 --- a/src/airportcalc/import-export.ts +++ b/src/airportcalc/import-export.ts @@ -1,13 +1,15 @@ import L from "leaflet"; -import { g } from "../airportcalc/globals"; +import { g } from "./globals"; import { Feature, GeoJson } from "../utils/geojson"; import { airportcalcGroup, clear, showNotif } from "./airportcalc"; -export const importer = document.getElementById("importer")! as HTMLInputElement; +export const importer = document.getElementById( + "importer" +)! as HTMLInputElement; export function exportAirportcalc(): GeoJson { const features: Feature[] = []; - (airportcalcGroup.getLayers() as (L.Polygon | L.Circle)[]).forEach((l) => { + (airportcalcGroup.getLayers() as (L.Polygon | L.Circle)[]).forEach(l => { const feature: Feature = { type: "feature", geometry: { @@ -27,7 +29,8 @@ export function exportAirportcalc(): GeoJson { } else { //console.log(JSON.stringify(l._latlngs)) const latlngs = l.getLatLngs() as L.LatLng[][]; - feature.geometry.coordinates = latlngs.map((ll) => ll.map((sll): [number, number] => [sll.lat, sll.lng]) + feature.geometry.coordinates = latlngs.map(ll => + ll.map((sll): [number, number] => [sll.lat, sll.lng]) ); } features.push(feature); @@ -38,7 +41,7 @@ export function exportAirportcalc(): GeoJson { }; } export function importAirportcalc(geojson: GeoJson) { - geojson.features.forEach((f) => { + geojson.features.forEach(f => { if (f.properties.shape == "Circle") airportcalcGroup.addLayer( L.circle(f.geometry.coordinates as [number, number], { @@ -56,7 +59,6 @@ export function importAirportcalc(geojson: GeoJson) { { color: f.properties.color } ).addTo(g().map) ); - else airportcalcGroup.addLayer( L.polygon(f.geometry.coordinates as [number, number][], { diff --git a/src/airportcalc/index.ts b/src/airportcalc/index.ts index aec6101..e1e1dd5 100644 --- a/src/airportcalc/index.ts +++ b/src/airportcalc/index.ts @@ -1,4 +1,4 @@ -import { initAirportcalc } from "../airportcalc/airportcalc.ts"; +import { initAirportcalc } from "./airportcalc.ts"; import { initMap } from "../map.ts"; import "@fortawesome/fontawesome-free/css/all.min.css"; @@ -20,7 +20,7 @@ L.Icon.Default.mergeOptions({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment iconUrl: require("leaflet/dist/images/marker-icon.png"), // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - shadowUrl: require("leaflet/dist/images/marker-shadow.png") + shadowUrl: require("leaflet/dist/images/marker-shadow.png"), }); window.acGlobals = new Globals(initMap()); diff --git a/src/map.ts b/src/map.ts index fa49d2a..79c3e36 100644 --- a/src/map.ts +++ b/src/map.ts @@ -63,5 +63,5 @@ export function initMap(): L.Map { }) .addTo(map); - return map + return map; } diff --git a/src/map/index.ts b/src/map/index.ts index 2430a2d..c41c1a2 100644 --- a/src/map/index.ts +++ b/src/map/index.ts @@ -23,7 +23,7 @@ L.Icon.Default.mergeOptions({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment iconUrl: require("leaflet/dist/images/marker-icon.png"), // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - shadowUrl: require("leaflet/dist/images/marker-shadow.png") + shadowUrl: require("leaflet/dist/images/marker-shadow.png"), }); window.mapGlobals = new Globals(initMap()); diff --git a/src/map/map-cities.ts b/src/map/map-cities.ts index 2ee652a..bc1bb0c 100644 --- a/src/map/map-cities.ts +++ b/src/map/map-cities.ts @@ -12,7 +12,7 @@ export async function initMapCities() { mapLayers(); gb().city.enable(); gb().airportCalc.enable(); - $("#search__input").removeAttr("disabled") + $("#search__input").removeAttr("disabled"); } //when we zoom the map @@ -33,7 +33,7 @@ export function mapLayers() { return; } else { map.removeLayer(searchLayer); - console.log(cityLayers.entries()) + console.log(cityLayers.entries()); map.addLayer(cityLayers.get("Community")!); map.addLayer(cityLayers.get("Premier")!); map.addLayer(cityLayers.get("Governor")!); @@ -111,31 +111,29 @@ function mapTowns(towns: Town[]) { cityMarkers.set(town["Town Rank"], []); } //create marker and add it to array - cityMarkers - .get(town["Town Rank"])! - .push( - L.circleMarker(coords, { - color: rankColors[town["Town Rank"]], - radius: 7, - }).bindPopup( - `Name: ${town.Name}
Mayor: ${town.Mayor}
Deputy Mayor: ${ - town["Deputy Mayor"] - }
Rank: ${ - town["Town Rank"] - }
Navigate to here with RapidRoute` - ) - ); + cityMarkers.get(town["Town Rank"])!.push( + L.circleMarker(coords, { + color: rankColors[town["Town Rank"]], + radius: 7, + }).bindPopup( + `Name: ${town.Name}
Mayor: ${town.Mayor}
Deputy Mayor: ${ + town["Deputy Mayor"] + }
Rank: ${ + town["Town Rank"] + }
Navigate to here with RapidRoute` + ) + ); } } //for each type of city - CityMap.cityTypes.forEach((type) => { + CityMap.cityTypes.forEach(type => { //create a new feature group const featureGroup = L.featureGroup() as L.FeatureGroup; //and add all cities of type - cityMarkers.get(type)!.forEach((city) => { + cityMarkers.get(type)!.forEach(city => { featureGroup.addLayer(city); }); cityLayers.set(type, featureGroup); diff --git a/src/map/townsearch.ts b/src/map/townsearch.ts index ab7ac10..aa6d26c 100644 --- a/src/map/townsearch.ts +++ b/src/map/townsearch.ts @@ -55,8 +55,7 @@ function townSearch(query: string) { //hide old search map.removeLayer(gcm().searchLayer); //redefine feature group - gcm().searchLayer = - L.featureGroup() as L.FeatureGroup; + gcm().searchLayer = L.featureGroup() as L.FeatureGroup; //tell other functions not to display towns window.mapGlobals.displayTowns = false; //get members with names (including old names) matching query @@ -75,7 +74,7 @@ function townSearch(query: string) { } //filter towns by search query const relevantTowns = window.mapGlobals.cityMap.towns.filter( - (t) => + t => t.Name?.toString().toLowerCase().includes(query.toLowerCase()) ?? relevantNames.includes(t.Mayor.toString().toLowerCase()) ); @@ -136,17 +135,9 @@ function startSearch() { "
No Results
"; } for (const result of results) { - const ele = document.getElementById( - "search__results" - )!; - ele.innerHTML += `
${ - result.Name - }
Rank: ${ - result["Town Rank"] - }
Mayor: ${ - result.Mayor - }
`; - ele.querySelector("div")!.onclick = () => focusMap(result.X, result.Z) + const ele = document.getElementById("search__results")!; + ele.innerHTML += `
${result.Name}
Rank: ${result["Town Rank"]}
Mayor: ${result.Mayor}
`; + ele.querySelector("div")!.onclick = () => focusMap(result.X, result.Z); } } } @@ -203,6 +194,6 @@ export function focusMap(x: number, z: number) { "This town cannot be displayed because it contains invalid coordinates. Please contact a staff member to fix." ); - console.log(x, z) + console.log(x, z); g().map.flyTo(mapcoord([x, z]), 5); } diff --git a/src/map/ui.ts b/src/map/ui.ts index 289c038..e37a8fd 100644 --- a/src/map/ui.ts +++ b/src/map/ui.ts @@ -6,11 +6,11 @@ let lastY: number; let offset = 0; let lastScrollTop = 0; -container.on("touchstart", (e) => { +container.on("touchstart", e => { lastY = e.touches[0].clientY; }); -container.on("touchmove", (e) => { +container.on("touchmove", e => { if (window.innerWidth > 1000) { container.css("transform", "none"); return; diff --git a/src/map/waypoint-viewer.ts b/src/map/waypoint-viewer.ts index 2a35030..728b183 100644 --- a/src/map/waypoint-viewer.ts +++ b/src/map/waypoint-viewer.ts @@ -9,12 +9,12 @@ export async function initWaypoints() { const res = await fetch( "https://docs.google.com/spreadsheets/d/11E60uIBKs5cOSIRHLz0O0nLCefpj7HgndS1gIXY_1hw/export?format=csv&gid=707730663" ); - const wps = (await res.text()).split("\n").map((a) => a.split(",")); + const wps = (await res.text()).split("\n").map(a => a.split(",")); wps.shift(); for (const wp of wps) { console.log(wp); L.circleMarker( - mapcoord(wp[1].split(" ").map((a) => parseInt(a)) as [number, number]), + mapcoord(wp[1].split(" ").map(a => parseInt(a)) as [number, number]), { radius: 5 } ) .bindPopup(wp[0]) From adebe4635afd869c95addec8126b7c32e120688d Mon Sep 17 00:00:00 2001 From: 7d <61975820+iiiii7d@users.noreply.github.com> Date: Thu, 4 Jan 2024 00:42:38 +0800 Subject: [PATCH 3/3] replace ruler with plane --- src/map/globals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/globals.ts b/src/map/globals.ts index 3a0a30e..338480c 100644 --- a/src/map/globals.ts +++ b/src/map/globals.ts @@ -111,7 +111,7 @@ export class Buttons { this.city.disable(); this.airportCalc = L.easyButton( - "fa-ruler", + "fa-plane", () => { window.open("./airportcalc.html", "_self"); },