-
-
Save caseycesari/470d6c0037da363c9864b0b81c1c9b63 to your computer and use it in GitHub Desktop.
ReactPDF MapBox Map example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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