This commit is contained in:
Christian Beutel
2024-06-15 18:13:46 +02:00
parent 48c192376c
commit 369e63972c
27 changed files with 1321 additions and 41 deletions

9
web/package-lock.json generated
View File

@@ -16,6 +16,7 @@
"@types/three": "^0.161.2",
"canvg": "^4.0.1",
"crypto-random-string": "^5.0.0",
"instead": "^1.0.3",
"isomorphic-xml2js": "^0.1.3",
"jspdf": "^2.5.1",
"jszip": "^3.10.1",
@@ -2929,6 +2930,14 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/instead": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/instead/-/instead-1.0.3.tgz",
"integrity": "sha512-7EUe8+ZmvERjdLwrEc1/CdErN8n8XYdF/uI+n8aLbs6Uf/wyqUXyFrCQ8HYADBFaz4+IqGiPVSTInpkMwqrXuw==",
"engines": {
"node": ">=10"
}
},
"node_modules/internal-slot": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",

View File

@@ -40,6 +40,7 @@
"@types/three": "^0.161.2",
"canvg": "^4.0.1",
"crypto-random-string": "^5.0.0",
"instead": "^1.0.3",
"isomorphic-xml2js": "^0.1.3",
"jspdf": "^2.5.1",
"jszip": "^3.10.1",

View File

@@ -65,7 +65,7 @@ export const handle: Handle = async ({ event, resolve }) => {
// send back the default 'pb_auth' cookie to the client with the latest store state
response.headers.set(
'set-cookie',
pb.authStore.exportToCookie({ httpOnly: false, secure: false })
pb.authStore.exportToCookie({ httpOnly: false, secure: true })
)
return response

View File

@@ -68,7 +68,7 @@
<li
class="menu-item flex items-center px-4 py-3 cursor-pointer hover:bg-menu-item-background-hover focus:bg-menu-item-background-focus transition-colors"
role="presentation"
on:mouseup|stopPropagation={() => handleItemClick(item)}
on:mousedown|stopPropagation={() => handleItemClick(item)}
>
{#if item.icon}
<i class="fa fa-{item.icon} mr-3"></i>

View File

@@ -5,6 +5,7 @@
export let src: string;
export let isThumbnail: boolean = false;
export let showThumbnailControls: boolean = true;
export let showExifControls: boolean = false;
const dispatch = createEventDispatcher();
@@ -12,6 +13,10 @@
dispatch("thumbnail", src);
}
function handleExifClick() {
dispatch("exif", src);
}
function handleDeleteClick() {
dispatch("delete", src);
}
@@ -38,7 +43,15 @@
><i class="fa fa-file-image text-primary"></i></button
>
{/if}
{#if showExifControls}
<button
type="button"
class="tooltip"
data-title={$_("get-position-from-exif")}
on:click={handleExifClick}
><i class="fa fa-magnifying-glass-location text-primary"></i></button
>
{/if}
<button
class="tooltip"
data-title={$_("delete")}

View File

@@ -8,6 +8,8 @@
import "leaflet.awesome-markers/dist/leaflet.awesome-markers.css";
import "leaflet/dist/leaflet.css";
import { createEventDispatcher, onMount } from "svelte";
import { _ } from "svelte-i18n";
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
export let trail: Trail | null;
export let markers: Marker[] = [];
@@ -27,10 +29,17 @@
controlElevation.load(gpxData);
}
$: if(options) {
controlElevation?.updateOptions(options)
$: if (options) {
controlElevation?.updateOptions(options);
}
const hotlineSwitcherItems: DropdownItem[] = [
{ text: $_("altitude"), value: "altitude" },
{ text: $_("slope"), value: "slope" },
{ text: $_("speed"), value: "speed" },
{ text: $_("off"), value: false },
];
onMount(async () => {
L = (await import("leaflet")).default;
await import("leaflet-gpx");
@@ -55,9 +64,39 @@
dispatch("click", e);
});
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
const baseLayer = L.tileLayer(
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
{
attribution: "© OpenStreetMap contributors",
}).addTo(map);
},
);
const topoLayer = L.tileLayer(
"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png ",
{
attribution: "© OpenStreetMap contributors",
},
);
const baseMaps = {
OpenStreetMaps: baseLayer,
OpenTopoMaps: topoLayer,
};
L.control.layers(baseMaps).addTo(map);
switch (localStorage.getItem("layer")) {
case "OpenTopoMaps":
topoLayer.addTo(map);
break;
default:
baseLayer.addTo(map);
break;
}
map!.on("baselayerchange", function (e) {
localStorage.setItem("layer", e.name);
});
const default_elevation_options = {
height: 200,
@@ -84,11 +123,16 @@
// Toggle "leaflet-almostover" integration
almostOver: false,
// Toggle "leaflet-distance-markers" integration
distanceMarkers: { lazy: false, distance: false, direction: true, offset: 500 },
distanceMarkers: {
lazy: false,
distance: false,
direction: true,
offset: 500,
},
// Toggle "leaflet-edgescale" integration
edgeScale: false,
// Toggle "leaflet-hotline" integration
hotline: true,
hotline: "altitude",
// Display track datetimes: true || false
timestamps: false,
waypoints: false,
@@ -103,7 +147,7 @@
prefix: "fa",
markerColor: "cadetblue",
iconColor: "white",
className: "awesome-marker pointer-events-none"
className: "awesome-marker pointer-events-none",
}),
},
trkEnd: {
@@ -112,11 +156,11 @@
prefix: "fa",
markerColor: "cadetblue",
iconColor: "white",
className: "awesome-marker pointer-events-none"
className: "awesome-marker pointer-events-none",
}),
},
graticule: false,
drawing: false
drawing: false,
};
const elevation_options = Object.assign(
@@ -137,16 +181,37 @@
graticule.addTo(map!);
}
});
function switchHotline(metric: "altitude" | "slope" | "speed" | false) {
controlElevation.updateOptions({ hotline: metric, autofitBounds: false });
controlElevation.clear();
controlElevation.load(gpxData);
}
</script>
<div id="map-container" class="flex flex-col h-full">
<div
id="map"
class="rounded-xl z-0 basis-full min-h-72"
class="rounded-xl z-0 basis-full min-h-96 md:min-h-0"
style={crosshair
? "position: relative; outline-style: none;cursor: crosshair !important"
: "position: relative; outline-style: none;"}
></div>
>
<div class="absolute top-20 right-3 text-sm" style="z-index: 500">
<Dropdown
items={hotlineSwitcherItems}
on:change={(e) => switchHotline(e.detail.value)}
let:toggleMenu={openDropdown}
>
<button
class="rounded-sm bg-white text-black hover:bg-gray-200 focus:ring-4 ring-gray-100/50 transition-colors h-11 w-11"
on:click={openDropdown}
>
<i class="fa fa-route text-lg"></i>
</button>
</Dropdown>
</div>
</div>
<div class="flex items-center justify-between">
<slot />
<div class="basis-[300px] flex-grow flex-shrink-0" id="elevation"></div>

View File

@@ -8,6 +8,7 @@
export let parent: { [key: string]: any };
export let thumbnail: number = 0;
export let showThumbnailControls: boolean = true;
export let showExifControls: boolean = false;
let photoPreviews: string[] = [];
@@ -59,7 +60,7 @@
if (!photoFiles) {
photoFiles = [];
}
photoFiles = [...photoFiles, file]
photoFiles = [...photoFiles, file];
}
}
@@ -117,7 +118,9 @@
on:delete={() => handlePhotoDelete(i)}
isThumbnail={thumbnail === i}
on:thumbnail={() => makePhotoThumbnail(i)}
on:exif
{showThumbnailControls}
{showExifControls}
></PhotoCard>
</div>
{/each}

View File

@@ -35,9 +35,21 @@
onMount(() => {
const storedDisplayOption = localStorage.getItem("displayOption");
const storedSort= localStorage.getItem("sort");
const storedSortOrder= localStorage.getItem("sort_order");
if (storedDisplayOption) {
selectedDisplayOption = storedDisplayOption;
}
if(storedSort) {
filter.sort = storedSort as typeof filter.sort
}
if(storedSortOrder) {
filter.sortOrder = storedSortOrder as typeof filter.sortOrder
}
if(storedSort || storedSortOrder) {
dispatch("update", filter);
}
});
function setDisplayOption() {
@@ -45,6 +57,7 @@
}
function setSort() {
localStorage.setItem("sort", filter.sort)
dispatch("update", filter);
}
@@ -54,6 +67,7 @@
} else {
filter.sortOrder = "+";
}
localStorage.setItem("sort_order", filter.sortOrder)
dispatch("update", filter);
}
</script>

View File

@@ -4,6 +4,7 @@
import { waypoint } from "$lib/stores/waypoint_store";
import { icons } from "$lib/util/icon_util";
import EXIF from "$lib/vendor/exif-js/exif";
import { createForm } from "$lib/vendor/svelte-form-lib/index";
import { util } from "$lib/vendor/svelte-form-lib/util";
import { _ } from "svelte-i18n";
@@ -12,6 +13,9 @@
import TextField from "../base/text_field.svelte";
import Textarea from "../base/textarea.svelte";
import PhotoPicker from "../trail/photo_picker.svelte";
import { convertDMSToDD } from "$lib/util/leaflet_util";
import { show_toast } from "$lib/stores/toast_store";
export let openModal: (() => void) | undefined = undefined;
export let closeModal: (() => void) | undefined = undefined;
@@ -35,6 +39,26 @@
.filter((i) => i.includes($form.icon ?? ""))
.map((i) => ({ text: i, value: i, icon: i }))
: [];
function getCoordinatesFromPhoto(src: string) {
EXIF.getData({ src: src }, function (p) {
const lat = EXIF.getTag(p, "GPSLatitude");
const latDir = EXIF.getTag(p, "GPSLatitudeRef");
const lon = EXIF.getTag(p, "GPSLongitude");
const lonDir = EXIF.getTag(p, "GPSLongitudeRef");
if (lat && lon) {
$form.lat = convertDMSToDD(lat, latDir);
$form.lon = convertDMSToDD(lon, lonDir);
} else {
show_toast({
text: "No GPS data in image",
icon: "close",
type: "error",
});
}
});
}
</script>
<Modal
@@ -102,9 +126,11 @@
<PhotoPicker
id="waypoint"
parent={$form}
on:exif={(e) => getCoordinatesFromPhoto(e.detail)}
bind:photos={$form.photos}
bind:photoFiles={$form._photos}
showThumbnailControls={false}
showExifControls={true}
></PhotoPicker>
</div>
</form>

View File

@@ -74,6 +74,7 @@
"file-format": "Dateiformat",
"french": "Französisch",
"german": "Deutsch",
"get-position-from-exif": "Koordinaten aus EXIF Daten",
"grid": "Gitter",
"hero_section_0_text": "Entdecke spannende Routen, speichere deine Favoriten und erlebe die Schönheit der Natur. Finde dein nächstes Abenteuer!",
"hero_section_1_heading": "Hier gibt es noch keine Routen",
@@ -117,6 +118,7 @@
"no-results": "Keine Ergebnisse gefunden",
"not-a-valid-email-address": "Keine gültige Email-Adresse",
"not-completed": "Nicht abgeschlossen",
"off": "Aus",
"or": "oder",
"orientation": "Ausrichtung",
"paper-size": "Papierformat",
@@ -147,7 +149,9 @@
"show-in-overview": "In der Übersicht anzeigen",
"show-on-map": "Auf der Karte anzeigen",
"slogan": "Speichere deine Abenteuer!",
"slope": "Steigung",
"sort": "Sortieren",
"speed": "Geschwindigkeit",
"stop-drawing": "Zeichnen beenden",
"summit-book": "Gipfelbuch",
"text": "Text",

View File

@@ -74,6 +74,7 @@
"file-format": "File format",
"french": "French",
"german": "German",
"get-position-from-exif": "Get coordinates from EXIF data",
"grid": "Grid",
"hero_section_0_text": "Explore exciting trails, save your favorites, and experience the beauty of nature. Find your next adventure!",
"hero_section_1_heading": "It seems there are no trails here yet.",
@@ -117,6 +118,7 @@
"no-results": "No results found",
"not-a-valid-email-address": "Not a valid email address",
"not-completed": "Not completed",
"off": "Off",
"or": "or",
"orientation": "Orientation",
"paper-size": "Paper size",
@@ -147,7 +149,9 @@
"show-in-overview": "Show in overview",
"show-on-map": "Show on map",
"slogan": "Save your adventures!",
"slope": "Slope",
"sort": "Sort",
"speed": "Speed",
"stop-drawing": "Stop drawing",
"summit-book": "Summit Book",
"text": "Text",

View File

@@ -74,6 +74,7 @@
"file-format": "",
"french": "Français",
"german": "Allemand",
"get-position-from-exif": "",
"grid": "Grille",
"hero_section_0_text": "Explorez des itinéraires passionnants, sauvegardez vos favoris et découvrez la beauté de la nature. Trouvez votre prochaine aventure !",
"hero_section_1_heading": "Il semble qu'il n'y ait pas encore d'itinéraire ici.",
@@ -117,6 +118,7 @@
"no-results": "Pas de résultat",
"not-a-valid-email-address": "Adresse email invalide",
"not-completed": "Pas terminé",
"off": "",
"or": "ou",
"orientation": "Orientation",
"paper-size": "Taille de papier",
@@ -147,7 +149,9 @@
"show-in-overview": "Voir dans l'aperçu",
"show-on-map": "Voir sur la carte",
"slogan": "Sauvegarder vos aventures !",
"slope": "",
"sort": "Trier",
"speed": "",
"stop-drawing": "",
"summit-book": "Livre du sommet",
"text": "Texte",

View File

@@ -74,6 +74,7 @@
"file-format": "",
"french": "Francia",
"german": "Német",
"get-position-from-exif": "",
"grid": "",
"hero_section_0_text": "Fedezze fel az izgalmas útvonalakat, mentse el kedvenceit, és tapasztalja meg a természet szépségét. Találja meg a következő kalandját!",
"hero_section_1_heading": "Úgy tűnik, itt még nincsenek ösvények.",
@@ -117,6 +118,7 @@
"no-results": "Nincs eredmény",
"not-a-valid-email-address": "Érvénytelen e-mail cím",
"not-completed": "Nem teljesített",
"off": "",
"or": "vagy",
"orientation": "",
"paper-size": "",
@@ -147,7 +149,9 @@
"show-in-overview": "Áttekintés",
"show-on-map": "Mutatás térképen",
"slogan": "Mentse el a kalandjait!",
"slope": "",
"sort": "Rendezés",
"speed": "",
"stop-drawing": "",
"summit-book": "Csúcspont könyv",
"text": "Szöveg",

View File

@@ -74,6 +74,7 @@
"file-format": "",
"french": "Frans",
"german": "Duits",
"get-position-from-exif": "",
"grid": "Rooster",
"hero_section_0_text": "Verken uitdagende wandelroutes, bewaar je favoriete en ervaar de pracht van de natuur. Op naar het volgende avontuur!",
"hero_section_1_heading": "Je hebt nog geen wandelroutes.",
@@ -117,6 +118,7 @@
"no-results": "Er zijn geen zoekresultaten",
"not-a-valid-email-address": "Het e-mailadres is ongeldig",
"not-completed": "Niet voltooid",
"off": "",
"or": "of",
"orientation": "Oriëntatie",
"paper-size": "Papierformaat",
@@ -147,7 +149,9 @@
"show-in-overview": "Tonen op overzicht",
"show-on-map": "Tonen op kaart",
"slogan": "Bewaar je avonturen!",
"slope": "",
"sort": "Sorteren",
"speed": "",
"stop-drawing": "",
"summit-book": "Bergtopboek",
"text": "Tekst",

View File

@@ -74,6 +74,7 @@
"file-format": "",
"french": "Francuski",
"german": "Niemiecki",
"get-position-from-exif": "",
"grid": "",
"hero_section_0_text": "Eksploruj ekscytujące szlaki, zapisz swoje ulubione, doświadcz piękna naturalnego świata. Znajdź swoją następną przygodę!",
"hero_section_1_heading": "Wygląda na to że nie ma tutaj ścieżek.",
@@ -117,6 +118,7 @@
"no-results": "Brak wyników",
"not-a-valid-email-address": "Nieprawidłowy adres email",
"not-completed": "Nie dokończono",
"off": "",
"or": "lub",
"orientation": "",
"paper-size": "",
@@ -147,7 +149,9 @@
"show-in-overview": "Pokaż w przeglądzie",
"show-on-map": "Pokaż na mapie",
"slogan": "Zapisz swoją wyprawę!",
"slope": "",
"sort": "Sortowanie",
"speed": "",
"stop-drawing": "",
"summit-book": "Logbook",
"text": "Tekst",

View File

@@ -74,6 +74,7 @@
"file-format": "",
"french": "Francês",
"german": "Alemão",
"get-position-from-exif": "",
"grid": "",
"hero_section_0_text": "Explore trilhas emocionantes, salve seus favoritos e experimente a beleza da natureza. Encontre sua próxima aventura!",
"hero_section_1_heading": "Parece que ainda não há trilhas aqui.",
@@ -117,6 +118,7 @@
"no-results": "Nenhum resultado encontrado",
"not-a-valid-email-address": "Não um endereço de e-mail válido",
"not-completed": "Não preenchido",
"off": "",
"or": "ou",
"orientation": "",
"paper-size": "",
@@ -147,7 +149,9 @@
"show-in-overview": "Mostrar na vista geral",
"show-on-map": "Mostrar no mapa",
"slogan": "Guarde as suas aventuras!",
"slope": "",
"sort": "Ordenar",
"speed": "",
"stop-drawing": "",
"summit-book": "Livro da cimeira",
"text": "Texto",

View File

@@ -74,6 +74,7 @@
"file-format": "",
"french": "法语",
"german": "德语",
"get-position-from-exif": "",
"grid": "",
"hero_section_0_text": "探索激动人心的探险,收藏最爱行程,,体验美丽地大自然。找到你的下一次冒险!",
"hero_section_1_heading": "这儿似乎还没有行程。",
@@ -117,6 +118,7 @@
"no-results": "没有找到结果",
"not-a-valid-email-address": "无效电子邮箱地址",
"not-completed": "未完成",
"off": "",
"or": "或",
"orientation": "",
"paper-size": "",
@@ -147,7 +149,9 @@
"show-in-overview": "详情中展示",
"show-on-map": "地图中展示",
"slogan": "保存你的冒险!",
"slope": "",
"sort": "排序",
"speed": "",
"stop-drawing": "",
"summit-book": "详细日程",
"text": "文本",

View File

@@ -95,3 +95,12 @@ export function calculateScaleFactor(map: Map) {
return scaleFactor
}
export function convertDMSToDD(dms: Number[], direction: "N" | "O" | "S" | "W") {
var dd = dms[0].valueOf() + dms[1].valueOf() / 60 + dms[2].valueOf() / (60 * 60);
if (direction == "S" || direction == "W") {
dd = dd * -1;
}
return dd;
}

10
web/src/lib/vendor/exif-js/exif.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
interface EXIFStatic {
getData(url: { src: string } | HTMLImageElement | File, callback: (photo: any) => void): any;
getTag(img: { src: string } | HTMLImageElement | File, tag: any): any;
getAllTags(img: string | File): any;
pretty(img: any): string;
readFromBinaryFile(file: any): any;
}
declare var EXIF: EXIFStatic;
export = EXIF;

1047
web/src/lib/vendor/exif-js/exif.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -452,8 +452,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
.then(() => {
layer.eachLayer((trkseg) => {
if (trkseg.feature.geometry.type != "Point") {
let geo = L.geoJson(trkseg.toGeoJSON(), { coordsToLatLng: (coords) => L.latLng(coords[0], coords[1], (coords[2] * (this.options.altitudeFactor || 1)) || 0) });
let line = L.hotline(geo.toGeoJSON().features[0].geometry.coordinates, {
let line = L.hotline(this._data.map(m => [m.x, m.y, m[prop] || 0]), {
renderer: L.Hotline.renderer(),
min: isFinite(this.track_info[prop + '_min']) ? this.track_info[prop + '_min'] : 0,
max: isFinite(this.track_info[prop + '_max']) ? this.track_info[prop + '_max'] : 1,
@@ -1037,7 +1036,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
// check and fix missing data on last added point
if (i > 0 && isNaN(prev[attr])) {
if (!isNaN(lastValid[attr]) && !isNaN(curr[attr])) {
prev[attr] = (lastValid[attr] + curr[attr]) / 2;
prev[attr] = _.round((lastValid[attr] + curr[attr]) / 2, props.decimals);
} else if (!isNaN(lastValid[attr])) {
prev[attr] = lastValid[attr];
} else if (!isNaN(curr[attr])) {

View File

@@ -1,7 +1,12 @@
// +layout.ts
import '$lib/i18n';
import type { LayoutServerLoad } from './$types';
import { env } from "$env/dynamic/private";
export const load: LayoutServerLoad = async ({ locals }) => {
return { settings: locals.settings }
export const load: LayoutServerLoad = async ({ locals, url }) => {
const warnings = []
if (env.ORIGIN != url.origin) {
warnings.push(`You are accessing wanderer from <span class="font-mono bg-gray-100">${url.origin}</span> but your ORIGIN environment variable is set to <span class="font-mono bg-gray-100">${env.ORIGIN}</span>. This may cause errors.`)
}
return { settings: locals.settings, warnings: warnings }
}

View File

@@ -7,10 +7,13 @@
import { currentUser } from "$lib/stores/user_store";
import { isRouteProtected } from "$lib/util/authorization_util";
import "@fortawesome/fontawesome-free/css/all.min.css";
import { fade, slide } from "svelte/transition";
import { slide } from "svelte/transition";
import "../css/app.css";
import "../css/components.css";
import "../css/theme.css";
import { page } from "$app/stores";
import { onMount } from "svelte";
beforeNavigate((n) => {
if (!$currentUser && isRouteProtected(n.to?.url?.pathname ?? "")) {
@@ -20,6 +23,7 @@
});
let hideDemoHint = false;
let hideWarning = false;
</script>
{#if env.PUBLIC_IS_DEMO === "true" && !hideDemoHint}
@@ -38,6 +42,19 @@
</div>
{/if}
{#if $page.data.warnings?.length && !hideWarning}
<div
class="flex items-center justify-between bg-red-200 text-center p-4 text-sm"
out:slide
>
<div></div>
<span>{@html $page.data.warnings[0]} </span>
<button class="btn-icon self-end" on:click={() => (hideWarning = true)}
><i class="fa fa-close"></i></button
>
</div>
{/if}
<NavBar></NavBar>
<Toast></Toast>
<slot />

View File

@@ -64,9 +64,39 @@
map = L.map("map").setView([0, 0], 4);
map.attributionControl.setPrefix(false);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
const baseLayer = L.tileLayer(
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
{
attribution: "© OpenStreetMap contributors",
}).addTo(map);
},
);
const topoLayer = L.tileLayer(
"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png ",
{
attribution: "© OpenStreetMap contributors",
},
);
const baseMaps = {
OpenStreetMaps: baseLayer,
OpenTopoMaps: topoLayer,
};
L.control.layers(baseMaps).addTo(map);
switch (localStorage.getItem("layer")) {
case "OpenTopoMaps":
topoLayer.addTo(map);
break;
default:
baseLayer.addTo(map);
break;
}
map.on("baselayerchange", function (e) {
localStorage.setItem("layer", e.name);
});
map.on("moveend", async (e) => {
const bounds = map.getBounds();

View File

@@ -195,7 +195,7 @@ https://github.com/iosphere/Leaflet.hotline/
* @returns {Array.<number>} The RGB values as an array [r, g, b]
*/
getRGBForValue: function (value) {
var valueRelative = Math.min(Math.max((value - this._min) / (this._max - this._min), 0), 0.999);
var valueRelative = Math.min(Math.max((value - this._min) / (this._max - this._min), 0), 0.999) || 0;
var paletteIndex = Math.floor(valueRelative * 256) * 4;
return [

View File

@@ -16,7 +16,7 @@ export function Altitude() {
required: this.options.slope,
meta: 'z',
unit: altitude.label,
statsName: 'elevation',
statsName: 'altitude',
deltaMax: this.options.altitudeDeltaMax,
clampRange: this.options.altitudeRange,
// init: ({point}) => {