Enhance map functionality and display of IP data
Publish telemt-api gateway Docker image / test (push) Successful in 24s
Publish telemt-api gateway Docker image / build-and-push (push) Successful in 2m10s

- Introduced a point counter for the map to show the number of IP locations displayed, improving user feedback.
- Updated the map rendering logic to handle cases where the map or layers are not yet initialized, preventing potential errors.
- Improved coordinate handling by ensuring latitude and longitude values are properly validated and converted to numbers.
- Enhanced the map's initial view settings to provide a better user experience when displaying points.
This commit is contained in:
Denozordec
2026-03-30 15:41:47 +07:00
parent f710847320
commit 3703421dd8
+29 -8
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { onMount, tick } from 'svelte';
import { fetchAggUniqueIps, ApiError } from '$lib/api/client.js';
import type { components } from '$lib/api/aggregate.gen.js';
import type * as Leaflet from 'leaflet';
@@ -22,6 +22,7 @@
let leaflet: typeof import('leaflet') | null = null;
let map: Leaflet.Map | null = null;
let markersLayer: Leaflet.LayerGroup | null = null;
let mapPointsCount = $state(0);
async function ensureLeaflet() {
if (leaflet) return;
@@ -41,6 +42,7 @@
})
.addTo(map);
markersLayer = leaflet.layerGroup().addTo(map);
map.invalidateSize();
}
function clearMap() {
@@ -49,8 +51,7 @@
function renderMap() {
initMap();
if (!leaflet || !map || !markersLayer) return;
markersLayer.clearLayers();
mapPointsCount = 0;
// GeoIP для City сейчас идёт в координатах уровня city,
// поэтому группируем по округлённым координатам.
@@ -68,9 +69,12 @@
for (const ur of rows ?? []) {
for (const ip of ur.ips ?? []) {
const lat = ip.latitude ?? null;
const lon = ip.longitude ?? null;
const latRaw = ip.latitude ?? null;
const lonRaw = ip.longitude ?? null;
const lat = latRaw == null ? null : Number(latRaw);
const lon = lonRaw == null ? null : Number(lonRaw);
if (lat == null || lon == null) continue;
if (!Number.isFinite(lat) || !Number.isFinite(lon)) continue;
// Округление: ~11 км по широте на 0.1 градуса.
const latG = Math.round(lat * 10) / 10;
@@ -99,11 +103,16 @@
}
const points = Array.from(groups.values());
mapPointsCount = points.length;
if (points.length === 0) {
map.invalidateSize();
map?.invalidateSize();
return;
}
// Если карта/слой ещё не готовы — хотя бы покажем счётчик точек.
if (!leaflet || !map || !markersLayer) return;
markersLayer.clearLayers();
for (const p of points) {
const r = Math.min(28, 5 + Math.log10(p.count + 1) * 9);
const color =
@@ -128,8 +137,13 @@
}
map.invalidateSize();
const bounds = leaflet.latLngBounds(points.map((p) => [p.lat, p.lon] as [number, number]));
map.fitBounds(bounds.pad(0.2));
// Сначала ставим вид по первой точке (на случай проблем с fitBounds/bounds).
map.setView([points[0].lat, points[0].lon], points.length === 1 ? 5 : 3);
if (points.length > 1) {
const bounds = leaflet.latLngBounds(points.map((p) => [p.lat, p.lon] as [number, number]));
map.fitBounds(bounds.pad(0.2));
}
}
async function load() {
@@ -137,6 +151,8 @@
err = null;
try {
await ensureLeaflet();
// Ждём, пока bind:this завершится и контейнеру карты будет доступен размер.
await tick();
initMap();
const env = await fetchAggUniqueIps({ geo });
partial = !!env.partial;
@@ -200,6 +216,11 @@
</Card.Header>
<Card.Content class="p-0">
<div class="h-[420px] w-full rounded-md" bind:this={mapEl}></div>
{#if mapPointsCount > 0}
<div class="px-4 pb-3 pt-2 text-xs text-muted-foreground">
Точек на карте: {mapPointsCount}
</div>
{/if}
</Card.Content>
</Card.Root>
{:else}