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