UNPKG

@trailstash/ultra

Version:

A web based tool for making MapLibre GL maps with data from sources such as Overpass, GeoJSON, GPX, KML, TCX, etc

260 lines (246 loc) 6.66 kB
import equal from "deep-equal"; import pick from "lodash.pick"; import { compressToEncodedURIComponent } from "lz-string"; import { h } from "../lib/dom.js"; import { setBaseStyle } from "../lib/style.js"; import { setSetting, parseSettings } from "../lib/settings.js"; import { getAutoRunFromQueryParams, getOptionsFromQueryParams, getQueryFromQueryParams, toQueryParams, } from "../lib/queryParams.js"; import { localStorage } from "../lib/localStorage.js"; import { UltraMap } from "./ultra-map.js"; import { HelpModal } from "./help-modal.js"; import { StylePicker } from "./style-picker.js"; import { alertOnError } from "../lib/error.js"; const style = new CSSStyleSheet(); style.replaceSync(` :host { height: 100%; width: 100%; display: flex; flex-direction: column; } main { display: flex; flex-direction: row; flex-grow: 1; } code-editor { width: 45%; border: none; border-right: 1px solid #8f8f9d; resize: horizontal; overflow: auto; } ultra-map { flex-grow: 1; height: 100%; } @media (max-width: 900px) { main { flex-direction: column; } code-editor { width: auto; height: 45%; resize: vertical; } } code-editor.fs { display: none; } help-modal { float: right; } .fs help-modal, .fs run-button { z-index: 1; display: block; position: fixed; top: 4px; left: 4px; } :host(:fullscreen) code-editor, :host(:fullscreen) nav-bar { display: none; } run-button, help-modal { margin-right: 4px; } `); export class UltraApp extends HTMLElement { query = ""; settings = {}; help; runButton = true; static MAP_INIT_SETTINGS = [ "loadSettingsFromQueryParams", "options", "controls", "mapStyle", "queryProviders", "persistState", ]; static props = ["settings", "query", "help"]; constructor() { super(); // Bind methods this.onClickRun = this.onClickRun.bind(this); this.onClickAbort = this.onClickAbort.bind(this); this.resetMap = this.resetMap.bind(this); this.onMoveEnd = this.onMoveEnd.bind(this); } async connectedCallback() { // Get initial query and center&zoom try { this.query = (await Promise.resolve(getQueryFromQueryParams()).catch( alertOnError, )) || this.query; } catch (e) { alert(e.message); } this.settings = { ...this.settings, loadSettingsFromQueryParams: false, }; const querySettings = parseSettings(this.query); delete querySettings.query; if (querySettings.title) { this.settings.title = querySettings.title; document.title = this.settings.title; } if (querySettings.icon) { this.settings.icon = querySettings.icon; document.querySelector("[rel=icon]").href = this.settings.icon; } if (querySettings.controls) { this.settings.controls = querySettings.controls; } if (querySettings.options) { this.settings.options = querySettings.options; } if (querySettings.help) { this.help = querySettings.help; } if (typeof querySettings.runButton !== "undefined") { this.runButton = !!querySettings.runButton; } // Create & populate Shadow DOM const shadow = this.attachShadow({ mode: "open" }); shadow.adoptedStyleSheets.push(style); shadow.appendChild( h( "nav-bar", { mode: "app", title: this.settings.title, icon: this.settings.icon }, h( "span", { slot: "buttons" }, ...(this.runButton ? [ h("run-button", { colors: { run: "white", abort: "white", text: "black", }, text: { run: "Reload", abort: "Cancel", }, icons: { run: "rotate", abort: "xmark", }, }), ] : []), ...(this.help ? [ h("help-modal", { help: this.help, minimal: true, }), ] : []), ), ), ); shadow.appendChild( h( "main", {}, h("ultra-map", pick(this.settings, UltraApp.MAP_INIT_SETTINGS)), ), ); this.refs = { navBar: shadow.querySelector("nav-bar"), ultraMap: shadow.querySelector("ultra-map"), runButton: shadow.querySelector("run-button"), stylePicker: shadow.querySelector("style-picker"), }; // Setup Event Listeners this.refs.runButton?.addEventListener("run", this.onClickRun); this.refs.runButton?.addEventListener("abort", this.onClickAbort); this.refs.ultraMap.map.on("moveend", this.onMoveEnd); this.refs.ultraMap.map.once("idle", this.onMoveEnd); this.onClickRun(); } async onClickRun() { if (this.refs.runButton) this.refs.runButton.loading = true; // Loading button state this.resetMap(); // clear settings from previous run try { // basic query/option parsing const settings = { ...this.settings, ...parseSettings(this.query), }; Object.assign(this.refs.ultraMap, { ...pick(settings, UltraMap.RUNTIME_SETTINGS), mapStyle: setBaseStyle( settings.mapStyle, this.settings.mapStyle || UltraMap.defaults.mapStyle, ), }); this.controller = new AbortController(); const { data, mapStyle } = await this.refs.ultraMap.run(this.controller); delete this.controller; if (this.refs.runButton) this.refs.runButton.loading = false; } catch (err) { this.resetMap(); this.refs.ultraMap.run(); if (this.refs.runButton) this.refs.runButton.loading = false; if (err.name != "AbortError") { alert(err); throw err; } } } onClickAbort() { if (this.controller) { this.controller.abort(); delete this.controller; } this.resetMap(); this.refs.ultraMap.run(); if (this.refs.runButton) this.refs.runButton.loading = false; } resetMap() { Object.assign( this.refs.ultraMap, pick( { ...UltraMap.defaults, mapStyle: this.settings.mapStyle || UltraMap.defaults.mapStyle, }, UltraMap.RUNTIME_SETTINGS, ), ); } onMoveEnd() { const zoom = this.refs.ultraMap.zoom; const center = this.refs.ultraMap.center.toArray(); } }