diff options
| author | 2026-05-02 23:49:00 +0700 | |
|---|---|---|
| committer | 2026-05-02 23:49:00 +0700 | |
| commit | d8de4ede8c6a0cc02b9c8c30d68155b1f838254c (patch) | |
| tree | 449701dcf7ac0aafc7f21116b77203d0cc51fc98 /views/pages/global-map.html | |
| parent | 4c05d4c66827d6e5a131c657ee08293bcde497d1 (diff) | |
| parent | cca3ff72b5e29b31122c0a0496be741e7bc0162b (diff) | |
| download | web-d8de4ede8c6a0cc02b9c8c30d68155b1f838254c.tar.gz web-d8de4ede8c6a0cc02b9c8c30d68155b1f838254c.zip | |
v0.14.0 (#27)v0.14.0
* [feat: migrate to
uz.saya.casa](https://github.com/SayaAndy/saya-today-web/commit/d3a63ca8a84bf4583bac8f5fd09bd52774bc4158)
* [feat: overhaul the design of blog
catalog](https://github.com/SayaAndy/saya-today-web/commit/99294b260661a8d3f6992404de7881945eab837b)
* [feat: replace font-andika with M PLUS and Gentium
Plus](https://github.com/SayaAndy/saya-today-web/commit/b1355436e96f2dfa777e843b53896d0bb71628e3)
* [feat: use yandexcloud.kz for photos &
static](https://github.com/SayaAndy/saya-today-web/commit/3898baa47faf268864ecac3699f7e98c6cac01a4)
* [feat: move from b2 to s3 for blog pages &
facts](https://github.com/SayaAndy/saya-today-web/commit/3efa6ed24713658af855c0ca9504f3e089342182)
* [feat: support s3 for blog
pages](https://github.com/SayaAndy/saya-today-web/commit/66b2a96fcca113105043be3cb9c49f8ee7fe240c)
* [feat: use accent colors for glightbox
tooltip](https://github.com/SayaAndy/saya-today-web/commit/81a9147c5406e3b2f2441373b935d7c5de2780da)
* [fix: set sidecar onclick attribute correctly in
update](https://github.com/SayaAndy/saya-today-web/commit/941bd3b3f3e26ca62757a34a20bc9b323be02a74)
* [fix: set cache-control for non-cacheable
pages](https://github.com/SayaAndy/saya-today-web/commit/78d6b00cd973e4254ea13cbb17bb35a30fef51b9)
* [feat: update font-awesome 6.7.2 to
7.2.0](https://github.com/SayaAndy/saya-today-web/commit/5e64aa9db2796e8aa6267d2ed9179ea01dc6561b)
* [fix: remove leading slash in path with going
up](https://github.com/SayaAndy/saya-today-web/commit/13428b467f752d9b53b39a3c8122998f1896ed1e)
* [feat: implement rate
limiting](https://github.com/SayaAndy/saya-today-web/commit/ca1f36376865cb2a20923aa62fdff3ee217abde3)
* [feat: integrate unix sockets into stage &
prod](https://github.com/SayaAndy/saya-today-web/commit/507bcbac6b1bf9f1a731afbe82bec10700f4028c)
* [feat: compact & move new fonts into
yandexcloud.kz](https://github.com/SayaAndy/saya-today-web/pull/27/commits/cca3ff72b5e29b31122c0a0496be741e7bc0162b)
* [feat: update all github actions, golang dependencies, golang to 1.26,
alpine to
3.23](https://github.com/SayaAndy/saya-today-web/commit/a3aa1dd07abfbbb27a7dfeeb536a6fcc51709256)
* [refactor: simpler frontmatter parser (replace regexp with bytes
pkg)](https://github.com/SayaAndy/saya-today-web/commit/42bf9d8e7f459f9021fb6fb389d3b51fed5edc35)
* [feat: allow some api methods + disallow user pages for
bots](https://github.com/SayaAndy/saya-today-web/commit/e3c80a07514a052adafd5c0f51daf69b664530e0)
Diffstat (limited to 'views/pages/global-map.html')
| -rw-r--r-- | views/pages/global-map.html | 598 |
1 files changed, 354 insertions, 244 deletions
diff --git a/views/pages/global-map.html b/views/pages/global-map.html index 604b52e..bb51131 100644 --- a/views/pages/global-map.html +++ b/views/pages/global-map.html @@ -1,303 +1,413 @@ -<!DOCTYPE html> +<!doctype html> <html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <meta name="description" content="{{ .L.GlobalMap.Description }}"> - <meta property="og:title" content="{{ .L.GlobalMap.Header }}"> - <meta property="og:description" content="{{ .L.GlobalMap.Description }}"> - <meta property="og:url" content="{{ .CanonicalEndpoint }}/{{ .Lang }}/map"> - <meta property="og:type" content="site"> - <title>{{ .L.GlobalMap.Header }} // SAYA TODAY</title> - <link rel="canonical" href="{{ .CanonicalEndpoint }}/{{ .Lang }}/map"> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"> - {{ block "header" . }}{{ end }} - <link href="/output.css" rel="stylesheet"> - <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.css"> - <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet.markercluster/1.4.1/MarkerCluster.css"> - <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css"> -</head> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta name="description" content="{{ .L.GlobalMap.Description }}" /> + <meta property="og:title" content="{{ .L.GlobalMap.Header }}" /> + <meta + property="og:description" + content="{{ .L.GlobalMap.Description }}" + /> + <meta + property="og:url" + content="{{ .CanonicalEndpoint }}/{{ .Lang }}/map" + /> + <meta property="og:type" content="site" /> + <title>{{ .L.GlobalMap.Header }} // SAYA.UZ</title> + <link rel="canonical" href="{{ .CanonicalEndpoint }}/{{ .Lang }}/map"> + <link rel="stylesheet" href="{{ .StaticStorageBaseUrl }}/fonts/fonts.css"> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/font-awesome/7.2.0/css/fontawesome.min.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/font-awesome/7.2.0/css/brands.min.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/font-awesome/7.2.0/css/solid.min.css" + /> + {{ block "header" . }}{{ end }} + <link rel="stylesheet" href="/output.css"> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css" + /> + </head> -<body data-theme="ram" class="font-andika text-main-hard overflow-hidden bg-interlocked-hexagons h-dvh"> - - <script> - function switchTheme(theme) { - if (theme == "" || typeof theme == "undefined") { - const currentTheme = document.body.getAttribute('data-theme'); - switch (currentTheme) { - case "olive": theme = "lettuce"; break; - case "lettuce": theme = "night"; break; - case "night": theme = "ram"; break; - case "ram": theme = "lilac"; break; - case "lilac": theme = "olive"; break; + <body + data-theme="ram" + class="font-m-plus text-main-hard overflow-hidden bg-interlocked-hexagons h-dvh" + > + <script> + function switchTheme(theme) { + if (theme == "" || typeof theme == "undefined") { + const currentTheme = + document.body.getAttribute("data-theme"); + switch (currentTheme) { + case "olive": + theme = "lettuce"; + break; + case "lettuce": + theme = "night"; + break; + case "night": + theme = "ram"; + break; + case "ram": + theme = "lilac"; + break; + case "lilac": + theme = "olive"; + break; + } } + localStorage.setItem("theme", theme); + document.body.setAttribute("data-theme", theme); } - localStorage.setItem('theme', theme); - document.body.setAttribute('data-theme', theme); - } - const savedTheme = localStorage.getItem('theme') || 'lilac'; - switchTheme(savedTheme); - </script> + const savedTheme = localStorage.getItem("theme") || "lilac"; + switchTheme(savedTheme); + </script> - <script> - const clientTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + <script> + const clientTimeZone = + Intl.DateTimeFormat().resolvedOptions().timeZone; - function convertRemToPixels(rem) { - return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); - } - </script> + function convertRemToPixels(rem) { + return ( + rem * + parseFloat( + getComputedStyle(document.documentElement).fontSize, + ) + ); + } + </script> - <div id="sidebar" class="bg-sidebar block fixed shadow-[0_0_0.4rem_black] z-1001 w-fit h-auto"> - <div class="text-[3rem] flex flex-col gap-0 relative z-1001"> - <a href="./" class="themed-button themed-button-sidebar w-fit h-fit" draggable="false"> - <i class="fas fa-circle-left flex w-18 h-18 content-center text-center text-sidebar"></i> - </a> - <a href="/{{ .Lang }}" class="themed-button themed-button-sidebar w-fit h-fit" draggable="false"> - <i class="fas fa-home flex w-18 h-18 content-center text-center text-sidebar"></i> - </a> - <a href="/{{ .Lang }}/blog" class="themed-button themed-button-sidebar w-fit h-fit" draggable="false"> - <i class="fas fa-search flex w-18 h-18 content-center text-center text-sidebar"></i> - </a> - <a href="/{{ .Lang }}/map" class="themed-button themed-button-sidebar w-fit h-fit" draggable="false"> - <i class="fas fa-map flex w-18 h-18 content-center text-center text-sidebar"></i> - </a> - <div id="toolbar-theme-button" onclick="toggleThemeWheel();" class="themed-button themed-button-sidebar w-fit h-fit" draggable="false"> - <i id="toolbar-theme-button-content" class="fas fa-swatchbook flex w-18 h-18 content-center text-center text-sidebar"></i> + <div + id="sidebar" + class="bg-sidebar block fixed shadow-[0_0_0.4rem_black] z-1001 w-fit h-auto" + > + <div class="text-[3rem] flex flex-col gap-0 relative z-1001"> + <a + href="./" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + class="fas fa-circle-left flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <a + href="/{{ .Lang }}" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + class="fas fa-home flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <a + href="/{{ .Lang }}/blog" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + class="fas fa-search flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <a + href="/{{ .Lang }}/map" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + class="fas fa-map flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <div + id="toolbar-theme-button" + onclick="toggleThemeWheel()" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + id="toolbar-theme-button-content" + class="fas fa-swatchbook flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </div> + <a + href="/{{ .Lang }}/user" + id="sidebar-user-button" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + onclick="return changeUrl('/{{ .Lang }}/user');" + class="fas fa-user flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + </div> + <div class="theme-wheel" id="theme-wheel"> + <div class="theme-icon" data-theme="olive"></div> + <div class="theme-icon" data-theme="lettuce"></div> + <div class="theme-icon" data-theme="night"></div> + <div class="theme-icon" data-theme="ram"></div> + <div class="theme-icon" data-theme="lilac"></div> + <div class="theme-pin"></div> </div> - <a href="/{{ .Lang }}/user" id="sidebar-user-button" class="themed-button themed-button-sidebar w-fit h-fit" draggable="false"> - <i onclick="return changeUrl('/{{ .Lang }}/user');" class="fas fa-user flex w-18 h-18 content-center text-center text-sidebar"></i> - </a> - </div> - <div class="theme-wheel" id="theme-wheel"> - <div class="theme-icon" data-theme="olive"></div> - <div class="theme-icon" data-theme="lettuce"></div> - <div class="theme-icon" data-theme="night"></div> - <div class="theme-icon" data-theme="ram"></div> - <div class="theme-icon" data-theme="lilac"></div> - <div class="theme-pin"></div> </div> - </div> - - <div id="map-container" class="bg-background-dark flex z-10 w-full h-full"></div> - - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/htmx/2.0.6/htmx.min.js"></script> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.js"></script> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></script> - - <script> - const themeWheel = document.getElementById('theme-wheel'); - const themeButton = document.getElementById('toolbar-theme-button'); - const themeIcons = document.querySelectorAll('.theme-icon'); - const themeWheelRadius = convertRemToPixels(6); - - themeIcons.forEach(icon => { - if (icon.getAttribute('data-theme') == savedTheme) { - icon.classList.add('active'); - } - }); - function calculateVmax(percent) { - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; + <div + id="map-container" + class="bg-paper bg-background-dark flex z-10 w-full h-full" + ></div> - const largerDimension = Math.max(viewportWidth, viewportHeight); + <script src="{{ .StaticStorageBaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></script> - const vmaxValue = (largerDimension / 100) * percent; + <script> + const themeWheel = document.getElementById("theme-wheel"); + const themeButton = document.getElementById("toolbar-theme-button"); + const themeIcons = document.querySelectorAll(".theme-icon"); + const themeWheelRadius = convertRemToPixels(6); + const THUMB_BASE = "{{ .ThumbnailBaseUrl }}"; - return vmaxValue; - } + themeIcons.forEach((icon) => { + if (icon.getAttribute("data-theme") == savedTheme) { + icon.classList.add("active"); + } + }); - function positionThemeIcons() { - const sliceAngle = 360 / themeIcons.length; + function calculateVmax(percent) { + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; - themeIcons.forEach((icon, index) => { - const startAngle = index * sliceAngle; - const endAngle = (index + 1) * sliceAngle; + const largerDimension = Math.max(viewportWidth, viewportHeight); - const clipPath = createSliceClipPath(startAngle, endAngle, themeWheelRadius); - icon.style.clipPath = clipPath; - }); - } + const vmaxValue = (largerDimension / 100) * percent; - function createSliceClipPath(startAngle, endAngle, radius) { - const centerX = radius; - const centerY = radius; + return vmaxValue; + } - let points = `${centerX}px ${centerY}px`; + function positionThemeIcons() { + const sliceAngle = 360 / themeIcons.length; - const segments = Math.max(2, Math.ceil(Math.abs(endAngle - startAngle) / 5)); - const angleStep = (endAngle - startAngle) / segments; + themeIcons.forEach((icon, index) => { + const startAngle = index * sliceAngle; + const endAngle = (index + 1) * sliceAngle; - for (let i = 0; i <= segments; i++) { - const angle = ((startAngle + angleStep * i - 90) * Math.PI) / 180; - const x = centerX + Math.cos(angle) * radius; - const y = centerY + Math.sin(angle) * radius; - points += `, ${x}px ${y}px`; + const clipPath = createSliceClipPath( + startAngle, + endAngle, + themeWheelRadius, + ); + icon.style.clipPath = clipPath; + }); } - points += `, ${centerX}px ${centerY}px`; - - return `polygon(${points})`; - } + function createSliceClipPath(startAngle, endAngle, radius) { + const centerX = radius; + const centerY = radius; - function positionThemeWheel() { - const buttonRect = themeButton.getBoundingClientRect(); - const buttonCenterX = buttonRect.left + buttonRect.width / 2; - const buttonCenterY = buttonRect.top + buttonRect.height / 2; + let points = `${centerX}px ${centerY}px`; - const margin = 0; + const segments = Math.max( + 2, + Math.ceil(Math.abs(endAngle - startAngle) / 5), + ); + const angleStep = (endAngle - startAngle) / segments; - let wheelX = buttonCenterX - themeWheelRadius; - let wheelY = buttonCenterY - themeWheelRadius; + for (let i = 0; i <= segments; i++) { + const angle = + ((startAngle + angleStep * i - 90) * Math.PI) / 180; + const x = centerX + Math.cos(angle) * radius; + const y = centerY + Math.sin(angle) * radius; + points += `, ${x}px ${y}px`; + } - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; + points += `, ${centerX}px ${centerY}px`; - if (wheelX < margin) { - wheelX = margin; - } else if (wheelX + (themeWheelRadius * 2) > viewportWidth - margin) { - wheelX = viewportWidth - (themeWheelRadius * 2) - margin; + return `polygon(${points})`; } - if (wheelY < margin) { - wheelY = margin; - } else if (wheelY + (themeWheelRadius * 2) > viewportHeight - margin) { - wheelY = viewportHeight - (themeWheelRadius * 2) - margin; - } + function positionThemeWheel() { + const buttonRect = themeButton.getBoundingClientRect(); + const buttonCenterX = buttonRect.left + buttonRect.width / 2; + const buttonCenterY = buttonRect.top + buttonRect.height / 2; - themeWheel.style.left = wheelX + 'px'; - themeWheel.style.top = wheelY + 'px'; - } + const margin = 0; - function hideThemeWheel() { - themeWheel.classList.remove('active'); - } + let wheelX = buttonCenterX - themeWheelRadius; + let wheelY = buttonCenterY - themeWheelRadius; - function showThemeWheel() { - positionThemeWheel(); - positionThemeIcons(); - themeWheel.classList.add('active'); - } + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; - function toggleThemeWheel() { - if (themeWheel.classList.contains('active')) { - hideThemeWheel(); - } else { - showThemeWheel(); - } - } + if (wheelX < margin) { + wheelX = margin; + } else if ( + wheelX + themeWheelRadius * 2 > + viewportWidth - margin + ) { + wheelX = viewportWidth - themeWheelRadius * 2 - margin; + } - themeIcons.forEach(icon => { - icon.addEventListener('click', (e) => { - e.stopPropagation(); - const theme = icon.getAttribute('data-theme'); - switchTheme(theme); - themeIcons.forEach(icon => { - icon.classList.remove('active'); - }) - icon.classList.add('active'); - }); - }); + if (wheelY < margin) { + wheelY = margin; + } else if ( + wheelY + themeWheelRadius * 2 > + viewportHeight - margin + ) { + wheelY = viewportHeight - themeWheelRadius * 2 - margin; + } - themeWheel.addEventListener('mouseleave', hideThemeWheel); + themeWheel.style.left = wheelX + "px"; + themeWheel.style.top = wheelY + "px"; + } - document.addEventListener('click', (e) => { - if (!themeWheel.contains(e.target) && e.target !== themeButton && !e.target.classList.contains("toolbar-theme-button-content")) { - hideThemeWheel(); + function hideThemeWheel() { + themeWheel.classList.remove("active"); } - }); - </script> - <script> - var map; - var markers = L.markerClusterGroup(); + function showThemeWheel() { + positionThemeWheel(); + positionThemeIcons(); + themeWheel.classList.add("active"); + } - function createCustomIcon(color, borderColor = "var(--main-soft-color)") { - const svgIcon = ` - <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> - <path d="M12 1C7.5 1 4 4.5 4 9c0 6.5 8 14 8 14s8-7.5 8-14c0-4.5-3.5-8-8-8zm0 11c-1.5 0-3-1.5-3-3s1.5-3 3-3 3 1.5 3 3-1.5 3-3 3z" - fill="${color}" - stroke="${borderColor}" - stroke-width="1" - stroke-linejoin="round"/> - </svg> - `; + function toggleThemeWheel() { + if (themeWheel.classList.contains("active")) { + hideThemeWheel(); + } else { + showThemeWheel(); + } + } - return L.divIcon({ - html: svgIcon, - className: 'custom-svg-marker', - iconSize: [32, 32], - iconAnchor: [16, 32], - popupAnchor: [0, -32] + themeIcons.forEach((icon) => { + icon.addEventListener("click", (e) => { + e.stopPropagation(); + const theme = icon.getAttribute("data-theme"); + switchTheme(theme); + themeIcons.forEach((icon) => { + icon.classList.remove("active"); + }); + icon.classList.add("active"); + }); }); - } - function initLocationMap() { - map = L.map('map-container').setView([{{ .MapLocationLat }}, {{ .MapLocationLong }}], 4); + themeWheel.addEventListener("mouseleave", hideThemeWheel); - L.tileLayer('https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', { - maxZoom: 20, - attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', - zoomControl: false - }).addTo(map); + document.addEventListener("click", (e) => { + if ( + !themeWheel.contains(e.target) && + e.target !== themeButton && + !e.target.classList.contains("toolbar-theme-button-content") + ) { + hideThemeWheel(); + } + }); + </script> - L.control.zoom({ - position: 'topright' - }).addTo(map); + <script> + var map; + var markers = L.markerClusterGroup(); - map.addLayer(markers); - } + function createCustomIcon(color, borderColor = "var(--color-main-soft)") { + const svgIcon = ` + <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> + <path d="M12 1C7.5 1 4 4.5 4 9c0 6.5 8 14 8 14s8-7.5 8-14c0-4.5-3.5-8-8-8zm0 11c-1.5 0-3-1.5-3-3s1.5-3 3-3 3 1.5 3 3-1.5 3-3 3z" + fill="${color}" + stroke="${borderColor}" + stroke-width="1" + stroke-linejoin="round"/> + </svg> + `; - function markerToHsl(str, newCoef) { - let hash = 0; - for (let i = 0; i < str.length / 2; i++) { - hash = str.charCodeAt(i) + ((hash << 5) - hash); - hash = str.charCodeAt(str.length - i - 1) + ((hash << 5) - hash); - hash %= 360; + return L.divIcon({ + html: svgIcon, + className: 'custom-svg-marker', + iconSize: [32, 32], + iconAnchor: [16, 32], + popupAnchor: [0, -32] + }); } - return [hash, (20 + newCoef * 80).toFixed(0), (20 + newCoef * 75).toFixed(0)]; - } + function initLocationMap() { + map = L.map('map-container').setView([{{ .MapLocationLat }}, {{ .MapLocationLong }}], 4); - function mapAddMarker(x, y, relativeErrorMeters = 0, title = "", link = "", thumbnail = "", newCoef = 1) { - const [h, s, l] = markerToHsl(title, newCoef); - const color = `hsl(${h} ${s}% ${l}%)`; + L.tileLayer('https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', { + maxZoom: 20, + attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', + zoomControl: false + }).addTo(map); - if (relativeErrorMeters != 0) { - var circle = L.circle([x, y], { - color: color, - fillColor: color, - fillOpacity: 0.15, - radius: relativeErrorMeters + L.control.zoom({ + position: 'topright' }).addTo(map); + + map.addLayer(markers); } - var marker = L.marker([x, y], { - icon: createCustomIcon(color), - title: title, - }).bindPopup(` - <div class="flex flex-row justify-center justify-items-center"> - <img onclick="location.href='${link}';" class="cursor-pointer object-cover rounded-[10%] select-none w-12 h-12 mr-1" src="https://f003.backblazeb2.com/file/sayana-photos/webp-320p/${thumbnail}.webp"> - <span><b><a href="${link}">${title}</a></b><br><a href="https://www.openstreetmap.org/#map=13/${x}/${y}">${x}, ${y}</a></span> - </div>`); + function markerToHsl(str, newCoef) { + let hash = 0; + for (let i = 0; i < str.length / 2; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + hash = str.charCodeAt(str.length - i - 1) + ((hash << 5) - hash); + hash %= 360; + } - markers.addLayer(marker); - } + return [hash, (20 + newCoef * 80).toFixed(0), (20 + newCoef * 75).toFixed(0)]; + } - document.addEventListener('DOMContentLoaded', () => { - initLocationMap(); - {{- range .MapMarkers }} - mapAddMarker({{ .Lat }}, {{ .Long }}, {{ .AccuracyMeters }}, {{ .Title }}, {{ .PageLink }}, {{ .Thumbnail }}, {{ fdiv .Index (len $.MapMarkers) }}); - {{- end }} - }); + function mapAddMarker(x, y, relativeErrorMeters = 0, title = "", link = "", thumbnail = "", newCoef = 1) { + const [h, s, l] = markerToHsl(title, newCoef); + const color = `hsl(${h} ${s}% ${l}%)`; - window.addEventListener('resize', function() { - setTimeout(() => { - map.invalidateSize(); - }, 100); - }); - </script> + if (relativeErrorMeters != 0) { + var circle = L.circle([x, y], { + color: color, + fillColor: color, + fillOpacity: 0.15, + radius: relativeErrorMeters + }).addTo(map); + } -</body> + var marker = L.marker([x, y], { + icon: createCustomIcon(color), + title: title, + }).bindPopup(` + <div class="flex flex-row justify-center justify-items-center"> + <img onclick="location.href='${link}';" class="cursor-pointer object-cover rounded-[10%] select-none w-12 h-12 mr-1" src="${THUMB_BASE.replace('%s', thumbnail)}"> + <span><b><a href="${link}">${title}</a></b><br><a href="https://www.openstreetmap.org/#map=13/${x}/${y}">${x}, ${y}</a></span> + </div>`); + + markers.addLayer(marker); + } + + document.addEventListener('DOMContentLoaded', () => { + initLocationMap(); + {{- range .MapMarkers }} + mapAddMarker({{ .Lat }}, {{ .Long }}, {{ .AccuracyMeters }}, {{ .Title }}, {{ .PageLink }}, {{ .Thumbnail }}, {{ fdiv .Index (len $.MapMarkers) }}); + {{- end }} + }); + + window.addEventListener('resize', function() { + setTimeout(() => { + map.invalidateSize(); + }, 100); + }); + </script> + </body> </html> |