From 0cc537a901e77faf693df19543322ced86555473 Mon Sep 17 00:00:00 2001 From: MarcosPierasNL <pieras.marcos@gmail.com> Date: Wed, 4 Sep 2024 15:18:45 +0200 Subject: [PATCH] feat(mapvis): export png from mapvis --- .../lib/vis/visualizations/mapvis/mapvis.tsx | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx index 6890056ec..c37a2cae6 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useCallback, useState, useRef } from 'react'; -import DeckGL from '@deck.gl/react'; +import React, { useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react'; +import DeckGL, { DeckGLProps, DeckGLRef } from '@deck.gl/react'; import { CompositeLayer, FlyToInterpolator, WebMercatorViewport } from '@deck.gl/core'; import { CompositeLayerType, Coordinate, LayerSettingsType, LocationInfo, SearchResultType } from './mapvis.types'; import { VISComponentType, VisualizationPropTypes } from '../../common'; @@ -30,7 +30,7 @@ const INITIAL_VIEW_STATE = { const FLY_SPEED = 1000; -export const MapVis = (props: VisualizationPropTypes<MapProps>) => { +export const MapVis = forwardRef((props: VisualizationPropTypes<MapProps>, refExternal) => { const ref = useRef<HTMLDivElement>(null); const baseLayer = useRef(createBaseMap()); const [viewport, setViewport] = useState<Record<string, any>>(INITIAL_VIEW_STATE); @@ -42,6 +42,50 @@ export const MapVis = (props: VisualizationPropTypes<MapProps>) => { const [searchResult, setSearchResult] = useState<SearchResultType | undefined>(undefined); const coordinateLookup = useCoordinateLookup(props.data.nodes, props.settings.location); + const [shouldExport, setShouldExport] = useState<boolean>(false); + + const captureImage = () => { + const canvas = ref.current?.querySelector('canvas'); + + if (canvas) { + canvas.toBlob((blob) => { + if (blob) { + const link = document.createElement('a'); + link.download = 'map-visualization.png'; + link.href = URL.createObjectURL(blob); + link.click(); + } else { + console.error('Failed to capture canvas'); + } + }, 'image/png'); + } else { + console.error('Canvas element not found'); + } + }; + + const exportImageInternal = () => { + requestAnimationFrame(() => { + captureImage(); + props.updateSettings({ export2PNG: false }); + }); + }; + // !FIXME I have to wrap exportImageInternal() with a useEffect otherwise didnt work, by just calling exportImageInternal() from outside + useEffect(() => { + if (props.settings.export2PNG) { + exportImageInternal(); + } + }, [props.settings.export2PNG]); + + useEffect(() => { + if (shouldExport) { + exportImageInternal(); + setShouldExport(false); + } + }, [shouldExport]); + + useImperativeHandle(refExternal, () => ({ + exportImageInternal: () => setShouldExport(true), + })); const getFittedViewport = useCallback( (minLat: number, maxLat: number, minLon: number, maxLon: number) => { @@ -228,17 +272,22 @@ export const MapVis = (props: VisualizationPropTypes<MapProps>) => { <Attribution /> </div> ); -}; +}); + +const mapRef = React.createRef<{ exportImageInternal: () => void }>(); const MapComponent: VISComponentType<MapProps> = { displayName: 'MapVis', description: 'Geographical Features', - component: MapVis, + component: React.forwardRef((props: VisualizationPropTypes<MapProps>, ref) => <MapVis {...props} ref={mapRef} />), settingsComponent: MapSettings, settings: settings, exportImage: () => { - alert('Not yet supported'); + if (mapRef.current) { + mapRef.current.exportImageInternal(); + } else { + console.error('Map reference is not set.'); + } }, }; - export default MapComponent; -- GitLab