all repos — onyx @ d60cf731844a2454755a95eff4906e181da13608

minimal map annotation and location data sharing tool

add skeleton for most UI elements, implement cirlces
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmL4GlYACgkQO3+8IhRO
Y5hKTA//bU3aFgRkMUwG7nj79GwETm069vkuFddzJyuXaNokLxJU2VSs5POsPFm/
/AxULjSFPVeT/y3OenIQ/bvtZ8eSl1e9y9gHGUFqofbQwOJ9eLS1u0DqH9S1jPUC
nSEkK63ep4Y8w1My8tg3yaaOHyj1H9DzbkwrpuqVStAseRiPUy6Vq5a8GZ3G7BS1
IR+V/DZH70IBcSF5o8IaLW2HPBhFoVdszgjvaGFKikImL5k3amNodUE/ngC0NsFR
lzSmRNjqqxA9UGKDGFs4sRJRJcGmWxsEOeJJPBk1fad3YtyisdUyBk0Md0mCahGe
BgydF9lSrRK+mZQz54uL4nMhhmacjDRtaPUH2YIk7zUw6BSeu7C+KetyAUMIzK0j
npXG0X99NHXDitkEYMk5CUdGUxkxYjW0rVHRO+X3QBUP6vrTBUiLHeJoqNtUCWYI
Dh9pzCiFmg88BbaWxSkrT8ScYkvG0mW7YxOFkTsTyS6Z7gJuQAGHPrIuxHsI6V+D
AoZkWOa2lD3h30jlNiI73kAVsOkFTNcOzJdNfeH+OiOU07oErUNqZi/TEk25e0sP
iGZDVHvgwjh2TwOB9ouU8opxMZgVPfh9XG/h4CzREIVtJAqj+xyyTEYoKMkMB/dG
k5kHibkqL4htY5ssts2mVzi2Pe3is8kGJdz1+J/CjDJsAcJxiUc=
=7DK9
-----END PGP SIGNATURE-----
commit

d60cf731844a2454755a95eff4906e181da13608

parent

cd2f0ab2ecf8763f37ef3988ef01313a7529ace2

M README.mdREADME.md

@@ -2,7 +2,7 @@ # onyx/scry

## about -`onyx/scry` is a lightweight map annotation and location data management and sharing tool built using [leaflet](https://leafletjs.org) and [typescript](https://typescriptlang.org). It is intended as a standalone tool to generate, manage, and share simple location data in the form of points, circles, and polygons. All of these have associated titles and descriptions and can be easily imported or exported to a `json` format for easy sharing. All data is saved locally via the `localStorage` API, and the only network calls are those to retrieve either the streetmap or satellite tile data. +`onyx/scry` is a lightweight map annotation and location data management and sharing tool built using [leaflet](https://leafletjs.com) and [typescript](https://typescriptlang.org). It is intended as a standalone tool to generate, manage, and share simple location data in the form of points, circles, and polygons. All of these have associated titles and descriptions and can be easily imported or exported to a `json` format for easy sharing. All data is saved locally via the `localStorage` API, and the only network calls are those to retrieve either the streetmap or satellite tile data. ## usage
M site/index.htmlsite/index.html

@@ -11,6 +11,7 @@ </head>

<body> <div id="map"></div> + <div id="mapControls"> <button id="addPoint-btn">&middot;</button> <button id="addCircle-btn">&compfn;</button>

@@ -29,6 +30,32 @@ <h2 id="modal-title"></h2>

<div id="modal-content"> </div> </div> + +<div id="cancel-container"> + <button class="cancel-btn">Cancel</button> +</div> + +<div id="confirm-container"> + <span id="confirm-msg"></span> + <button id="yes-btn">Yes</button> + <button id="no-btn">No</button> +</div> + +<div id="import-export-container"> + <h2></h2> + <textarea id="import-export-textarea"></textarea> + <button id="import-export-ok-btn">OK</button> + <button id="import-export-cancel-btn">Cancel</button> +</div> + +<div id="overlays-menu-container"> + <details id="markers-wrapper"><summary>Markers</summary><ul id="markers-list"></ul></details> + <details id="circles-wrapper"><summary>Circles</summary><ul id="circles-list"></ul></details> + <details id="polygons-wrapper"><summary>Polygons</summary><ul id="polygons-list"></ul></details> + <button id="import-btn">Import</button> + <button id="export-all-btn">Export All</button> +</div> + </body> <link rel='stylesheet' type="text/css" href="/static/leaflet.css"> <script src="/static/leaflet.js"></script>
M src/.srcmapsrc/.srcmap

@@ -1,5 +1,5 @@

-10-overlay.ts 146 +10-overlay.ts 147 11-tilelayer.ts 37 -20-modal.ts 78 +20-modal.ts 112 30-handlers.ts 2 -99-onyx-scry.ts 79 +99-onyx-scry.ts 103
M src/10-overlay.tssrc/10-overlay.ts

@@ -70,6 +70,7 @@

constructor(name: string, desc: string, point: Point, options: any) { super(name, desc, [ point ], options); this.self = L.circle(point, options); + this.self.bindPopup(`<h3>${name}</h3><p>${desc}</p>`); } }
M src/20-modal.tssrc/20-modal.ts

@@ -24,12 +24,20 @@ submitBtn(): HTMLElement | null {

return document.getElementById("modal-submitBtn"); } + radiusContainer(): HTMLElement | null { + return document.getElementById("radius-container"); + } + nameField(): string { return (document.getElementById("modal-name") as HTMLInputElement)?.value ?? ""; } descField(): string { return (document.getElementById("modal-desc") as HTMLInputElement)?.value ?? ""; + } + + radiusField(): string { + return (document.getElementById("modal-radius") as HTMLInputElement)?.value ?? ""; } visible(): boolean {

@@ -45,9 +53,9 @@ }

setState(state: OverlayType, args: any): void { const _this = this; + const title = this.title() switch (state) { case OverlayType.POINT: - const title = this.title() if (title) { title.innerHTML = "Add Marker"; }

@@ -71,6 +79,32 @@ });

break; case OverlayType.CIRCLE: + if (title) { + title.innerHTML = "Add Circle"; + } + fetch("/static/pointModal.html") + .then(r=>r.text()) + .then(t=> { + const content = _this.content(); + if (content) { content.innerHTML = t; } + const radiusContainer = _this.radiusContainer(); + if (radiusContainer) { + radiusContainer.style.display = "block"; + } + const submitBtn = _this.submitBtn(); + if (submitBtn) { + submitBtn.onclick = () => { + const name = _this.nameField(); + const desc = _this.descField(); + const radius = _this.radiusField(); + const circle = new Circle(name, desc, args.latlng, {radius: Number(radius)}); + circle.add(args.map); + args.overlays.circles.push(circle); + _this.setVisible(false); + } + } + }); + break; case OverlayType.POLYGON: break;
M src/99-onyx-scry.tssrc/99-onyx-scry.ts

@@ -29,7 +29,16 @@ overlays.polygons.forEach(m=>m.add(map));

const modal = new Modal(); - const addMarkerHandler = (e: any) => { + const resetMapClick = (): void => { + try { + map.off("click", addMarkerHandler); + } catch {} + try { + map.off("click", addCircleHandler); + } catch {} + } + + const addMarkerHandler = (e: any): void => { modal.setVisible(true); modal.setState(OverlayType.POINT, { latlng: e.latlng,

@@ -39,34 +48,49 @@ });

map.off("click", addMarkerHandler); } + const addCircleHandler = (e: any): void => { + modal.setVisible(true); + modal.setState(OverlayType.CIRCLE, { + latlng: e.latlng, + map: map, + overlays: overlays, + }); + map.off("click", addCircleHandler); + } + const addMarkerBtn = document.getElementById("addPoint-btn"); if (addMarkerBtn) { - addMarkerBtn.onclick = (e: any)=>{ - try{ - map.off("click", addMarkerHandler); - } finally { - map.on("click", addMarkerHandler); - } + addMarkerBtn.onclick = (e: any): void => { + resetMapClick() + map.on("click", addMarkerHandler); }; } + const addCircleBtn = document.getElementById("addCircle-btn"); + if (addCircleBtn) { + addCircleBtn.onclick = (e: any): void => { + resetMapClick(); + map.on("click", addCircleHandler); + } + } + const saveBtn = document.getElementById("save-btn"); if (saveBtn) { - saveBtn.onclick = (e: any) => { + saveBtn.onclick = (e: any): void => { OverlayState.save(overlays); }; } const clearBtn = document.getElementById("clear-btn"); if (clearBtn) { - clearBtn.onclick = (e: any) => { + clearBtn.onclick = (e: any): void => { overlays = OverlayState.clear(overlays, map); } } const tilesBtn = document.getElementById("tiles-btn"); if (tilesBtn) { - tilesBtn.onclick = (e: any) => { + tilesBtn.onclick = (e: any): void => { if (TileLayerWrapper.getActiveLayer() == satelliteLayer) { TileLayerWrapper.enableOnly(streetLayer, map); } else {
M src/onyx-scry.tssrc/onyx-scry.ts

@@ -70,6 +70,7 @@

constructor(name: string, desc: string, point: Point, options: any) { super(name, desc, [ point ], options); this.self = L.circle(point, options); + this.self.bindPopup(`<h3>${name}</h3><p>${desc}</p>`); } }

@@ -207,6 +208,10 @@ submitBtn(): HTMLElement | null {

return document.getElementById("modal-submitBtn"); } + radiusContainer(): HTMLElement | null { + return document.getElementById("radius-container"); + } + nameField(): string { return (document.getElementById("modal-name") as HTMLInputElement)?.value ?? ""; }

@@ -215,6 +220,10 @@ descField(): string {

return (document.getElementById("modal-desc") as HTMLInputElement)?.value ?? ""; } + radiusField(): string { + return (document.getElementById("modal-radius") as HTMLInputElement)?.value ?? ""; + } + visible(): boolean { return this.self()?.style.display != "none"; }

@@ -228,9 +237,9 @@ }

setState(state: OverlayType, args: any): void { const _this = this; + const title = this.title() switch (state) { case OverlayType.POINT: - const title = this.title() if (title) { title.innerHTML = "Add Marker"; }

@@ -254,6 +263,32 @@ });

break; case OverlayType.CIRCLE: + if (title) { + title.innerHTML = "Add Circle"; + } + fetch("/static/pointModal.html") + .then(r=>r.text()) + .then(t=> { + const content = _this.content(); + if (content) { content.innerHTML = t; } + const radiusContainer = _this.radiusContainer(); + if (radiusContainer) { + radiusContainer.style.display = "block"; + } + const submitBtn = _this.submitBtn(); + if (submitBtn) { + submitBtn.onclick = () => { + const name = _this.nameField(); + const desc = _this.descField(); + const radius = _this.radiusField(); + const circle = new Circle(name, desc, args.latlng, {radius: Number(radius)}); + circle.add(args.map); + args.overlays.circles.push(circle); + _this.setVisible(false); + } + } + }); + break; case OverlayType.POLYGON: break;

@@ -292,7 +327,16 @@ overlays.polygons.forEach(m=>m.add(map));

const modal = new Modal(); - const addMarkerHandler = (e: any) => { + const resetMapClick = (): void => { + try { + map.off("click", addMarkerHandler); + } catch {} + try { + map.off("click", addCircleHandler); + } catch {} + } + + const addMarkerHandler = (e: any): void => { modal.setVisible(true); modal.setState(OverlayType.POINT, { latlng: e.latlng,

@@ -302,34 +346,49 @@ });

map.off("click", addMarkerHandler); } + const addCircleHandler = (e: any): void => { + modal.setVisible(true); + modal.setState(OverlayType.CIRCLE, { + latlng: e.latlng, + map: map, + overlays: overlays, + }); + map.off("click", addCircleHandler); + } + const addMarkerBtn = document.getElementById("addPoint-btn"); if (addMarkerBtn) { - addMarkerBtn.onclick = (e: any)=>{ - try{ - map.off("click", addMarkerHandler); - } finally { - map.on("click", addMarkerHandler); - } + addMarkerBtn.onclick = (e: any): void => { + resetMapClick() + map.on("click", addMarkerHandler); }; } + const addCircleBtn = document.getElementById("addCircle-btn"); + if (addCircleBtn) { + addCircleBtn.onclick = (e: any): void => { + resetMapClick(); + map.on("click", addCircleHandler); + } + } + const saveBtn = document.getElementById("save-btn"); if (saveBtn) { - saveBtn.onclick = (e: any) => { + saveBtn.onclick = (e: any): void => { OverlayState.save(overlays); }; } const clearBtn = document.getElementById("clear-btn"); if (clearBtn) { - clearBtn.onclick = (e: any) => { + clearBtn.onclick = (e: any): void => { overlays = OverlayState.clear(overlays, map); } } const tilesBtn = document.getElementById("tiles-btn"); if (tilesBtn) { - tilesBtn.onclick = (e: any) => { + tilesBtn.onclick = (e: any): void => { if (TileLayerWrapper.getActiveLayer() == satelliteLayer) { TileLayerWrapper.enableOnly(streetLayer, map); } else {
M static/onyx-scry.jsstatic/onyx-scry.js

@@ -65,6 +65,7 @@ __extends(Circle, _super);

function Circle(name, desc, point, options) { var _this_1 = _super.call(this, name, desc, [point], options) || this; _this_1.self = L.circle(point, options); + _this_1.self.bindPopup("<h3>" + name + "</h3><p>" + desc + "</p>"); return _this_1; } return Circle;

@@ -189,6 +190,9 @@ };

Modal.prototype.submitBtn = function () { return document.getElementById("modal-submitBtn"); }; + Modal.prototype.radiusContainer = function () { + return document.getElementById("radius-container"); + }; Modal.prototype.nameField = function () { var _a, _b; return (_b = (_a = document.getElementById("modal-name")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";

@@ -197,6 +201,10 @@ Modal.prototype.descField = function () {

var _a, _b; return (_b = (_a = document.getElementById("modal-desc")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : ""; }; + Modal.prototype.radiusField = function () { + var _a, _b; + return (_b = (_a = document.getElementById("modal-radius")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : ""; + }; Modal.prototype.visible = function () { var _a; return ((_a = this.self()) === null || _a === void 0 ? void 0 : _a.style.display) != "none";

@@ -209,9 +217,9 @@ }

}; Modal.prototype.setState = function (state, args) { var _this = this; + var title = this.title(); switch (state) { case OverlayType.POINT: - var title = this.title(); if (title) { title.innerHTML = "Add Marker"; }

@@ -236,6 +244,33 @@ }

}); break; case OverlayType.CIRCLE: + if (title) { + title.innerHTML = "Add Circle"; + } + fetch("/static/pointModal.html") + .then(function (r) { return r.text(); }) + .then(function (t) { + var content = _this.content(); + if (content) { + content.innerHTML = t; + } + var radiusContainer = _this.radiusContainer(); + if (radiusContainer) { + radiusContainer.style.display = "block"; + } + var submitBtn = _this.submitBtn(); + if (submitBtn) { + submitBtn.onclick = function () { + var name = _this.nameField(); + var desc = _this.descField(); + var radius = _this.radiusField(); + var circle = new Circle(name, desc, args.latlng, { radius: Number(radius) }); + circle.add(args.map); + args.overlays.circles.push(circle); + _this.setVisible(false); + }; + } + }); break; case OverlayType.POLYGON: break;

@@ -265,6 +300,16 @@ overlays.markers.forEach(function (m) { return m.add(map); });

overlays.circles.forEach(function (m) { return m.add(map); }); overlays.polygons.forEach(function (m) { return m.add(map); }); var modal = new Modal(); + var resetMapClick = function () { + try { + map.off("click", addMarkerHandler); + } + catch (_a) { } + try { + map.off("click", addCircleHandler); + } + catch (_b) { } + }; var addMarkerHandler = function (e) { modal.setVisible(true); modal.setState(OverlayType.POINT, {

@@ -274,15 +319,27 @@ overlays: overlays

}); map.off("click", addMarkerHandler); }; + var addCircleHandler = function (e) { + modal.setVisible(true); + modal.setState(OverlayType.CIRCLE, { + latlng: e.latlng, + map: map, + overlays: overlays + }); + map.off("click", addCircleHandler); + }; var addMarkerBtn = document.getElementById("addPoint-btn"); if (addMarkerBtn) { addMarkerBtn.onclick = function (e) { - try { - map.off("click", addMarkerHandler); - } - finally { - map.on("click", addMarkerHandler); - } + resetMapClick(); + map.on("click", addMarkerHandler); + }; + } + var addCircleBtn = document.getElementById("addCircle-btn"); + if (addCircleBtn) { + addCircleBtn.onclick = function (e) { + resetMapClick(); + map.on("click", addCircleHandler); }; } var saveBtn = document.getElementById("save-btn");
M static/pointModal.htmlstatic/pointModal.html

@@ -1,5 +1,9 @@

-<label for="modal-name">Name</label> -<input type="text" id="modal-name"> -<label for="modal-desc">Description</label> -<textarea id="modal-desc"></textarea> +<label for="modal-name">Name</label><br/> +<input type="text" id="modal-name"><br/> +<label for="modal-desc">Description</label><br/> +<textarea id="modal-desc"></textarea><br/> +<div id="radius-container"> +<label for="modal-radius">Radius (meters)</label><br/> +<input type="number" step="1" id="modal-radius" value="500"><br/> +</div> <button id="modal-submitBtn">Add</button>
M static/style.cssstatic/style.css

@@ -47,15 +47,16 @@ z-index: 1;

} #modal-container { - background: #222222; + background: #000000; color: #c9c9c9; position: fixed; - width: 1200px; - max-width: 80vw; + width: 100%; + max-width: 800px; top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0); - max-height: 80%; + height: auto; + max-height: calc(100vh - 2.5em); z-index: 4; text-align: left; display: none;

@@ -78,23 +79,24 @@ #modal-content label{

display: block; float: left; text-transform: uppercase; - background: dimgray; - color: black; + color: white; } -#modal-content input[type="text"], #modal-content textarea { +#modal-content #radius-container { + display: none; +} + +#modal-content input[type="text"], #modal-content textarea, #modal-content input[type="number"] { display: block; width: 100%; font-size: 150%; - color: #c9c9c9; - background: transparent; + color: white; border: none; - border: solid 2px dimgray; margin-bottom: 1em; font-family: sans-serif; box-sizing: border-box; padding: 0.5em; - background: rgba(0, 0, 0, 0.5); + background: #222222; }

@@ -103,6 +105,12 @@ resize: none;

height: 8em; } + +#modal-content input[type="number"] { + width: 12ch; + text-align: right; +} + button.closeBtn { float: right; background: transparent;

@@ -120,13 +128,48 @@ }

button#modal-submitBtn { font-size: 150%; - background: black; - color: #c9c9c9; - border: none; - border-left: solid 2px dimgray; - border-right: solid 2px dimgray; + background: transparent;; + color: white; + border: solid 2px dimgray; position: relative; margin-left: auto; padding: 0.25em; text-transform: uppercase; +} + +button#modal-submitBtn:hover { + background: white; + color: black; + border: solid 2px white; +} + +#cancel-container, #confirm-container { + position: fixed; + font-size: 200%; + bottom: 1.5em; + display: none; + left: 50%; + transform: translateY(-50%); + background: black; +} + +#import-export-container { + position: fixed; + width: 100%; + max-width: 800px; + top: 50%; + left: 50%; + transform: translate3d(-50%, -50%, 0); + height: calc(100vh - 2.5em); + max-height: 600px; + background: black; +} + +#overlays-menu-container { + position: fixed; + height: calc(100vh - 2.5em); + right: 0; + width: 100%; + max-width: 800px; + background: black; }