import React from 'react' import {throttle} from 'lodash'; import MapMaplibreGlLayerPopup from './MapMaplibreGlLayerPopup'; import 'ol/ol.css' import {apply} from 'ol-mapbox-style'; import {Map, View, Overlay} from 'ol'; import {toLonLat} from 'ol/proj'; import type {StyleSpecification} from 'maplibre-gl'; function renderCoords (coords: string[]) { if (!coords || coords.length < 2) { return null; } else { return {coords.map((coord) => String(coord).padStart(7, "\u00A0")).join(', ')} } } type MapOpenLayersProps = { onDataChange?(...args: unknown[]): unknown mapStyle: object accessToken?: string style?: object onLayerSelect(...args: unknown[]): unknown debugToolbox: boolean replaceAccessTokens(...args: unknown[]): unknown onChange(...args: unknown[]): unknown }; type MapOpenLayersState = { zoom: string rotation: string cursor: string[] center: string[] selectedFeatures?: any[] }; export default class MapOpenLayers extends React.Component { static defaultProps = { onMapLoaded: () => {}, onDataChange: () => {}, onLayerSelect: () => {}, } updateStyle: any; map: any; container: HTMLDivElement | null = null; overlay: Overlay | undefined; popupContainer: HTMLElement | null = null; constructor(props: MapOpenLayersProps) { super(props); this.state = { zoom: "0", rotation: "0", cursor: [] as string[], center: [], }; this.updateStyle = throttle(this._updateStyle.bind(this), 200); } _updateStyle(newMapStyle: StyleSpecification) { if(!this.map) return; // See this.map.getLayers().clear(); apply(this.map, newMapStyle); } componentDidUpdate(prevProps: MapOpenLayersProps) { if (this.props.mapStyle !== prevProps.mapStyle) { this.updateStyle( this.props.replaceAccessTokens(this.props.mapStyle) ); } } componentDidMount() { this.overlay = new Overlay({ element: this.popupContainer!, autoPan: true, autoPanAnimation: { duration: 250 } }); const map = new Map({ target: this.container!, overlays: [this.overlay], view: new View({ zoom: 1, center: [180, -90], }) }); map.on('pointermove', (evt) => { const coords = toLonLat(evt.coordinate); this.setState({ cursor: [ coords[0].toFixed(2), coords[1].toFixed(2) ] }) }) const onMoveEnd = () => { const zoom = map.getView().getZoom(); const center = toLonLat(map.getView().getCenter()!); this.props.onChange({ zoom, center: { lng: center[0], lat: center[1], }, }); } onMoveEnd(); map.on('moveend', onMoveEnd); map.on('postrender', (_e) => { const center = toLonLat(map.getView().getCenter()!); this.setState({ center: [ center[0].toFixed(2), center[1].toFixed(2), ], rotation: map.getView().getRotation().toFixed(2), zoom: map.getView().getZoom()!.toFixed(2) }); }); this.map = map; this.updateStyle( this.props.replaceAccessTokens(this.props.mapStyle) ); } closeOverlay = (e: any) => { e.target.blur(); this.overlay!.setPosition(undefined); } render() { return
this.popupContainer = x} style={{background: "black"}} className="maputnik-popup" >
Zoom: {this.state.zoom}
{this.props.debugToolbox &&
{renderCoords(this.state.cursor)}
{renderCoords(this.state.center)}
{this.state.rotation}
}
this.container = x} role="region" aria-label="Map view" style={{ ...this.props.style, }}>
} }