import React, { Component, Fragment, useEffect } from 'react'; import { AttributionControl, MapContainer, ZoomControl, useMapEvent, Pane, useMap } from 'react-leaflet'; import 'leaflet/dist/leaflet.css'; import './map.css'; import { apiGet } from '../apiHelpers'; import { HelpIcon } from '../components/icons'; import { categoryMapsConfig } from '../config/category-maps-config'; import { Category } from '../config/categories-config'; import { initialMapViewport, mapBackgroundColor, MapTheme } from '../config/map-config'; import { Building } from '../models/building'; import { CityBaseMapLayer } from './layers/city-base-map-layer'; import { CityBoundaryLayer } from './layers/city-boundary-layer'; import { BuildingBaseLayer } from './layers/building-base-layer'; import { BuildingDataLayer } from './layers/building-data-layer'; import { BuildingNumbersLayer } from './layers/building-numbers-layer'; import { BuildingHighlightLayer } from './layers/building-highlight-layer'; import Legend from './legend'; import SearchBox from './search-box'; import ThemeSwitcher from './theme-switcher'; interface ColouringMapProps { selectedBuildingId: number; mode: 'basic' | 'view' | 'edit' | 'multi-edit'; category: Category; revisionId: string; onBuildingAction: (building: Building) => void; } interface ColouringMapState { theme: MapTheme; position: [number, number]; zoom: number; } /** * Map area */ class ColouringMap extends Component { constructor(props) { super(props); this.state = { theme: 'light', ...initialMapViewport }; this.handleClick = this.handleClick.bind(this); this.handleLocate = this.handleLocate.bind(this); this.themeSwitch = this.themeSwitch.bind(this); } handleLocate(lat: number, lng: number, zoom: number){ this.setState({ position: [lat, lng], zoom: zoom }); } handleClick(e) { const { lat, lng } = e.latlng; apiGet(`/api/buildings/locate?lat=${lat}&lng=${lng}`) .then(data => { const building = data?.[0]; this.props.onBuildingAction(building); }).catch(err => console.error(err)); } themeSwitch(e) { e.preventDefault(); const newTheme = (this.state.theme === 'light')? 'night' : 'light'; this.setState({theme: newTheme}); } render() { const categoryMapDefinition = categoryMapsConfig[this.props.category]; const tileset = categoryMapDefinition.mapStyle; const hasSelection = this.props.selectedBuildingId != undefined; const isEdit = ['edit', 'multi-edit'].includes(this.props.mode); return (
{ tileset && } { this.props.selectedBuildingId && } { this.props.mode !== 'basic' && { !hasSelection &&
{isEdit ? 'Click a building to edit' : 'Click a building for details'}
} {/* */}
}
); } } function ClickHandler({ onClick }: {onClick: (e) => void}) { useMapEvent('click', (e) => onClick(e)); return null; } function MapBackgroundColor({ theme}: {theme: MapTheme}) { const map = useMap(); useEffect(() => { map.getContainer().style.backgroundColor = mapBackgroundColor[theme]; }); return null; } function MapViewport({ position, zoom }: { position: [number, number]; zoom: number; }) { const map = useMap(); useEffect(() => { map.setView(position, zoom) }, [position, zoom]); return null; } export default ColouringMap;