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">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount, tick } from 'svelte';
|
||||||
import { fetchAggUniqueIps, ApiError } from '$lib/api/client.js';
|
import { fetchAggUniqueIps, ApiError } from '$lib/api/client.js';
|
||||||
import type { components } from '$lib/api/aggregate.gen.js';
|
import type { components } from '$lib/api/aggregate.gen.js';
|
||||||
import type * as Leaflet from 'leaflet';
|
import type * as Leaflet from 'leaflet';
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
let leaflet: typeof import('leaflet') | null = null;
|
let leaflet: typeof import('leaflet') | null = null;
|
||||||
let map: Leaflet.Map | null = null;
|
let map: Leaflet.Map | null = null;
|
||||||
let markersLayer: Leaflet.LayerGroup | null = null;
|
let markersLayer: Leaflet.LayerGroup | null = null;
|
||||||
|
let mapPointsCount = $state(0);
|
||||||
|
|
||||||
async function ensureLeaflet() {
|
async function ensureLeaflet() {
|
||||||
if (leaflet) return;
|
if (leaflet) return;
|
||||||
@@ -41,6 +42,7 @@
|
|||||||
})
|
})
|
||||||
.addTo(map);
|
.addTo(map);
|
||||||
markersLayer = leaflet.layerGroup().addTo(map);
|
markersLayer = leaflet.layerGroup().addTo(map);
|
||||||
|
map.invalidateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearMap() {
|
function clearMap() {
|
||||||
@@ -49,8 +51,7 @@
|
|||||||
|
|
||||||
function renderMap() {
|
function renderMap() {
|
||||||
initMap();
|
initMap();
|
||||||
if (!leaflet || !map || !markersLayer) return;
|
mapPointsCount = 0;
|
||||||
markersLayer.clearLayers();
|
|
||||||
|
|
||||||
// GeoIP для City сейчас идёт в координатах уровня city,
|
// GeoIP для City сейчас идёт в координатах уровня city,
|
||||||
// поэтому группируем по округлённым координатам.
|
// поэтому группируем по округлённым координатам.
|
||||||
@@ -68,9 +69,12 @@
|
|||||||
|
|
||||||
for (const ur of rows ?? []) {
|
for (const ur of rows ?? []) {
|
||||||
for (const ip of ur.ips ?? []) {
|
for (const ip of ur.ips ?? []) {
|
||||||
const lat = ip.latitude ?? null;
|
const latRaw = ip.latitude ?? null;
|
||||||
const lon = ip.longitude ?? 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 (lat == null || lon == null) continue;
|
||||||
|
if (!Number.isFinite(lat) || !Number.isFinite(lon)) continue;
|
||||||
|
|
||||||
// Округление: ~11 км по широте на 0.1 градуса.
|
// Округление: ~11 км по широте на 0.1 градуса.
|
||||||
const latG = Math.round(lat * 10) / 10;
|
const latG = Math.round(lat * 10) / 10;
|
||||||
@@ -99,11 +103,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const points = Array.from(groups.values());
|
const points = Array.from(groups.values());
|
||||||
|
mapPointsCount = points.length;
|
||||||
if (points.length === 0) {
|
if (points.length === 0) {
|
||||||
map.invalidateSize();
|
map?.invalidateSize();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Если карта/слой ещё не готовы — хотя бы покажем счётчик точек.
|
||||||
|
if (!leaflet || !map || !markersLayer) return;
|
||||||
|
|
||||||
|
markersLayer.clearLayers();
|
||||||
for (const p of points) {
|
for (const p of points) {
|
||||||
const r = Math.min(28, 5 + Math.log10(p.count + 1) * 9);
|
const r = Math.min(28, 5 + Math.log10(p.count + 1) * 9);
|
||||||
const color =
|
const color =
|
||||||
@@ -128,8 +137,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
map.invalidateSize();
|
map.invalidateSize();
|
||||||
const bounds = leaflet.latLngBounds(points.map((p) => [p.lat, p.lon] as [number, number]));
|
// Сначала ставим вид по первой точке (на случай проблем с fitBounds/bounds).
|
||||||
map.fitBounds(bounds.pad(0.2));
|
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() {
|
async function load() {
|
||||||
@@ -137,6 +151,8 @@
|
|||||||
err = null;
|
err = null;
|
||||||
try {
|
try {
|
||||||
await ensureLeaflet();
|
await ensureLeaflet();
|
||||||
|
// Ждём, пока bind:this завершится и контейнеру карты будет доступен размер.
|
||||||
|
await tick();
|
||||||
initMap();
|
initMap();
|
||||||
const env = await fetchAggUniqueIps({ geo });
|
const env = await fetchAggUniqueIps({ geo });
|
||||||
partial = !!env.partial;
|
partial = !!env.partial;
|
||||||
@@ -200,6 +216,11 @@
|
|||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content class="p-0">
|
<Card.Content class="p-0">
|
||||||
<div class="h-[420px] w-full rounded-md" bind:this={mapEl}></div>
|
<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.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
Reference in New Issue
Block a user