Skip to content

Instantly share code, notes, and snippets.

@caseycesari
Last active September 27, 2022 16:26
Show Gist options
  • Save caseycesari/470d6c0037da363c9864b0b81c1c9b63 to your computer and use it in GitHub Desktop.
Save caseycesari/470d6c0037da363c9864b0b81c1c9b63 to your computer and use it in GitHub Desktop.
ReactPDF MapBox Map example
import { usePDF, Document, Page, Image } from '@react-pdf/renderer';
import Map, { Source, Layer } from 'react-map-gl';
import { useRef, useState, useEffect } from 'react';
import { debounce } from 'lodash';
const MyDoc = ({ mapImage }) => (
<Document>
<Page>
<Image width='600px' height='400px' src={mapImage} />
</Page>
</Document>
);
const Demo = () => {
const [isPDFAllowedToUpdate, setIsPDFAllowedToUpdate] = useState(false);
const [isPDFReadyForExport, setIsPDFReadyForExport] = useState(false);
const [mapImage, setMapImage] = useState(null);
const [mapUpdateAssigned, setMapUpdateAssigned] = useState(false);
const mapRef = useRef(null);
const pdfDocument = <MyDoc mapImage={mapImage} />;
const data = {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[-67.13734, 45.13745],
[-66.96466, 44.8097],
[-68.03252, 44.3252],
[-69.06, 43.98],
[-70.11617, 43.68405],
[-70.64573, 43.09008],
[-70.75102, 43.08003],
[-70.79761, 43.21973],
[-70.98176, 43.36789],
[-70.94416, 43.46633],
[-71.08482, 45.30524],
[-70.66002, 45.46022],
[-70.30495, 45.91479],
[-70.00014, 46.69317],
[-69.23708, 47.44777],
[-68.90478, 47.18479],
[-68.2343, 47.35462],
[-67.79035, 47.06624],
[-67.79141, 45.70258],
[-67.13734, 45.13745],
],
],
},
};
const layer = {
id: 'maine',
type: 'fill',
source: 'maine',
layout: {},
paint: {
'fill-color': '#0080ff',
'fill-opacity': 0.5,
},
};
const updateMapImage = () => {
const mapImage = mapRef?.current
?.getMap()
.getCanvas()
.toDataURL('image/png');
setMapImage(mapImage);
};
const startPDFUpdateProcess = () => {
setIsPDFAllowedToUpdate(true);
};
const [pdfInstance, updatePdfInstance] = usePDF({ document: pdfDocument });
useEffect(() => {
if (isPDFAllowedToUpdate) {
updatePdfInstance();
setIsPDFReadyForExport(true);
}
// We disable exhausive deps because eslint was asking to add
// updatePdfInstance to the deps array but doing so caused an infinite loop
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isPDFAllowedToUpdate, mapImage]);
const { blob: pdfBlob, loading: pdfLoading, error: pdfError } = pdfInstance;
useEffect(() => {
if (isPDFReadyForExport && !pdfLoading) {
// https://dev.to/nombrekeff/download-file-from-blob-21ho
downloadBlob({
blob: pdfBlob,
});
// Reset the PDF generation flags
setIsPDFAllowedToUpdate(false);
setIsPDFReadyForExport(false);
}
}, [isPDFReadyForExport, pdfLoading, pdfBlob]);
useEffect(() => {
if (pdfError) {
// Reset the PDF generation flags
setIsPDFAllowedToUpdate(false);
setIsPDFReadyForExport(false);
}
}, [pdfError]);
// Ideally, this hook would have the mapRef as a dependency
// so that the events are only assigned once. However,
// adding mapRef as a dependency caused React to throw some errors.
// Instead, the mapUpdateAssigned state is used to ensure
// the event listeners are only assigned once.
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
if (mapRef.current && !mapUpdateAssigned) {
const map = mapRef.current.getMap();
const debouncedGetMapImage = debounce(updateMapImage, 500);
map.on('moveend', () => {
debouncedGetMapImage();
});
map.on('data', () => {
debouncedGetMapImage();
});
setMapUpdateAssigned(true);
}
// React wants the setMapUpdateAssigned function as
// dependency of this hook. However, we don't want the hook
// to run when those variables change. Adding an empty dependency
// array (which makes the hook run once after the component
// is rendered) would be ideal except for the fact that mapRef
// was undefined when hook first ran, which prevented the event
// from being assigned.
});
return (
<>
<Map
longitude={-68.1373}
latitude={45.1375}
zoom={5}
width='100%'
height='100%'
mapStyle='mapbox://styles/mapbox/streets-v9'
ref={mapRef}
mapboxApiAccessToken={''}
preserveDrawingBuffer
>
<Source type='geojson' data={data}>
<Layer {...layer} />
</Source>
</Map>
<a onClick={startPDFUpdateProcess}>Download PDF</a>
</>
);
};
export default Demo;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment