feat: add settings modal for map display
This commit is contained in:
@@ -5,14 +5,20 @@ import {
|
||||
Marker,
|
||||
Popup,
|
||||
TileLayer,
|
||||
Tooltip,
|
||||
useMap,
|
||||
useMapEvents,
|
||||
} from 'react-leaflet';
|
||||
import type { MapTileStyle } from './mapStyles';
|
||||
import type { BBox, ParsedOsmResult } from '../search/types';
|
||||
|
||||
type MapViewProps = {
|
||||
results: ParsedOsmResult[];
|
||||
selectedResultId: string | null;
|
||||
tileStyle: MapTileStyle;
|
||||
showResultLabels: boolean;
|
||||
fitMapToResults: boolean;
|
||||
dimMap: boolean;
|
||||
onResultSelect: (id: string) => void;
|
||||
onViewportChange: (bbox: BBox) => void;
|
||||
};
|
||||
@@ -23,6 +29,10 @@ const DEFAULT_ZOOM = 13;
|
||||
export function MapView({
|
||||
results,
|
||||
selectedResultId,
|
||||
tileStyle,
|
||||
showResultLabels,
|
||||
fitMapToResults,
|
||||
dimMap,
|
||||
onResultSelect,
|
||||
onViewportChange,
|
||||
}: MapViewProps) {
|
||||
@@ -53,14 +63,17 @@ export function MapView({
|
||||
center={DEFAULT_CENTER}
|
||||
zoom={DEFAULT_ZOOM}
|
||||
minZoom={3}
|
||||
className="map-view"
|
||||
className={dimMap ? 'map-view map-view-dimmed' : 'map-view'}
|
||||
zoomControl
|
||||
>
|
||||
<TileLayer
|
||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
key={tileStyle.id}
|
||||
attribution={tileStyle.attribution}
|
||||
url={tileStyle.url}
|
||||
maxZoom={tileStyle.maxZoom}
|
||||
/>
|
||||
<ViewportReporter onViewportChange={onViewportChange} />
|
||||
<FitResults results={results} enabled={fitMapToResults} />
|
||||
<SelectedResultFocus result={results.find((item) => item.id === selectedResultId) ?? null} />
|
||||
{results.map((result) => (
|
||||
<Marker
|
||||
@@ -76,6 +89,11 @@ export function MapView({
|
||||
<br />
|
||||
{result.type} {result.osmId}
|
||||
</Popup>
|
||||
{showResultLabels ? (
|
||||
<Tooltip permanent direction="top" offset={[0, -10]} opacity={0.94}>
|
||||
{result.name ?? `${result.type} ${result.osmId}`}
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</Marker>
|
||||
))}
|
||||
</MapContainer>
|
||||
@@ -99,6 +117,33 @@ function ViewportReporter({
|
||||
return null;
|
||||
}
|
||||
|
||||
function FitResults({
|
||||
results,
|
||||
enabled,
|
||||
}: {
|
||||
results: ParsedOsmResult[];
|
||||
enabled: boolean;
|
||||
}) {
|
||||
const map = useMap();
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled || results.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bounds = L.latLngBounds(results.map((result) => [result.lat, result.lon]));
|
||||
|
||||
if (bounds.isValid()) {
|
||||
map.fitBounds(bounds.pad(0.2), {
|
||||
animate: true,
|
||||
maxZoom: 16,
|
||||
});
|
||||
}
|
||||
}, [enabled, map, results]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function SelectedResultFocus({ result }: { result: ParsedOsmResult | null }) {
|
||||
const map = useMap();
|
||||
|
||||
|
||||
57
src/features/map/mapStyles.ts
Normal file
57
src/features/map/mapStyles.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
export type MapStyleId =
|
||||
| 'osm-standard'
|
||||
| 'osm-humanitarian'
|
||||
| 'carto-light'
|
||||
| 'carto-dark';
|
||||
|
||||
export type MapTileStyle = {
|
||||
id: MapStyleId;
|
||||
label: string;
|
||||
description: string;
|
||||
url: string;
|
||||
attribution: string;
|
||||
maxZoom?: number;
|
||||
};
|
||||
|
||||
export const mapTileStyles: MapTileStyle[] = [
|
||||
{
|
||||
id: 'osm-standard',
|
||||
label: 'OpenStreetMap Standard',
|
||||
description: 'Default OSM raster tiles with broad detail.',
|
||||
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
attribution:
|
||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||
maxZoom: 19,
|
||||
},
|
||||
{
|
||||
id: 'osm-humanitarian',
|
||||
label: 'OpenStreetMap Humanitarian',
|
||||
description: 'Higher-contrast OSM style focused on humanitarian mapping.',
|
||||
url: 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
|
||||
attribution:
|
||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Tiles style by HOT',
|
||||
maxZoom: 20,
|
||||
},
|
||||
{
|
||||
id: 'carto-light',
|
||||
label: 'CARTO Positron',
|
||||
description: 'Light basemap that keeps point results prominent.',
|
||||
url: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',
|
||||
attribution:
|
||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
maxZoom: 20,
|
||||
},
|
||||
{
|
||||
id: 'carto-dark',
|
||||
label: 'CARTO Dark Matter',
|
||||
description: 'Dark basemap for high-contrast result exploration.',
|
||||
url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
|
||||
attribution:
|
||||
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
maxZoom: 20,
|
||||
},
|
||||
];
|
||||
|
||||
export function getMapTileStyle(id: MapStyleId): MapTileStyle {
|
||||
return mapTileStyles.find((style) => style.id === id) ?? mapTileStyles[0];
|
||||
}
|
||||
Reference in New Issue
Block a user