diff --git a/README.md b/README.md index 3328416..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 @@ -14,5 +16,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..6e3efdf --- /dev/null +++ b/airportcalc.html @@ -0,0 +1,31 @@ + + + + Airportcalc 2 + + + + + + + + + + + + +
+
+ + + + diff --git a/build.mjs b/build.mjs index a46be20..0070c2c 100644 --- a/build.mjs +++ b/build.mjs @@ -10,12 +10,16 @@ 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", - publicPath: process.argv[2] == "prod" ? "https://mrt-map.github.io/map" : undefined, + outdir: "out", + publicPath: + process.argv[2] == "prod" ? "https://mrt-map.github.io/map" : undefined, plugins: [ sassPlugin({ async transform(source) { @@ -34,6 +38,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..9a9a17f 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + MRT City Map @@ -10,10 +10,10 @@ - + - - + + @@ -29,14 +29,5 @@
-
- - diff --git a/media/ac192.png b/media/ac192.png new file mode 100644 index 0000000..84ffa07 Binary files /dev/null and b/media/ac192.png differ diff --git a/media/ac512.png b/media/ac512.png new file mode 100644 index 0000000..cdd0f63 Binary files /dev/null and b/media/ac512.png differ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8d208a..af2a57e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1181,7 +1181,7 @@ packages: hasBin: true dependencies: caniuse-lite: 1.0.30001572 - electron-to-chromium: 1.4.616 + electron-to-chromium: 1.4.617 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: true @@ -1313,8 +1313,8 @@ packages: esutils: 2.0.3 dev: true - /electron-to-chromium@1.4.616: - resolution: {integrity: sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==} + /electron-to-chromium@1.4.617: + resolution: {integrity: sha512-sYNE3QxcDS4ANW1k4S/wWYMXjCVcFSOX3Bg8jpuMFaXt/x8JCmp0R1Xe1ZXDX4WXnSRBf+GJ/3eGWicUuQq5cg==} dev: true /esbuild-sass-plugin@2.16.1(esbuild@0.19.11): @@ -1324,7 +1324,7 @@ packages: dependencies: esbuild: 0.19.11 resolve: 1.22.8 - sass: 1.69.6 + sass: 1.69.7 dev: true /esbuild@0.19.11: @@ -2327,8 +2327,8 @@ packages: queue-microtask: 1.2.3 dev: true - /sass@1.69.6: - resolution: {integrity: sha512-qbRr3k9JGHWXCvZU77SD2OTwUlC+gNT+61JOLcmLm+XqH4h/5D+p4IIsxvpkB89S9AwJOyb5+rWNpIucaFxSFQ==} + /sass@1.69.7: + resolution: {integrity: sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==} engines: {node: '>=14.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..28ea71a --- /dev/null +++ b/src/airportcalc/airportcalc.ts @@ -0,0 +1,208 @@ +import L from "leaflet"; +import "leaflet-control-bar"; +import { g } from "./globals"; +import { worldcoord } from "../utils/coord"; +import { GeoJson } from "../utils/geojson"; +import { exportAirportcalc, importAirportcalc } from "./import-export"; + +const VERSION = "2.2.1 (20240104)"; + +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< + L.Polygon | L.Circle +>; + +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..12a4b33 --- /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..af2c134 --- /dev/null +++ b/src/airportcalc/import-export.ts @@ -0,0 +1,90 @@ +import L from "leaflet"; +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 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..e1e1dd5 --- /dev/null +++ b/src/airportcalc/index.ts @@ -0,0 +1,27 @@ +import { initAirportcalc } from "./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..79c3e36 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..338480c 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() { @@ -113,30 +111,31 @@ export class Buttons { this.city.disable(); this.airportCalc = L.easyButton( - "fa-ruler", - toggleControls, + "fa-plane", + () => { + 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 83% rename from src/index.ts rename to src/map/index.ts index b1df9e3..c41c1a2 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"; @@ -23,12 +23,11 @@ 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"), }); -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 82% rename from src/map-cities.ts rename to src/map/map-cities.ts index 662894f..bc1bb0c 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( @@ -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/townsearch.ts b/src/map/townsearch.ts similarity index 91% rename from src/townsearch.ts rename to src/map/townsearch.ts index b1d72f7..aa6d26c 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; @@ -55,10 +55,9 @@ 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.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 +73,13 @@ function townSearch(query: string) { } } //filter towns by search query - const relevantTowns = window.globals.cityMap.towns.filter( - (t) => + 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 +123,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 { @@ -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/ui.ts b/src/map/ui.ts similarity index 95% rename from src/ui.ts rename to src/map/ui.ts index 289c038..e37a8fd 100644 --- a/src/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/waypoint-viewer.ts b/src/map/waypoint-viewer.ts similarity index 84% rename from src/waypoint-viewer.ts rename to src/map/waypoint-viewer.ts index df3ab7c..728b183 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; @@ -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]) 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