Skip to content

Instantly share code, notes, and snippets.

@eddydg
Last active October 30, 2017 14:22
Show Gist options
  • Save eddydg/acc0ef21a2f0c5a54918ac5308f89865 to your computer and use it in GitHub Desktop.
Save eddydg/acc0ef21a2f0c5a54918ac5308f89865 to your computer and use it in GitHub Desktop.
wrOrro
<div id='map'></div>
mapboxgl.accessToken = 'pk.eyJ1IjoiZWRkeWRnIiwiYSI6ImNqOGZ6bmU2YTA5ZXQzM25yeTl2bG5lb24ifQ.Nkc9Tn6VNRGaV4lqWH5rxg';
const getStateUrl = stateCode => {
const c = stateCode.toLowerCase();
return `https://code.highcharts.com/mapdata/countries/${c}/${c}-all.geo.json`;
}
const options = [{
name: 'Population',
description: 'Estimated total population',
property: 'POP_EST',
stops: [
[0, '#595367'],
[400000000, '#eee']
]
}, {
name: 'GDP',
description: 'Estimate total GDP in millions of dollars',
property: 'GDP_MD_EST',
stops: [
[0, '#f8d5cc'],
[1000, '#f4bfb6'],
[5000, '#f1a8a5'],
[10000, '#ee8f9a'],
[50000, '#ec739b'],
[100000, '#dd5ca8'],
[250000, '#c44cc0'],
[5000000, '#9f43d7'],
[10000000, '#6e40e6']
]
}];
const specialCountries = ['PRK', 'CHN'];
let data = {};
fetch('https://cdn.rawgit.com/eddydg/mapbox-countries/9b0c7d0c/assets/ne_110m_admin_0_countries_lakes_independent_guyane_converted.geojson')
.then(resp => resp.json())
.then(d => data = d);
class Application extends React.Component {
map;
constructor(props: Props) {
super(props);
this.state = {
active: options[0],
country: ''
};
}
componentDidUpdate() {
this.setFill();
}
getTooltip(mouseEvent) {
const mousePosition = mouseEvent.point;
const {height, width} = this.map.transform;
const tooltipOffset = this.getTooltipOffset({height, width}, mousePosition);
return new mapboxgl.Marker(this.tooltipContainer, {
offset: tooltipOffset
}).setLngLat(mouseEvent.lngLat)
}
componentDidMount() {
this.map = new mapboxgl.Map({
container: this.mapContainer,
style: 'mapbox://styles/eddydg/cj8zr0o4pj5tf2rnrsz0eai8o',
center: [5, 34],
zoom: 1.5
});
this.map.on('click', 'countries', e => {
if (this.state.country) {
// When zooming on a country, disable any other interaction
return;
}
const features = this.map.queryRenderedFeatures(e.point, {layers: ['countries']});
if (features.length) {
const feature = features[0];
const countryCode = feature.properties.ADM0_A3;
if (specialCountries.includes(countryCode)) {
return;
}
const regionsUrl = 'https://raw.githubusercontent.com/eddydg/geojson-regions/master/data/geojson/' + countryCode + '_adm1.geojson';
this.setState({ country: countryCode });
const stateSource = this.map.getSource('states');
this.map.addSource('states', {
type: 'geojson',
data: regionsUrl
});
this.map.setPaintProperty('countries', 'fill-opacity', 0.1);
this.map.addLayer({
id: "states",
type:"fill",
source: "states",
"paint": {
"fill-color": "#595367",
"fill-outline-color": "#aaa"
}
}, 'countries-hatched');
let bbox = JSON.parse(feature.properties.bbox);
this.map.fitBounds(bbox, {padding: 20});
this.map.scrollZoom.disable();
this.map.dragPan.disable();
this.map.doubleClickZoom.disable();
this.map.keyboard.disable();
this.map.dragRotate.disable();
}
});
this.map.on('contextmenu', e => {
if (!this.state.country) {
return;
}
this.setState({ country: '' });
this.map.zoomTo(1.5);
this.map.setPaintProperty('countries', 'fill-opacity', 1);
this.map.removeLayer('states');
this.map.removeSource('states'); // Be sure this source is not used anymore
});
this.map.on('mousemove', 'countries', (e) => {
if (this.state.country) {
return;
}
const features = this.map.queryRenderedFeatures(e.point);
const countryCode = features[0].properties['ADM0_A3'];
this.map.setFilter("countries-hover", ['==', 'ADM0_A3', countryCode]);
this.getTooltip(e).addTo(this.map);
this.setTooltip(features);
if (specialCountries.includes(countryCode)) {
this.map.getCanvas().style.cursor = 'not-allowed';
} else {
this.map.getCanvas().style.cursor = features.length ? 'pointer' : '';
}
});
this.map.on('mouseout', 'countries', e => {
this.map.setFilter('countries-hover', ['==', 'admin', '']);
this.map.getCanvas().style.cursor = '';
this.setTooltip([]);
});
this.map.on('mousemove', 'states', (e) => {
const features = this.map.queryRenderedFeatures(e.point);
this.getTooltip(e).addTo(this.map);
this.setTooltip(features);
this.map.getCanvas().style.cursor = features.length ? 'pointer' : '';
});
this.map.on('mouseout', 'states', e => {
this.map.getCanvas().style.cursor = '';
this.setTooltip([]);
});
this.map.on('load', () => {
this.map.addSource('countries', {
type: 'geojson',
data: data
});
// const imgUrl = "https://i.imgur.com/3srHc8Z.png"
const imgUrl = "https://i.imgur.com/BBHuNOa.jpg";
this.map.loadImage(imgUrl, (err, image) => {
if (err) throw err;
this.map.addImage('strips', image);
this.map.addLayer({
id: 'countries-hatched',
type: 'fill',
source: 'countries',
paint: {
"fill-pattern": "strips",
"fill-opacity": 0.8
},
filter: ['in', 'ADM0_A3', ...specialCountries]
});
});
this.map.addLayer({
id: 'countries',
type: 'fill',
source: 'countries'
}, 'country-label-lg'); // ID matches `mapbox/streets-v9`
this.map.addLayer({
id: "countries-hover",
type:"line",
source: "countries",
"paint": {
"line-color": "#000",
"line-opacity": 0.1,
"line-width": 2
},
filter: ['==', 'ADM0_A3', '']
});
/* this.map.addLayer({
id: "countries-hover",
type:"fill",
source: "countries",
"paint": {
"fill-opacity": 0,
"fill-outline-color": "#000"
},
filter: ['==', 'ADM0_A3', '']
}); */
this.tooltipContainer = document.createElement('div');
this.setFill();
});
}
getTooltipOffset(mapSize, mousePosition) {
return [
mousePosition.x > mapSize.width * 2 / 3 ? -180 - 20 : 20,
mousePosition.y > mapSize.height / 4 ? -20 : 80 + 20
];
}
setTooltip(features) {
if (features.length) {
const f = features[0];
const countryName = f.properties.NAME || f.properties.NAME_1;
const pop = f.properties.POP_EST || '';
const tooltip = (
<div className="flex-parent-inline flex-parent--center-cross flex-parent--column absolute bottom">
<div className="flex-child px12 py12 bg-gray-dark color-white shadow-darken10 round txt-s w180 clip txt-truncate">
<div>
<strong className='mr3'>{countryName}</strong>
<br/>
<span className='color-gray-light'>{pop}</span>
</div>
</div>
<span className="flex-child color-gray-dark"></span>
</div>
);
ReactDOM.render(tooltip, this.tooltipContainer);
} else {
this.tooltipContainer.innerHTML = "";
}
}
setFill() {
const { property, stops } = this.state.active;
this.map.setPaintProperty('countries', 'fill-color', {
property,
stops
});
}
render() {
const { name, description, stops, property } = this.state.active;
const renderLegendKeys = (stop, i) => {
return (
<div key={i} className='txt-s'>
<span className='mr6 round-full w12 h12 inline-block align-middle' style={{ backgroundColor: stop[1] }} />
<span>{`${stop[0].toLocaleString()}`}</span>
</div>
);
}
const renderOptions = (option, i) => {
return (
<label key={i} className="toggle-container">
<input onChange={() => this.setState({ active: options[i] })} checked={option.property === property} name="toggle" type="radio" />
<div className="toggle txt-s py3 toggle--active-white">{option.name}</div>
</label>
);
}
return (
<div>
<div ref={el => this.mapContainer = el} className="absolute top right left bottom" />
<div className="toggle-group absolute top left ml12 mt12 border border--2 border--white bg-white shadow-darken10 z1">
{options.map(renderOptions)}
</div>
<div className="bg-white absolute bottom right mr12 mb24 py12 px12 shadow-darken10 round z1 wmax180">
<div className='mb6'>
<h2 className="txt-bold txt-s block">{name}</h2>
<p className='txt-s color-gray'>{description}</p>
</div>
{stops.map(renderLegendKeys)}
</div>
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('map'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<script src="https://api.mapbox.com/mapbox-assembly/mbx/v0.18.0/assembly.js"></script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment