From 329c1c221517fed921f13d63f679b3a48d3a452a Mon Sep 17 00:00:00 2001 From: ThePetrovich Date: Mon, 30 Jun 2025 19:23:46 +0800 Subject: [PATCH 1/2] New panel layout --- package-lock.json | 16 +- package.json | 5 +- src/app.html | 4 + src/lib/components/ControlPanel.svelte | 316 ++++++++++++++++++ .../map.svelte => lib/components/Map.svelte} | 12 +- src/lib/components/Navbar.svelte | 102 ++++++ src/lib/components/PanelContainer.svelte | 14 + src/lib/components/ScenarioPanel.svelte | 0 src/lib/components/TabComponent.svelte | 50 +++ src/lib/components/TelemetryPanel.svelte | 97 ++++++ src/{routes => lib/components}/Toast.svelte | 0 .../components}/WindVisualisation.svelte | 2 +- src/routes/ControlPanel.svelte | 296 ---------------- src/routes/Navbar.svelte | 109 ------ src/routes/TelemetryPanel.svelte | 74 ---- src/routes/predict/+page.svelte | 42 ++- src/routes/track/+page.svelte | 6 +- static/css/custom.css | 41 ++- 18 files changed, 671 insertions(+), 515 deletions(-) create mode 100644 src/lib/components/ControlPanel.svelte rename src/{routes/map.svelte => lib/components/Map.svelte} (93%) create mode 100644 src/lib/components/Navbar.svelte create mode 100644 src/lib/components/PanelContainer.svelte create mode 100644 src/lib/components/ScenarioPanel.svelte create mode 100644 src/lib/components/TabComponent.svelte create mode 100644 src/lib/components/TelemetryPanel.svelte rename src/{routes => lib/components}/Toast.svelte (100%) rename src/{routes => lib/components}/WindVisualisation.svelte (99%) delete mode 100644 src/routes/ControlPanel.svelte delete mode 100644 src/routes/Navbar.svelte delete mode 100644 src/routes/TelemetryPanel.svelte diff --git a/package-lock.json b/package-lock.json index a86bf92..7e00222 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,11 @@ "version": "0.0.1", "dependencies": { "@sveltestrap/sveltestrap": "^7.1.0", - "bootstrap-icons": "^1.11.3", + "bootstrap-icons": "^1.13.1", "js-cookie": "^3.0.5", "leaflet": "^1.9.4", - "leaflet-velocity": "^2.1.4" + "leaflet-velocity": "^2.1.4", + "leaflet.heat": "^0.2.0" }, "devDependencies": { "@sveltejs/adapter-auto": "^4.0.0", @@ -894,9 +895,9 @@ } }, "node_modules/bootstrap-icons": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz", - "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", + "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==", "funding": [ { "type": "github", @@ -1098,6 +1099,11 @@ "resolved": "https://registry.npmjs.org/leaflet-velocity/-/leaflet-velocity-2.1.4.tgz", "integrity": "sha512-uTmSb2/Kn28S0itlmJBMy2ZRKsisWUr2wm9rtkKXjpq9Sai7tqKdTRHKfLgTOgEdWFf5Ctt2bQoB7kb50qC7eg==" }, + "node_modules/leaflet.heat": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/leaflet.heat/-/leaflet.heat-0.2.0.tgz", + "integrity": "sha512-Cd5PbAA/rX3X3XKxfDoUGi9qp78FyhWYurFg3nsfhntcM/MCNK08pRkf4iEenO1KNqwVPKCmkyktjW3UD+h9bQ==" + }, "node_modules/locate-character": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", diff --git a/package.json b/package.json index db0e69e..52e8a81 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,10 @@ }, "dependencies": { "@sveltestrap/sveltestrap": "^7.1.0", - "bootstrap-icons": "^1.11.3", + "bootstrap-icons": "^1.13.1", "js-cookie": "^3.0.5", "leaflet": "^1.9.4", - "leaflet-velocity": "^2.1.4" + "leaflet-velocity": "^2.1.4", + "leaflet.heat": "^0.2.0" } } diff --git a/src/app.html b/src/app.html index a01612f..e1deb10 100644 --- a/src/app.html +++ b/src/app.html @@ -6,6 +6,10 @@ + %sveltekit.head% diff --git a/src/lib/components/PanelContainer.svelte b/src/lib/components/PanelContainer.svelte new file mode 100644 index 0000000..d041189 --- /dev/null +++ b/src/lib/components/PanelContainer.svelte @@ -0,0 +1,14 @@ + + +
+ +
\ No newline at end of file diff --git a/src/lib/components/ScenarioPanel.svelte b/src/lib/components/ScenarioPanel.svelte new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/components/TabComponent.svelte b/src/lib/components/TabComponent.svelte new file mode 100644 index 0000000..594f4d0 --- /dev/null +++ b/src/lib/components/TabComponent.svelte @@ -0,0 +1,50 @@ + + +
+ {#each tabs as tab (tab.id)} + + {/each} +
+ + \ No newline at end of file diff --git a/src/lib/components/TelemetryPanel.svelte b/src/lib/components/TelemetryPanel.svelte new file mode 100644 index 0000000..7a69ca4 --- /dev/null +++ b/src/lib/components/TelemetryPanel.svelte @@ -0,0 +1,97 @@ + + + + + Последние данные телеметрии + + + {#if !isCollapsed} + + + + + + + + + + + + + + + + + + + + + + + {/if} + + diff --git a/src/routes/Toast.svelte b/src/lib/components/Toast.svelte similarity index 100% rename from src/routes/Toast.svelte rename to src/lib/components/Toast.svelte diff --git a/src/routes/WindVisualisation.svelte b/src/lib/components/WindVisualisation.svelte similarity index 99% rename from src/routes/WindVisualisation.svelte rename to src/lib/components/WindVisualisation.svelte index b028916..386c503 100644 --- a/src/routes/WindVisualisation.svelte +++ b/src/lib/components/WindVisualisation.svelte @@ -96,7 +96,7 @@ displayValues: true, displayOptions: { velocityType: 'Wind Speed', - position: 'bottomleft', + position: 'bottomright', emptyString: 'No wind data', }, data: windData diff --git a/src/routes/ControlPanel.svelte b/src/routes/ControlPanel.svelte deleted file mode 100644 index 5288255..0000000 --- a/src/routes/ControlPanel.svelte +++ /dev/null @@ -1,296 +0,0 @@ - - -
- - - - - - {#if !isCollapsed} - - - - - - {#each Object.keys(PROFILE_MAP) as profileName} - - {/each} - - - - - - - - - - - - - - - - - - - - - - / - - - - - - - - - - - - - - -
- - - - - - - - -
- -
- - - - - - - - -
- - - - - - -
- - - -
-
- {/if} -
-
diff --git a/src/routes/Navbar.svelte b/src/routes/Navbar.svelte deleted file mode 100644 index 3197208..0000000 --- a/src/routes/Navbar.svelte +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - diff --git a/src/routes/TelemetryPanel.svelte b/src/routes/TelemetryPanel.svelte deleted file mode 100644 index 20e9b19..0000000 --- a/src/routes/TelemetryPanel.svelte +++ /dev/null @@ -1,74 +0,0 @@ - - -
-
-
Последние данные телеметрии
- -
- {#if !isCollapsed} -
-
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
-
- {/if} -
- - diff --git a/src/routes/predict/+page.svelte b/src/routes/predict/+page.svelte index 54e13d0..667c46e 100644 --- a/src/routes/predict/+page.svelte +++ b/src/routes/predict/+page.svelte @@ -1,17 +1,21 @@ + +
- + + + +
+ {#if activeTab === 'control'} + + {:else if activeTab === 'telemetry'} + + {/if} +
+
diff --git a/src/routes/track/+page.svelte b/src/routes/track/+page.svelte index d056932..810dd26 100644 --- a/src/routes/track/+page.svelte +++ b/src/routes/track/+page.svelte @@ -1,7 +1,7 @@ -
+
-
\ No newline at end of file +
diff --git a/src/lib/components/ScenarioPanel.svelte b/src/lib/components/ScenarioPanel.svelte index e69de29..4791ced 100644 --- a/src/lib/components/ScenarioPanel.svelte +++ b/src/lib/components/ScenarioPanel.svelte @@ -0,0 +1,115 @@ + + + + + + + + {#if !isCollapsed} + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ {/if} +
diff --git a/src/lib/ext/leaflet-ruler/leaflet-ruler.ts b/src/lib/ext/leaflet-ruler/leaflet-ruler.ts new file mode 100644 index 0000000..40ef2be --- /dev/null +++ b/src/lib/ext/leaflet-ruler/leaflet-ruler.ts @@ -0,0 +1,286 @@ +import * as L from "leaflet"; +import { distHaversine, bearingHaversine } from "$lib/mathutil"; + +// Define an interface for the control's options for type safety. +export interface RulerOptions extends L.ControlOptions { + events?: { + onToggle?: (isActive: boolean) => void; + }; + circleMarker?: L.CircleMarkerOptions; + lineStyle?: L.PolylineOptions; + lengthUnit?: { + display?: string; + decimal?: number; + factor?: number | null; + label?: string; + }; + angleUnit?: { + display?: string; + decimal?: number; + factor?: number | null; + label?: string; + }; +} + +// Define an interface for the measurement result. +interface MeasurementResult { + Bearing: number; + Distance: number; +} + +// Use a modern TypeScript class that extends L.Control. +export class Ruler extends L.Control { + // Override the default options with our custom ones. + public options: RulerOptions = { + position: "topright", + events: { + onToggle: () => {}, + }, + circleMarker: { + color: "red", + radius: 2, + }, + lineStyle: { + color: "red", + dashArray: "1,6", + }, + lengthUnit: { + display: "km", + decimal: 2, + factor: null, + label: "Distance:", + }, + angleUnit: { + display: "°", + decimal: 2, + factor: null, + label: "Bearing:", + }, + }; + + // Declare class properties with types. + private _lastClickTime = 0; + private _map?: L.Map; + private _container?: HTMLElement; + private _choice = false; + private _defaultCursor = ""; + private _allLayers: L.LayerGroup = L.layerGroup(); + private _clickedLatLong: L.LatLng | null = null; + private _clickedPoints: L.LatLng[] = []; + private _totalLength = 0; + private _clickCount = 0; + private _tempLine: L.FeatureGroup = L.featureGroup(); + private _tempPoint: L.FeatureGroup = L.featureGroup(); + private _pointLayer: L.FeatureGroup = L.featureGroup(); + private _polylineLayer: L.FeatureGroup = L.featureGroup(); + private _movingLatLong: L.LatLng | null = null; + private _result: MeasurementResult = { Bearing: 0, Distance: 0 }; + private _addedLength = 0; + + constructor(options?: RulerOptions) { + super(options); + L.Util.setOptions(this, options); + } + + public isActive(): boolean { + return this._choice; + } + + public onAdd(map: L.Map): HTMLElement { + this._map = map; + this._container = L.DomUtil.create("div", "leaflet-bar leaflet-ruler"); + L.DomEvent.disableClickPropagation(this._container); + L.DomEvent.on(this._container, "click", this._toggleMeasure, this); + this._defaultCursor = this._map.getContainer().style.cursor; + this._allLayers = L.layerGroup(); + return this._container; + } + + public onRemove(): void { + if (this._container) { + L.DomEvent.off(this._container, "click", this._toggleMeasure, this); + } + if (this._choice) { + this._toggleMeasure(); // Turn off measurements + } + } + + private _toggleMeasure(): void { + this._choice = !this._choice; + this.options.events?.onToggle?.(this._choice); + + this._clickedLatLong = null; + this._clickedPoints = []; + this._totalLength = 0; + + if (!this._map || !this._container) return; + + const mapContainer = this._map.getContainer(); + + if (this._choice) { + this._map.doubleClickZoom.disable(); + L.DomEvent.on(mapContainer, "keydown", this._escape, this); + L.DomEvent.on(mapContainer, "dblclick", this._closePath, this); + this._container.classList.add("leaflet-ruler-clicked"); + this._clickCount = 0; + this._tempLine = L.featureGroup().addTo(this._allLayers); + this._tempPoint = L.featureGroup().addTo(this._allLayers); + this._pointLayer = L.featureGroup().addTo(this._allLayers); + this._polylineLayer = L.featureGroup().addTo(this._allLayers); + this._allLayers.addTo(this._map); + mapContainer.style.cursor = "crosshair"; + this._map.on("click", this._clicked, this); + this._map.on("mousemove", this._moving, this); + } else { + this._map.doubleClickZoom.enable(); + L.DomEvent.off(mapContainer, "keydown", this._escape, this); + L.DomEvent.off(mapContainer, "dblclick", this._closePath, this); + this._container.classList.remove("leaflet-ruler-clicked"); + this._map.removeLayer(this._allLayers); + this._allLayers = L.layerGroup(); + mapContainer.style.cursor = this._defaultCursor; + this._map.off("click", this._clicked, this); + this._map.off("mousemove", this._moving, this); + } + } + + private _clicked(e: L.LeafletMouseEvent): void { + // hack to prevent adding the same point twice on double click + let clickTime = Date.now(); + if (clickTime - this._lastClickTime < 200) { + this._closePath(); + return; + } + + this._lastClickTime = clickTime; + + this._clickedLatLong = e.latlng; + this._clickedPoints.push(this._clickedLatLong); + L.circleMarker(this._clickedLatLong, this.options.circleMarker).addTo(this._pointLayer); + + if (this._clickCount > 0 && !e.latlng.equals(this._clickedPoints[this._clickedPoints.length - 2], 0.0001)) { + if (this._movingLatLong) { + L.polyline( + [this._clickedPoints[this._clickCount - 1], this._movingLatLong], + this.options.lineStyle + ).addTo(this._polylineLayer); + } + let text: string; + this._totalLength += this._result.Distance; + const angleUnit = this.options.angleUnit!; + const lengthUnit = this.options.lengthUnit!; + + if (this._clickCount > 1) { + text = `${angleUnit.label} ${this._result.Bearing.toFixed(angleUnit.decimal)} ${ + angleUnit.display + }
${lengthUnit.label} ${this._totalLength.toFixed(lengthUnit.decimal)} ${ + lengthUnit.display + }`; + } else { + text = `${angleUnit.label} ${this._result.Bearing.toFixed(angleUnit.decimal)} ${ + angleUnit.display + }
${lengthUnit.label} ${this._result.Distance.toFixed(lengthUnit.decimal)} ${ + lengthUnit.display + }`; + } + L.circleMarker(this._clickedLatLong, this.options.circleMarker) + .bindTooltip(text, { permanent: true, className: "result-tooltip" }) + .addTo(this._pointLayer) + .openTooltip(); + } + this._clickCount++; + } + + private _moving(e: L.LeafletMouseEvent): void { + if (this._clickedLatLong && this._map) { + this._movingLatLong = e.latlng; + + this._tempLine.clearLayers(); + this._tempPoint.clearLayers(); + + this._calculateBearingAndDistance(); + this._addedLength = this._result.Distance + this._totalLength; + + L.polyline([this._clickedLatLong, this._movingLatLong], this.options.lineStyle).addTo(this._tempLine); + + const angleUnit = this.options.angleUnit!; + const lengthUnit = this.options.lengthUnit!; + let text: string; + + if (this._clickCount > 1) { + text = `${angleUnit.label} ${this._result.Bearing.toFixed(angleUnit.decimal)} ${ + angleUnit.display + }
${lengthUnit.label} ${this._addedLength.toFixed(lengthUnit.decimal)} ${ + lengthUnit.display + }
(+${this._result.Distance.toFixed(lengthUnit.decimal)})
`; + } else { + text = `${angleUnit.label} ${this._result.Bearing.toFixed(angleUnit.decimal)} ${ + angleUnit.display + }
${lengthUnit.label} ${this._result.Distance.toFixed(lengthUnit.decimal)} ${ + lengthUnit.display + }`; + } + L.circleMarker(this._movingLatLong, this.options.circleMarker) + .bindTooltip(text, { sticky: true, offset: L.point(0, -40), className: "moving-tooltip" }) + .addTo(this._tempPoint) + .openTooltip(); + } + } + + private _escape(e: Event): void { + if ((e as KeyboardEvent).key === "Escape") { + if (this._clickCount > 0) { + this._closePath(); + } else { + this._toggleMeasure(); + } + } + } + + private _calculateBearingAndDistance(): void { + if (!this._clickedLatLong || !this._movingLatLong) return; + + const f1 = this._clickedLatLong.lat; + const l1 = this._clickedLatLong.lng; + const f2 = this._movingLatLong.lat; + const l2 = this._movingLatLong.lng; + + const angleUnit = this.options.angleUnit!; + const lengthUnit = this.options.lengthUnit!; + + const brng = bearingHaversine({ lat: f1, lng: l1 }, { lat: f2, lng: l2 }); + + const distance = distHaversine({ lat: f1, lng: l1 }, { lat: f2, lng: l2 }); + + if (angleUnit.factor) { + this._result.Bearing = brng * angleUnit.factor; + } else { + this._result.Bearing = brng; + } + + if (lengthUnit.factor) { + this._result.Distance = distance * lengthUnit.factor; + } else { + this._result.Distance = distance; + } + + this._result = { + Bearing: brng, + Distance: distance, + }; + } + + private _closePath(): void { + if (!this._map || !this._container) return; + + this._map.removeLayer(this._tempLine); + this._map.removeLayer(this._tempPoint); + this._choice = false; + this._toggleMeasure(); + } +} + +// Factory function for creating the control, maintaining the Leaflet convention. +export const ruler = (options?: RulerOptions) => { + return new Ruler(options); +}; diff --git a/src/lib/mathutil.ts b/src/lib/mathutil.ts index 75bcebe..eee4bac 100644 --- a/src/lib/mathutil.ts +++ b/src/lib/mathutil.ts @@ -2,7 +2,7 @@ export function distHaversine( p1: { lat: number; lng: number }, p2: { lat: number; lng: number }, precision?: number -): string { +): number { const R = 6371; // Earth's mean radius in km const rad = (x: number): number => (x * Math.PI) / 180; @@ -20,5 +20,20 @@ export function distHaversine( const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); const d = R * c; - return d.toFixed(precision ?? 3); + return precision ? parseFloat(d.toFixed(precision)) : d; +} + +export function bearingHaversine( + p1: { lat: number; lng: number }, + p2: { lat: number; lng: number } +): number { + const rad = (x: number): number => (x * Math.PI) / 180; + + const dLong = rad(p2.lng - p1.lng); + const y = Math.sin(dLong) * Math.cos(rad(p2.lat)); + const x = + Math.cos(rad(p1.lat)) * Math.sin(rad(p2.lat)) - + Math.sin(rad(p1.lat)) * Math.cos(rad(p2.lat)) * Math.cos(dLong); + + return (Math.atan2(y, x) * 180) / Math.PI; } \ No newline at end of file diff --git a/src/lib/types.ts b/src/lib/types.ts index 34029d5..388f8d0 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -23,6 +23,11 @@ export interface FlightParameters { version: number; } +export interface Point { + latlng: LatLngLiteral & { alt: number }; + datetime: Date; +} + export interface TelemetryPoint { altitude: number; datetime: string; @@ -42,11 +47,8 @@ export interface RawTelemetry { } export interface Telemetry { - flight_path: [number, number, number][]; - launch: { - latlng: LatLngExpression; - datetime: Date; - }; + flight_path: LatLngExpression[]; + launch: Point; datapoints: TelemetryPoint[]; } @@ -74,38 +76,10 @@ export interface RawPrediction { } export interface Prediction { - flight_path: [number, number, number][]; - launch: { - latlng: LatLngExpression; - datetime: Date; - }; - burst: { - latlng: LatLngExpression; - datetime: Date; - }; - landing: { - latlng: LatLngExpression; - datetime: Date; - }; + flight_path: LatLngExpression[]; + launch: Point; + burst: Point; + landing: Point; profile: string; flight_time: number; } - -export interface Point { - latlng: LatLngLiteral & { alt: number }; - datetime: Date; -} - -export interface PredictionData { - launch: Point; - landing: Point; - burst: Point; - flight_path: LatLngExpression[]; - flight_time: number; -} - -export interface TelemetryData { - launch: Point; - datapoints: TelemetryPoint[]; - flight_path: LatLngExpression[]; -} \ No newline at end of file diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index f961cbf..cbb97e2 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,5 +1,5 @@
diff --git a/src/routes/predict/+page.svelte b/src/routes/predict/+page.svelte index 667c46e..62fc819 100644 --- a/src/routes/predict/+page.svelte +++ b/src/routes/predict/+page.svelte @@ -4,6 +4,7 @@ import Navbar from "$lib/components/Navbar.svelte"; import PanelContainer from "$lib/components/PanelContainer.svelte"; import TelemetryPanel from '$lib/components/TelemetryPanel.svelte'; + import ScenarioPanel from "$lib/components/ScenarioPanel.svelte"; import TabComponent from "$lib/components/TabComponent.svelte"; import { onMount } from "svelte"; import { PredictionStore } from "$lib/stores"; @@ -13,9 +14,10 @@ import L from "leaflet"; let map: Map | null = null; - let panel: PanelContainer | null = null; + let panelContainer: PanelContainer | null = null; + let controlPanel: ControlPanel | null = null; let selectionToastId: string | null = null; - let activeTab: 'control' | 'telemetry' = 'control'; + let activeTab: 'control' | 'scenario' | 'settings' | 'about' = 'scenario'; onMount(() => { PredictionStore.subscribe((data) => { @@ -24,10 +26,11 @@ } }); console.log("ControlPanel mounted"); - console.log(panel); + console.log(panelContainer); - if (panel) { - let element = panel.getElement(); + if (panelContainer) { + let element = panelContainer.getElement(); + if (!element) return; L.DomEvent.disableClickPropagation(element); L.DomEvent.disableScrollPropagation(element); } @@ -55,27 +58,23 @@ function handleCoordinateSelection(event: CustomEvent<{ lat: number; lng: number }>) { const { lat, lng } = event.detail; - panel?.updateLaunchPosition(lat, lng); + controlPanel?.updateLaunchPosition(lat, lng); console.log(`Selected coordinates: ${lat}, ${lng}`); if (selectionToastId) { removeToast(selectionToastId); selectionToastId = null; } } - - - -
- + {#if activeTab === 'control'} - - {:else if activeTab === 'telemetry'} - + + {:else if activeTab === 'scenario'} + + {:else if activeTab === 'settings'} + + {:else if activeTab === 'about'} + {/if} diff --git a/src/routes/user/account/+page.svelte b/src/routes/user/account/+page.svelte index 9d97fbd..f8ba827 100644 --- a/src/routes/user/account/+page.svelte +++ b/src/routes/user/account/+page.svelte @@ -1,5 +1,5 @@
diff --git a/static/css/custom.css b/static/css/custom.css index dc82f73..e716523 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -92,6 +92,28 @@ border-radius: var(--bs-border-radius) !important; } +.leaflet-tooltip-top::before { + border-top-color: var(--bs-border-color) !important; +} +.leaflet-tooltip-bottom::before { + border-bottom-color: var(--bs-border-color) !important; +} +.leaflet-tooltip-left::before { + border-left-color: var(--bs-border-color) !important; +} +.leaflet-tooltip-right::before { + border-right-color: var(--bs-border-color) !important; +} + +.leaflet-tooltip { + background-color: var(--bs-body-bg) !important; + border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; + border-radius: var(--bs-border-radius) !important; + color: var(--bs-body-color); + box-shadow: none !important; +} + + @media (max-width: 767.98px) { .coordinates-display { diff --git a/static/ext/leaflet-ruler/icon.png b/static/ext/leaflet-ruler/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..028741e9f3e82d798d46b677fc9ad9badbf28159 GIT binary patch literal 756 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5Sk5Uj7}P}D}aLRC7!;n>~M1xcvI-p~V4gYs(JQ z{XAtN9r~!Fyl&UZ%d3wTE1&zZ__mqi*`yAyr0&~kb0*KNb6#3@|8DTZ8;b&;_ugFT za_9EWuwMOh%N`wlW%BCtJ!f&5pczKXYeEItSgm+Ee|0yrnVz_ov|erJq_V~pwN*7U z&Z?=lh-wy|?y&vrztZSgO4t4?YDTx$eGarM;?3A<)Uf`O)9j8JRTXbGFW#}p-(rIO zbv>oD1FENbPH#TM_c-bQL(W90ElsCqF;y)5S8}d)Z`kQ?Cx0>g^KrLI4_WPd?Z?Dp z+NF`wM`i}Ku4TKttUcCL_Q=m=T)TNwZJw>ncAO*lcbm5H+A7OA(os(mZyaH&tJU<2 z;kS7EqsW3)EjMJ!t+StW44IdSaIewZf8=>(dY8J2Y7?{Ru_^kG5)CI`V|DkdP?&Y_ zVc+4ICR3u`-+D7~hm8M}T^VdwPFcp ztHiBAvB&WPP=h4MhT#0PlJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@~pS7(8A5T-G@y GGywn@4lo7) literal 0 HcmV?d00001 diff --git a/static/ext/leaflet-ruler/leaflet-ruler.css b/static/ext/leaflet-ruler/leaflet-ruler.css new file mode 100644 index 0000000..69927c3 --- /dev/null +++ b/static/ext/leaflet-ruler/leaflet-ruler.css @@ -0,0 +1,41 @@ +.leaflet-ruler{ + height: 35px; + width: 35px; + background-image: url("./icon.png"); /*
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
*/ + background-repeat: no-repeat; + background-position: center; +} +.leaflet-ruler:hover{ + background-image: url("./icon.png"); /*
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
*/ +} +.leaflet-ruler-clicked{ + height: 35px; + width: 35px; + background-repeat: no-repeat; + background-position: center; + background-image: url("./icon.png"); + border-color: chartreuse !important; +} +.leaflet-bar{ + background-color: #ffffff; +} +.leaflet-control { + cursor: pointer; +} +.result-tooltip{ + background-color: white; + border-width: medium; + border-color: #de0000; + font-size: smaller; +} +.moving-tooltip{ + background-color: rgba(255, 255, 255, .7); + background-clip: padding-box; + opacity: 0.5; + border: dotted; + border-color: red; + font-size: smaller; +} +.plus-length{ + padding-left: 45px; +} \ No newline at end of file