mirror of
https://github.com/gadgethd/ukmesh.git
synced 2026-09-16 03:55:34 +00:00
refactor(map): fix overlay anchors and migrate mini-maps to OpenFreeMap with attribution
This commit is contained in:
@@ -13,6 +13,14 @@ import {
|
||||
const SOURCE_ID = 'hopreach-rf-source';
|
||||
const LAYER_ID = 'hopreach-rf-layer';
|
||||
|
||||
function getFirstSymbolLayerId(map: maplibregl.Map): string | undefined {
|
||||
const layers = map.getStyle().layers;
|
||||
for (const layer of layers) {
|
||||
if (layer.type === 'symbol') return layer.id;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function removeRfLayers(map: maplibregl.Map): void {
|
||||
if (map.getLayer(LAYER_ID)) map.removeLayer(LAYER_ID);
|
||||
if (map.getSource(SOURCE_ID)) map.removeSource(SOURCE_ID);
|
||||
@@ -61,11 +69,7 @@ export function RfCoverageOverlay({
|
||||
releaseDataset = null;
|
||||
if (!visible || tiles.length === 0) return;
|
||||
|
||||
const beforeId = map.getLayer('map-labels-water')
|
||||
? 'map-labels-water'
|
||||
: map.getLayer('privacy-rings-layer')
|
||||
? 'privacy-rings-layer'
|
||||
: undefined;
|
||||
const beforeId = getFirstSymbolLayerId(map);
|
||||
|
||||
const dataset = registerRfRasterDataset(
|
||||
tiles.map((tile) => ({
|
||||
|
||||
@@ -34,9 +34,17 @@ type TopologyMapOverlayProps = Omit<TopologyMapProps, 'network' | 'observer' | '
|
||||
onNodeSelect: (nodeId: string) => void;
|
||||
};
|
||||
|
||||
function getFirstSymbolLayerId(map: maplibregl.Map): string | undefined {
|
||||
const layers = map.getStyle().layers;
|
||||
for (const layer of layers) {
|
||||
if (layer.type === 'symbol') return layer.id;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function addTopologyLayer(map: maplibregl.Map, layer: maplibregl.LayerSpecification): void {
|
||||
if (map.getLayer(layer.id)) return;
|
||||
const beforeId = map.getLayer('map-labels-water') ? 'map-labels-water' : undefined;
|
||||
const beforeId = getFirstSymbolLayerId(map);
|
||||
if (beforeId) map.addLayer(layer, beforeId);
|
||||
else map.addLayer(layer);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { lazy, memo, Suspense, useEffect } from 'react';
|
||||
import type * as maplibregl from 'maplibre-gl';
|
||||
import { AnimatedPathOverlay, type AerialPath } from '../Map/AnimatedPathOverlay.js';
|
||||
import { OPENFREEMAP_STYLE_DARK } from '../Map/mapConfig.js';
|
||||
|
||||
export const C_CYAN = '#00c4ff';
|
||||
export const C_GREEN = '#00e676';
|
||||
@@ -253,13 +254,6 @@ export const EmptyPacketState: React.FC<{ label?: string }> = ({
|
||||
label = 'No packet data in this window.',
|
||||
}) => <div className="stats-page__empty">{label}</div>;
|
||||
|
||||
const CARTO_DARK_TILES = [
|
||||
'https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
'https://b.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
'https://c.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
'https://d.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
];
|
||||
|
||||
export const DecodedPathMapView: React.FC<{ nodes: DecodedPathNode[] }> = ({ nodes }) => {
|
||||
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||
const [mapInstance, setMapInstance] = React.useState<maplibregl.Map | null>(null);
|
||||
@@ -284,23 +278,11 @@ export const DecodedPathMapView: React.FC<{ nodes: DecodedPathNode[] }> = ({ nod
|
||||
const maplibre = maplibreModule;
|
||||
map = new maplibre.Map({
|
||||
container: containerRef.current,
|
||||
style: {
|
||||
version: 8,
|
||||
sources: {
|
||||
tiles: {
|
||||
type: 'raster',
|
||||
tiles: CARTO_DARK_TILES,
|
||||
tileSize: 256,
|
||||
maxzoom: 19,
|
||||
attribution: '© OpenStreetMap © CARTO',
|
||||
},
|
||||
},
|
||||
layers: [{ id: 'bg', type: 'raster', source: 'tiles' }],
|
||||
},
|
||||
style: OPENFREEMAP_STYLE_DARK,
|
||||
center: [Number(nodes[0]!.lon), Number(nodes[0]!.lat)],
|
||||
zoom: 8,
|
||||
attributionControl: false,
|
||||
});
|
||||
map.addControl(new maplibre.AttributionControl({ compact: true }));
|
||||
map.on('load', () => {
|
||||
if (cancelled || !map) return;
|
||||
const bounds = new maplibre.LngLatBounds();
|
||||
|
||||
@@ -177,8 +177,8 @@ const OriginMiniMap = memo(function OriginMiniMap({
|
||||
style: MAP_STYLE,
|
||||
bounds,
|
||||
fitBoundsOptions: { padding: 40, maxZoom: 10 },
|
||||
attributionControl: false,
|
||||
});
|
||||
map.addControl(new maplibregl.AttributionControl({ compact: true }));
|
||||
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');
|
||||
map.on('load', () => {
|
||||
map.addSource('zone', {
|
||||
@@ -488,8 +488,8 @@ export function SpamPage() {
|
||||
style: MAP_STYLE,
|
||||
center: [-2.5, 54.0],
|
||||
zoom: 4.6,
|
||||
attributionControl: false,
|
||||
});
|
||||
map.addControl(new maplibregl.AttributionControl({ compact: true }));
|
||||
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');
|
||||
map.on('load', () => {
|
||||
map.addSource('zones', { type: 'geojson', data: EMPTY_FC });
|
||||
|
||||
@@ -111,8 +111,8 @@ export const OwnerMapView: React.FC<{
|
||||
style: MAP_STYLE,
|
||||
center: [DEFAULT_CENTER[1], DEFAULT_CENTER[0]],
|
||||
zoom: 7,
|
||||
attributionControl: false,
|
||||
});
|
||||
map.addControl(new maplibregl.AttributionControl({ compact: true }));
|
||||
mapRef.current = map;
|
||||
|
||||
// Keep the map as a static regional backdrop while preserving marker clicks.
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type * as maplibregl from 'maplibre-gl';
|
||||
import { AnimatedPathOverlay, type AerialPath, type AerialPathNode } from '../../components/Map/AnimatedPathOverlay.js';
|
||||
import { LoadingIndicator } from '../../components/LoadingIndicator.js';
|
||||
import { OPENFREEMAP_STYLE_DARK } from '../../components/Map/mapConfig.js';
|
||||
import type { MeshNode } from '../../hooks/useNodes.js';
|
||||
import {
|
||||
multiObserverPathRoutes,
|
||||
@@ -42,13 +43,6 @@ export type LazyPathResult = {
|
||||
paths: LazyPath[];
|
||||
};
|
||||
|
||||
const CARTO_TILES = [
|
||||
'https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
'https://b.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
'https://c.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
'https://d.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
];
|
||||
|
||||
function lazyPathRuns(nodes: readonly LazyPathNode[]): LazyPathNode[][] {
|
||||
const runs: LazyPathNode[][] = [];
|
||||
let current: LazyPathNode[] = [];
|
||||
@@ -193,17 +187,13 @@ export const PathMap: React.FC<{
|
||||
maplibreRef.current = maplibre;
|
||||
nextMap = new maplibre.Map({
|
||||
container: containerRef.current,
|
||||
style: {
|
||||
version: 8,
|
||||
sources: { tiles: { type: 'raster', tiles: CARTO_TILES, tileSize: 256, maxzoom: 19, attribution: '© OpenStreetMap © CARTO' } },
|
||||
layers: [{ id: 'bg', type: 'raster', source: 'tiles' }],
|
||||
},
|
||||
style: OPENFREEMAP_STYLE_DARK,
|
||||
center: [0, 51.5],
|
||||
zoom: 6,
|
||||
pitch: 50,
|
||||
bearing: -8,
|
||||
attributionControl: false,
|
||||
});
|
||||
nextMap.addControl(new maplibre.AttributionControl({ compact: true }));
|
||||
mapRef.current = nextMap;
|
||||
nextMap.on('load', () => {
|
||||
if (cancelled || !nextMap) return;
|
||||
|
||||
Reference in New Issue
Block a user