diff --git a/package-lock.json b/package-lock.json
index a86bf92..844ec4e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,10 +9,12 @@
"version": "0.0.1",
"dependencies": {
"@sveltestrap/sveltestrap": "^7.1.0",
- "bootstrap-icons": "^1.11.3",
+ "@types/leaflet": "^1.9.19",
+ "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",
@@ -866,6 +868,19 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
},
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="
+ },
+ "node_modules/@types/leaflet": {
+ "version": "1.9.19",
+ "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.19.tgz",
+ "integrity": "sha512-pB+n2daHcZPF2FDaWa+6B0a0mSDf4dPU35y5iTXsx7x/PzzshiX5atYiS1jlBn43X7XvM8AP+AB26lnSk0J4GA==",
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
"node_modules/acorn": {
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
@@ -894,9 +909,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 +1113,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..9d82df6 100644
--- a/package.json
+++ b/package.json
@@ -22,9 +22,11 @@
},
"dependencies": {
"@sveltestrap/sveltestrap": "^7.1.0",
- "bootstrap-icons": "^1.11.3",
+ "@types/leaflet": "^1.9.19",
+ "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..3ba5379 100644
--- a/src/app.html
+++ b/src/app.html
@@ -4,8 +4,15 @@
+
+
+
+
%sveltekit.head%
diff --git a/src/lib/components/PanelContainer.svelte b/src/lib/components/PanelContainer.svelte
new file mode 100644
index 0000000..3a9bb43
--- /dev/null
+++ b/src/lib/components/PanelContainer.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/src/lib/components/ScenarioPanel.svelte b/src/lib/components/ScenarioPanel.svelte
new file mode 100644
index 0000000..4791ced
--- /dev/null
+++ b/src/lib/components/ScenarioPanel.svelte
@@ -0,0 +1,115 @@
+
+
+
+
+ {#if !isCollapsed}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/if}
+
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/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/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 @@
-
-
-
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..62fc819 100644
--- a/src/routes/predict/+page.svelte
+++ b/src/routes/predict/+page.svelte
@@ -1,17 +1,23 @@
diff --git a/static/css/custom.css b/static/css/custom.css
index b9ce25f..e716523 100644
--- a/static/css/custom.css
+++ b/static/css/custom.css
@@ -2,26 +2,26 @@
height: var(--navbar-height);
padding-top: 0rem;
padding-bottom: 0rem;
- z-index: 1000;
+ z-index: 1002;
border: none;
background-color: white !important;
}
-.nav-link {
+.nav-full-height.nav-link {
color: inherit;
padding-left: 1rem !important;
padding-right: 1rem !important;
padding-top: 12px;
- background-color: var(--bs-light);
- margin-right: 1px;
+ background-color: white;
+ margin-right: -1px;
}
-.nav-link:hover {
+.nav-full-height.nav-link:hover {
color: white !important;;
background-color: var(--bs-primary);
}
-.nav-link.active {
+.nav-full-height.nav-link.active {
color: white !important;
background-color: var(--bs-primary);
}
@@ -39,7 +39,7 @@
margin-right: 1em;
}
.navbar {
- z-index: 1001;
+ z-index: 1002;
}
.card {
@@ -51,6 +51,8 @@
:root {
--navbar-height: 44px;
+ --panel-left: 20px;
+ --panel-top: 20px;
}
.map-container {
@@ -69,7 +71,6 @@
font-family: Arial, sans-serif;
font-size: 14px;
z-index: 1000;
- box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
border: 1px solid #ccc;
width: auto;
white-space: nowrap;
@@ -77,7 +78,45 @@
.panel-container {
position: absolute;
- bottom: 20px;
- left: 20px;
- z-index: 1000;
-}
\ No newline at end of file
+ top: var(--panel-top);
+ left: var(--panel-left);
+ width: 23rem;
+ max-height: 90vh;
+ max-width: calc(100vw - var(--panel-left) - var(--panel-left));
+ overflow-y: auto;
+ z-index: 1001;
+}
+
+.leaflet-bar {
+ border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;
+ 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 {
+ display: none;
+ }
+}
diff --git a/static/ext/leaflet-ruler/icon.png b/static/ext/leaflet-ruler/icon.png
new file mode 100644
index 0000000..028741e
Binary files /dev/null and b/static/ext/leaflet-ruler/icon.png differ
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"); /* */
+ background-repeat: no-repeat;
+ background-position: center;
+}
+.leaflet-ruler:hover{
+ background-image: url("./icon.png"); /* */
+}
+.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