2016-11-09 21:13:43 +00:00
|
|
|
import React from 'react';
|
|
|
|
import '../css/Map.scss';
|
2016-11-11 16:00:31 +00:00
|
|
|
//import 'leaflet.css';
|
|
|
|
import 'leaflet/dist/leaflet.css';
|
|
|
|
import 'leaflet-basemaps/L.Control.Basemaps.css';
|
|
|
|
import Leaflet from 'leaflet';
|
|
|
|
import 'leaflet-basemaps/L.Control.Basemaps';
|
2016-11-09 21:13:43 +00:00
|
|
|
import $ from 'jquery';
|
|
|
|
import ErrorMessage from './ErrorMessage';
|
|
|
|
|
|
|
|
class Map extends React.Component {
|
|
|
|
static defaultProps = {
|
|
|
|
maxzoom: 18,
|
|
|
|
minzoom: 0,
|
|
|
|
showBackground: false,
|
2016-11-16 18:02:43 +00:00
|
|
|
opacity: 100
|
|
|
|
};
|
2016-11-09 21:13:43 +00:00
|
|
|
|
2016-11-14 21:32:05 +00:00
|
|
|
static propTypes = {
|
2016-11-16 18:02:43 +00:00
|
|
|
maxzoom: React.PropTypes.number,
|
|
|
|
minzoom: React.PropTypes.number,
|
2016-11-14 21:32:05 +00:00
|
|
|
showBackground: React.PropTypes.bool,
|
2016-11-16 18:02:43 +00:00
|
|
|
tiles: React.PropTypes.array.isRequired,
|
|
|
|
opacity: React.PropTypes.number
|
|
|
|
};
|
2016-11-09 21:13:43 +00:00
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = {
|
2016-11-16 18:02:43 +00:00
|
|
|
error: "",
|
|
|
|
bounds: null
|
2016-11-09 21:13:43 +00:00
|
|
|
};
|
2016-11-16 18:02:43 +00:00
|
|
|
|
|
|
|
this.imageryLayers = [];
|
2016-11-09 21:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
2016-11-16 18:02:43 +00:00
|
|
|
const { showBackground, tiles } = this.props;
|
2016-11-09 21:13:43 +00:00
|
|
|
|
|
|
|
this.leaflet = Leaflet.map(this.container, {
|
2016-11-16 18:02:43 +00:00
|
|
|
scrollWheelZoom: true
|
2016-11-09 21:13:43 +00:00
|
|
|
});
|
|
|
|
|
2016-11-11 16:00:31 +00:00
|
|
|
if (showBackground) {
|
|
|
|
const basemaps = [
|
2016-11-11 17:55:56 +00:00
|
|
|
L.tileLayer('//{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', {
|
|
|
|
attribution: 'Map data: © Google Maps',
|
|
|
|
subdomains: ['mt0','mt1','mt2','mt3'],
|
|
|
|
maxZoom: 22,
|
|
|
|
minZoom: 0,
|
|
|
|
label: 'Google Maps Hybrid'
|
|
|
|
}),
|
2016-11-11 16:00:31 +00:00
|
|
|
L.tileLayer('//server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
|
|
|
attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community',
|
|
|
|
maxZoom: 22,
|
|
|
|
minZoom: 0,
|
|
|
|
label: 'ESRI Satellite' // optional label used for tooltip
|
|
|
|
}),
|
|
|
|
L.tileLayer('//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
|
|
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
|
|
maxZoom: 22,
|
|
|
|
minZoom: 0,
|
|
|
|
label: 'OSM Mapnik' // optional label used for tooltip
|
|
|
|
})
|
|
|
|
];
|
|
|
|
|
|
|
|
this.leaflet.addControl(Leaflet.control.basemaps({
|
|
|
|
basemaps: basemaps,
|
|
|
|
tileX: 0, // tile X coordinate
|
|
|
|
tileY: 0, // tile Y coordinate
|
|
|
|
tileZ: 1 // tile zoom level
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2016-11-16 18:02:43 +00:00
|
|
|
this.leaflet.fitWorld();
|
2016-11-09 21:13:43 +00:00
|
|
|
|
|
|
|
Leaflet.control.scale({
|
|
|
|
maxWidth: 250,
|
|
|
|
}).addTo(this.leaflet);
|
|
|
|
this.leaflet.attributionControl.setPrefix("");
|
|
|
|
|
2016-11-16 18:02:43 +00:00
|
|
|
this.tileJsonRequests = [];
|
2016-11-09 21:13:43 +00:00
|
|
|
|
2016-11-16 18:02:43 +00:00
|
|
|
tiles.forEach(tile => {
|
|
|
|
const { url, meta } = tile;
|
2016-11-09 21:13:43 +00:00
|
|
|
|
2016-11-16 18:02:43 +00:00
|
|
|
this.tileJsonRequests.push($.getJSON(url)
|
|
|
|
.done(info => {
|
|
|
|
const bounds = [info.bounds.slice(0, 2).reverse(), info.bounds.slice(2, 4).reverse()];
|
|
|
|
|
|
|
|
const layer = Leaflet.tileLayer(info.tiles[0], {
|
|
|
|
bounds,
|
|
|
|
minZoom: info.minzoom,
|
|
|
|
maxZoom: info.maxzoom,
|
|
|
|
tms: info.scheme === 'tms'
|
|
|
|
}).addTo(this.leaflet);
|
2016-11-09 21:13:43 +00:00
|
|
|
|
2016-11-16 18:02:43 +00:00
|
|
|
// Associate metadata with this layer
|
|
|
|
layer[Symbol.for("meta")] = meta;
|
|
|
|
|
|
|
|
this.imageryLayers.push(layer);
|
|
|
|
|
|
|
|
let mapBounds = this.state.bounds || Leaflet.latLngBounds(bounds);
|
|
|
|
mapBounds.extend(bounds);
|
|
|
|
this.setState({bounds: mapBounds});
|
|
|
|
})
|
|
|
|
.fail((_, __, err) => this.setState({error: err.message}))
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidUpdate() {
|
|
|
|
const { bounds } = this.state;
|
|
|
|
|
|
|
|
if (bounds) this.leaflet.fitBounds(bounds);
|
|
|
|
|
|
|
|
this.imageryLayers.forEach(imageryLayer => {
|
|
|
|
imageryLayer.setOpacity(this.props.opacity / 100);
|
|
|
|
});
|
2016-11-09 21:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
this.leaflet.remove();
|
2016-11-16 18:02:43 +00:00
|
|
|
|
|
|
|
if (this.tileJsonRequests) {
|
|
|
|
this.tileJsonRequests.forEach(tileJsonRequest => this.tileJsonRequest.abort());
|
|
|
|
this.tileJsonRequests = [];
|
|
|
|
}
|
2016-11-09 21:13:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div style={{height: "100%"}}>
|
2016-11-14 15:58:00 +00:00
|
|
|
<ErrorMessage bind={[this, 'error']} />
|
2016-11-09 21:13:43 +00:00
|
|
|
<div
|
|
|
|
style={{height: "100%"}}
|
|
|
|
ref={(domNode) => (this.container = domNode)}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default Map;
|