Enhance map functionality and display of IP data
- 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:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user