@pirireis/react-carto-map-gl
Version:
269 lines (242 loc) • 6.24 kB
JavaScript
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;