Skip to content

Instantly share code, notes, and snippets.

@nautilytics
Created January 26, 2021 21:16
Show Gist options
  • Save nautilytics/6c62979c31ab16efebfee26c81c358fb to your computer and use it in GitHub Desktop.
Save nautilytics/6c62979c31ab16efebfee26c81c358fb to your computer and use it in GitHub Desktop.
Rendering a performant Mapbox census tract map using react-map-gl and React Hooks
import * as React from 'react';
import ReactMapGL, { Layer, Source } from 'react-map-gl';
import { useEffect, useRef, useState } from 'react';
import { DATA_PROPERTY, LAYER_PROPERTIES, FILL_COLOR_PAINT, SELECTED_LAYER } from '../../constants';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
map: {
width: '100%',
height: '100%',
},
}));
const Map = (props) => {
const classes = useStyles();
const [state, setState] = useState({
width: 500,
height: 300,
activeFeature: null,
x: 0,
y: 0,
});
const mapRef = useRef(null);
const mapboxRef = useRef(null);
useEffect(() => {
const { offsetWidth, offsetHeight } = mapRef.current;
setState((prevState) => ({
...prevState,
width: offsetWidth,
height: offsetHeight,
}));
}, []);
const _onViewportChange = (viewport) =>
props.setState((prevState) => {
return {
...prevState,
viewport,
};
});
return (
<div className={classes.map} ref={mapRef}>
<ReactMapGL
{...props.state.viewport}
ref={mapboxRef}
width="100%"
height="100%"
onMouseLeave={() => {
const map = mapboxRef.current.getMap();
map.removeFeatureState({
source: "tractsclippedbystatewithdata",
sourceLayer: "tractsclippedbystatewithdata",
});
}}
onMouseMove={(event) => {
const {
srcEvent: { offsetX, offsetY },
} = event;
const point = [event.center.x, event.center.y];
const map = mapboxRef.current.getMap();
let feature;
try {
feature = map.queryRenderedFeatures(point, { layers: ["fill_layer"] })[0];
} catch (err) {
return;
}
if (feature) {
if (state.activeFeature) {
map.removeFeatureState({
source: "tractsclippedbystatewithdata",
sourceLayer: "tractsclippedbystatewithdata",
});
}
map.setFeatureState(
{
source: "tractsclippedbystatewithdata",
sourceLayer: "tractsclippedbystatewithdata",
id: feature.id,
},
{
hover: true,
},
);
}
setState((prevState) => ({
...prevState,
activeFeature: feature,
x: offsetX,
y: offsetY,
}));
}}
mapStyle={"mapbox://styles/graphicacy/ckhs3vpu21oqb19nyrsvzaq8n"}
onViewportChange={_onViewportChange}
mapboxApiAccessToken={process.env.REACT_APP_MAPBOX_TOKEN}
>
<Source id={"tractsclippedbystatewithdata"} type="vector" url={"mapbox://graphicacy.d4j3v1uy"} />
<Layer
type="fill"
id="fill_layer"
filter={["has", "total_est"]}
source={"tractsclippedbystatewithdata"}
source-layer={"tractsclippedbystatewithdata"}
paint={
{"fill-color":["step",["get","total_est"],
"#eff3ff",36836,
"#c6dbef",52390,
"#9ecae1",69315,
"#6baed6",91857,
"#4292c6",120714,
"#2171b5",166635,
"#084594",250002,
"#fff"
],
"fill-opacity":1
}
}
/>
<Layer
type="line"
source={"tractsclippedbystatewithdata"}
source-layer={"tractsclippedbystatewithdata"}
paint={{
"line-width": ["case", ["boolean", ["feature-state", "hover"], false], 3, 0.5],
"line-color": ["case", ["boolean", ["feature-state", "hover"], false], "#000", "#969090"],
"line-opacity": 0.5,
}}
/>
</ReactMapGL>
</div>
);
};
export default Map;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment