UNPKG

@pirireis/react-carto-map-gl

Version:

269 lines (242 loc) 6.24 kB
import React, {createRef, PureComponent} from 'react'; import PropTypes from 'prop-types'; import mapboxgl from 'mapbox-gl'; import {Deck} from '@deck.gl/core'; import MapboxCompare from 'mapbox-gl-compare'; import MapContext from '../MapContext/MapContext'; import '../../css/style.css'; class Map extends PureComponent { static propTypes = { /** * Children of Map can be Source, Layer or any node like Controls. */ children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.node), PropTypes.node, ]).isRequired, /** * Compares two maps. */ compare: PropTypes.bool, /** * CSS style of map container. */ containerStyle: PropTypes.object, /** * Basemap url of map. */ mapStyle: PropTypes.string.isRequired, /** * Center latitute/longitude of map. */ center: PropTypes.arrayOf(PropTypes.number), /** * Initial min zoom. */ minZoom: PropTypes.number, /** * Initial max zoom. */ maxZoom: PropTypes.number, /** * Initial zoom. */ zoom: PropTypes.number, /** * Mapbox Access token. */ token: PropTypes.string, /** * Callback function that will trigger on maps <b>style.load</b> event. <br /> * @param map mapbox map */ onMapReady: PropTypes.func, /** * Callback function that transforms mapbox requests. * @param string url * @param ResourceType resourceType * * @return RequestParameters * * @see node_modules/mapbox-gl/src/util/ajax.js */ transformRequest: PropTypes.func, }; static defaultProps = { center: [32.866287, 39.925533], zoom: 5, compare: false, }; constructor(props) { super(props); if (props.token) mapboxgl.accessToken = props.token; this.wrapperContainer = createRef(); this.container = createRef(); this.deckCanvas = createRef(); this.afterContainer = createRef(); this.compare = createRef(); } state = {ready: true}; componentDidMount() { this.createMap(); } componentDidUpdate(prevProps, prevState, snapshot) { const {map} = this.context; if (this.props.mapStyle !== prevProps.mapStyle) { // eslint-disable-next-line react/no-did-update-set-state this.setState({ready: false}, () => { map.setStyle(this.props.mapStyle); this.context.clearContext(); map.once('styledata', () => { setTimeout(() => { this.setState({ready: true}); }, 300); }); }); } if (this.props.minZoom !== prevProps.minZoom) { map.setMinZoom(this.props.minZoom); } if (this.props.maxZoom !== prevProps.maxZoom) { map.setMaxZoom(this.props.maxZoom); } if (this.props.compare !== prevProps.compare) { // eslint-disable-next-line react/no-did-update-set-state if (this.compare.current) this.compare.current.remove(); // this.context.clearContext(); this.createMap(); this.forceUpdate(); } } static contextType = MapContext; createMap() { const { mapStyle, afterMapStyle, center, zoom, minZoom, maxZoom, preserveDrawingBuffer, compare, transformRequest, } = this.props; if (compare) { this.context.setCompare(null); this.context.setMap(null); this.context.setAfterMap(null); const map = new mapboxgl.Map({ container: this.container.current, style: mapStyle, center: center || undefined, zoom: zoom || undefined, minZoom: minZoom || undefined, maxZoom: maxZoom || undefined, preserveDrawingBuffer: preserveDrawingBuffer || false, transformRequest: transformRequest || undefined, }); const afterMap = new mapboxgl.Map({ container: this.afterContainer.current, style: afterMapStyle || mapStyle, center: center || undefined, zoom: zoom || undefined, preserveDrawingBuffer: preserveDrawingBuffer || false, transformRequest: transformRequest || undefined, }); const compareMap = new MapboxCompare( map, afterMap, this.wrapperContainer.current, // options ); this.compare.current = compareMap; map.once('style.load', () => { this.context.setCompare(compare); this.context.setMap(map); this.context.setAfterMap(afterMap); map.resize(); this.props.onMapReady && this.props.onMapReady({map, afterMap, compareMap}); }); } else { this.context.setMap(null); const map = new mapboxgl.Map({ container: this.container.current, style: mapStyle, center: center || undefined, zoom: zoom || undefined, minZoom: minZoom || undefined, maxZoom: maxZoom || undefined, preserveDrawingBuffer: preserveDrawingBuffer || false, transformRequest: transformRequest || undefined, }); const deck = new Deck({ canvas: this.deckCanvas.current, width: '100%', height: '100%', initialViewState: { latitude: map.getCenter().lat, longitude: map.getCenter().lng, zoom: map.getZoom(), bearing: map.getBearing(), pitch: map.getPitch(), }, controller: true, onViewStateChange: view => { this.props.onViewStateChange(view); map.jumpTo({ center: [view.viewState.longitude, view.viewState.latitude], zoom: view.viewState.zoom, bearing: view.viewState.bearing, pitch: view.viewState.pitch, }); }, layers: [], }); this.deck = deck; this.context.setDeck(deck); map.resize(); map.once('idle', () => { this.context.setMap(map); this.props.onMapReady && this.props.onMapReady({map}); }); } } render() { const {ready} = this.state; const {containerStyle, children, compare} = this.props; const {map, afterMap} = this.context; if (compare) { return ( <> <div ref={this.wrapperContainer} style={containerStyle}> <div ref={this.container} style={containerStyle} className="swipemap" /> <div ref={this.afterContainer} style={containerStyle} className="swipemap" /> </div> {ready && map && afterMap && children} </> ); } else { return ( <> <div ref={this.container} style={containerStyle} /> <canvas ref={this.deckCanvas} className="deck-canvas" style={{top: 0, left: 0}} /> {ready && map && children} </> ); } } } export default Map;