diff --git a/client/package.json b/client/package.json index b0dee8277..b1a8ca79d 100644 --- a/client/package.json +++ b/client/package.json @@ -54,7 +54,6 @@ "@tanstack/react-query": "^4.2.1", "@tanstack/react-table": "8.13.2", "@tanstack/react-virtual": "3.0.1", - "@turf/bbox": "^6.5.0", "autoprefixer": "10.2.5", "axios": "1.3.4", "chroma-js": "2.1.2", diff --git a/client/src/containers/analysis-eudr-detail/map/component.tsx b/client/src/containers/analysis-eudr-detail/map/component.tsx deleted file mode 100644 index 8c557d6ee..000000000 --- a/client/src/containers/analysis-eudr-detail/map/component.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { useEffect, useState, useCallback } from 'react'; -import DeckGL from '@deck.gl/react/typed'; -import { GeoJsonLayer } from '@deck.gl/layers/typed'; -import Map from 'react-map-gl/maplibre'; -import { WebMercatorViewport, type MapViewState } from '@deck.gl/core/typed'; -import bbox from '@turf/bbox'; - -import ZoomControl from './zoom'; -import LegendControl from './legend'; - -import BasemapControl from '@/components/map/controls/basemap'; -import { INITIAL_VIEW_STATE, MAP_STYLES } from '@/components/map'; -import { usePlotGeometries } from '@/hooks/eudr'; - -import type { BasemapValue } from '@/components/map/controls/basemap/types'; -import type { MapStyle } from '@/components/map/types'; - -const EUDRMap = () => { - const [mapStyle, setMapStyle] = useState('terrain'); - const [viewState, setViewState] = useState(INITIAL_VIEW_STATE); - - const plotGeometries = usePlotGeometries(); - - const layer: GeoJsonLayer = new GeoJsonLayer({ - id: 'geojson-layer', - data: plotGeometries.data, - // Styles - filled: true, - getFillColor: [255, 176, 0, 84], - stroked: true, - getLineColor: [255, 176, 0, 255], - getLineWidth: 1, - lineWidthUnits: 'pixels', - // Interactive props - pickable: true, - autoHighlight: true, - highlightColor: [255, 176, 0, 255], - }); - - const layers = [layer]; - - const handleMapStyleChange = useCallback((newStyle: BasemapValue) => { - setMapStyle(newStyle); - }, []); - - const handleZoomIn = useCallback(() => { - const zoom = viewState.maxZoom === viewState.zoom ? viewState.zoom : viewState.zoom + 1; - setViewState({ ...viewState, zoom }); - }, [viewState]); - - const handleZoomOut = useCallback(() => { - const zoom = viewState.maxZoom === viewState.zoom ? viewState.zoom : viewState.zoom - 1; - setViewState({ ...viewState, zoom }); - }, [viewState]); - - const fitToPlotBounds = useCallback(() => { - if (!plotGeometries.data) return; - const [minLng, minLat, maxLng, maxLat] = bbox(plotGeometries.data); - const newViewport = new WebMercatorViewport(viewState); - const { longitude, latitude, zoom } = newViewport.fitBounds( - [ - [minLng, minLat], - [maxLng, maxLat], - ], - { - padding: 10, - }, - ); - if ( - viewState.latitude !== latitude || - viewState.longitude !== longitude || - viewState.zoom !== zoom - ) { - setViewState({ ...viewState, longitude, latitude, zoom }); - } - }, [plotGeometries.data, viewState]); - - // Fit to bounds when data is loaded or changed - useEffect(() => { - if (plotGeometries.data) { - fitToPlotBounds(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [plotGeometries.data]); - - const handleResize = useCallback(() => { - setTimeout(() => fitToPlotBounds(), 0); - }, [fitToPlotBounds]); - - return ( - <> - setViewState(viewState as MapViewState)} - controller={{ dragRotate: false }} - layers={layers} - onResize={handleResize} - > - - -
- - - -
- - ); -}; - -export default EUDRMap; diff --git a/client/src/containers/analysis-eudr-detail/map/index.ts b/client/src/containers/analysis-eudr-detail/map/index.ts deleted file mode 100644 index b404d7fd4..000000000 --- a/client/src/containers/analysis-eudr-detail/map/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './component'; diff --git a/client/src/containers/analysis-eudr-detail/map/legend/component.tsx b/client/src/containers/analysis-eudr-detail/map/legend/component.tsx deleted file mode 100644 index 54da03733..000000000 --- a/client/src/containers/analysis-eudr-detail/map/legend/component.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useState } from 'react'; -import classNames from 'classnames'; -import { MinusIcon, PlusIcon } from '@heroicons/react/outline'; - -import LegendItem from './item'; - -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; -import SandwichIcon from '@/components/icons/sandwich'; -import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; - -const EURDLegend = () => { - const [isOpen, setIsOpen] = useState(false); - const [isExpanded, setIsExpanded] = useState(false); - - return ( -
- - - - - -
-

Legend

-
- -
- - -
- -
-
- - - - -
-
-
-
-
- ); -}; - -export default EURDLegend; diff --git a/client/src/containers/analysis-eudr-detail/map/legend/index.ts b/client/src/containers/analysis-eudr-detail/map/legend/index.ts deleted file mode 100644 index b404d7fd4..000000000 --- a/client/src/containers/analysis-eudr-detail/map/legend/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './component'; diff --git a/client/src/containers/analysis-eudr-detail/map/legend/item.tsx b/client/src/containers/analysis-eudr-detail/map/legend/item.tsx deleted file mode 100644 index 3946119f2..000000000 --- a/client/src/containers/analysis-eudr-detail/map/legend/item.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import classNames from 'classnames'; - -import type { FC, PropsWithChildren } from 'react'; - -type LegendItemProps = { title: string; description: string; iconClassName?: string }; - -const LegendItem: FC> = ({ - title, - description, - children, - iconClassName, -}) => { - return ( -
-
-
-
-

{title}

-
-
-

{description}

- {children} -
-
- ); -}; - -export default LegendItem; diff --git a/client/src/containers/analysis-eudr-detail/map/zoom/component.tsx b/client/src/containers/analysis-eudr-detail/map/zoom/component.tsx deleted file mode 100644 index 8b2eeeba1..000000000 --- a/client/src/containers/analysis-eudr-detail/map/zoom/component.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { MinusIcon, PlusIcon } from '@heroicons/react/solid'; -import cx from 'classnames'; - -import type { MapViewState } from '@deck.gl/core/typed'; -import type { FC } from 'react'; - -const COMMON_CLASSES = - 'p-2 transition-colors bg-white cursor-pointer hover:bg-gray-100 active:bg-navy-50 disabled:bg-gray-100 disabled:opacity-75 disabled:cursor-default'; - -const ZoomControl: FC<{ - viewState: MapViewState; - className?: string; - onZoomIn: () => void; - onZoomOut: () => void; -}> = ({ viewState, className = null, onZoomIn, onZoomOut }) => { - const { zoom, minZoom, maxZoom } = viewState; - - return ( -
- - -
- ); -}; - -export default ZoomControl; diff --git a/client/src/containers/analysis-eudr-detail/map/zoom/index.ts b/client/src/containers/analysis-eudr-detail/map/zoom/index.ts deleted file mode 100644 index b404d7fd4..000000000 --- a/client/src/containers/analysis-eudr-detail/map/zoom/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './component'; diff --git a/client/src/containers/analysis-eudr/map/component.tsx b/client/src/containers/analysis-eudr/map/component.tsx index 0b6e282e0..40361bfe6 100644 --- a/client/src/containers/analysis-eudr/map/component.tsx +++ b/client/src/containers/analysis-eudr/map/component.tsx @@ -1,26 +1,31 @@ -import { useEffect, useState, useCallback } from 'react'; +import { useState, useCallback } from 'react'; import DeckGL from '@deck.gl/react/typed'; import { BitmapLayer, GeoJsonLayer } from '@deck.gl/layers/typed'; import Map from 'react-map-gl/maplibre'; -import { WebMercatorViewport, MapView } from '@deck.gl/core/typed'; +import { MapView } from '@deck.gl/core/typed'; import { TileLayer } from '@deck.gl/geo-layers/typed'; import { CartoLayer, setDefaultCredentials, MAP_TYPES, API_VERSIONS } from '@deck.gl/carto/typed'; -import bbox from '@turf/bbox'; import ZoomControl from './zoom'; import LegendControl from './legend'; import BasemapControl from './basemap'; import { useAppSelector } from '@/store/hooks'; -// import { setBasemap, setPlanetCompare } from '@/store/features/eudr'; import { INITIAL_VIEW_STATE, MAP_STYLES } from '@/components/map'; import { usePlotGeometries } from '@/hooks/eudr'; import { formatNumber } from '@/utils/number-format'; import type { PickingInfo, MapViewState } from '@deck.gl/core/typed'; +const DEFAULT_VIEW_STATE: MapViewState = { + ...INITIAL_VIEW_STATE, + latitude: -8.461844239054608, + longitude: -74.96226240479487, + zoom: 9, + maxZoom: 20, +}; + setDefaultCredentials({ - // apiBaseUrl: 'https://eudr.carto.com', apiBaseUrl: 'https://gcp-us-east1.api.carto.com', accessToken: 'eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfemsydWhpaDYiLCJqdGkiOiJjZDk0ZWIyZSJ9.oqLagnOEc-j7Z4hY-MTP1yoZA_vJ7WYYAkOz_NUmCJo', @@ -32,7 +37,7 @@ const EUDRMap = () => { ); const [hoverInfo, setHoverInfo] = useState(null); - const [viewState, setViewState] = useState(INITIAL_VIEW_STATE); + const [viewState, setViewState] = useState(DEFAULT_VIEW_STATE); const plotGeometries = usePlotGeometries(); @@ -125,6 +130,24 @@ const EUDRMap = () => { visible: contextualLayers['deforestation-alerts-2020-2022-hansen'].active, }); + const raddLayer = new CartoLayer({ + id: 'real-time-deforestation-alerts-since-2020-radd', + type: MAP_TYPES.QUERY, + connection: 'eudr', + data: 'SELECT * FROM `cartobq.eudr.RADD_date_confidence_3`', + pointRadiusMinPixels: 2, + getLineColor: [201, 42, 109], + getFillColor: [201, 42, 109], + lineWidthMinPixels: 1, + visible: contextualLayers['real-time-deforestation-alerts-since-2020-radd'].active, + credentials: { + apiVersion: API_VERSIONS.V3, + apiBaseUrl: 'https://gcp-us-east1.api.carto.com', + accessToken: + 'eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfemsydWhpaDYiLCJqdGkiOiI3NTFkNzA1YSJ9.jrVugV7HYfhmjxj-p2Iks8nL_AjHR91Q37JVP2fNmtc', + }, + }); + const handleZoomIn = useCallback(() => { const zoom = viewState.maxZoom === viewState.zoom ? viewState.zoom : viewState.zoom + 1; setViewState({ ...viewState, zoom }); @@ -135,88 +158,56 @@ const EUDRMap = () => { setViewState({ ...viewState, zoom }); }, [viewState]); - const fitToPlotBounds = useCallback(() => { - if (!plotGeometries.data) return; - const [minLng, minLat, maxLng, maxLat] = bbox(plotGeometries.data); - const newViewport = new WebMercatorViewport(viewState); - const { longitude, latitude, zoom } = newViewport.fitBounds( - [ - [minLng, minLat], - [maxLng, maxLat], - ], - { - padding: 10, - }, - ); - if ( - viewState.latitude !== latitude || - viewState.longitude !== longitude || - viewState.zoom !== zoom - ) { - setViewState({ ...viewState, longitude, latitude, zoom }); - } - }, [plotGeometries.data, viewState]); - - // Fit to bounds when data is loaded or changed - useEffect(() => { - if (plotGeometries.data) { - fitToPlotBounds(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [plotGeometries.data]); - - const handleResize = useCallback(() => { - setTimeout(() => fitToPlotBounds(), 0); - }, [fitToPlotBounds]); - return ( <> - setViewState(viewState as MapViewState)} - controller={{ dragRotate: false }} - layers={[ - basemap === 'planet' && !planetCompare ? [planetLayer] : null, - basemap === 'planet' && planetCompare ? [planetLayer, planetCompareLayer] : null, - forestCoverLayer, - deforestationLayer, - layer, - ]} - layerFilter={({ layer, viewport }) => { - return !planetCompare || viewport.id.startsWith(layer.id.split('-')[0]); - }} - onResize={handleResize} - {...(planetCompare - ? { - views: [ - new MapView({ - id: 'top', - y: 0, - height: '50%', - padding: { top: '100%' }, - }), - new MapView({ - id: 'bottom', - y: '50%', - height: '50%', - padding: { bottom: '100%' }, - }), - new MapView({ - id: 'full', - y: 0, - x: 0, - width: '100%', - height: '100%', - }), - ], - } - : {})} - > - - - {planetCompare && ( -
- )} +
+ setViewState(viewState as MapViewState)} + controller={{ dragRotate: false }} + layers={[ + basemap === 'planet' && !planetCompare ? [planetLayer] : null, + basemap === 'planet' && planetCompare ? [planetLayer, planetCompareLayer] : null, + forestCoverLayer, + deforestationLayer, + raddLayer, + layer, + ]} + layerFilter={({ layer, viewport }) => { + return !planetCompare || viewport.id.startsWith(layer.id.split('-')[0]); + }} + {...(planetCompare + ? { + views: [ + new MapView({ + id: 'top', + y: 0, + height: '50%', + padding: { top: '100%' }, + }), + new MapView({ + id: 'bottom', + y: '50%', + height: '50%', + padding: { bottom: '100%' }, + }), + new MapView({ + id: 'full', + y: 0, + x: 0, + width: '100%', + height: '100%', + }), + ], + } + : {})} + > + + + {planetCompare && ( +
+ )} +
{hoverInfo?.object && (
{ -
+
diff --git a/client/src/pages/eudr/suppliers/[supplierId].tsx b/client/src/pages/eudr/suppliers/[supplierId].tsx index 4888f4e26..48ac37ba1 100644 --- a/client/src/pages/eudr/suppliers/[supplierId].tsx +++ b/client/src/pages/eudr/suppliers/[supplierId].tsx @@ -9,7 +9,7 @@ import { tasksSSR } from 'services/ssr'; import ApplicationLayout from 'layouts/application'; import CollapseButton from 'containers/collapse-button/component'; import TitleTemplate from 'utils/titleTemplate'; -import Map from 'containers/analysis-eudr/map'; +import Map from '@/containers/analysis-eudr/map'; import EUDRFilters from '@/containers/analysis-eudr-detail/filters'; import { Button } from '@/components/ui/button'; import { useEUDRSupplier } from '@/hooks/eudr'; @@ -78,7 +78,7 @@ const MapPage: NextPageWithLayout = () => { -
+
diff --git a/client/yarn.lock b/client/yarn.lock index 84b0b258b..2e99fd3b2 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -2621,32 +2621,6 @@ __metadata: languageName: node linkType: hard -"@turf/bbox@npm:^6.5.0": - version: 6.5.0 - resolution: "@turf/bbox@npm:6.5.0" - dependencies: - "@turf/helpers": ^6.5.0 - "@turf/meta": ^6.5.0 - checksum: 537be56ae0c5ad44e71a691717b35745e947e19a6bd9f20fdac2ab4318caf98cd88472d7dbf576e8b32ead5da034d273ffb3f4559d6d386820ddcb88a1f7fedd - languageName: node - linkType: hard - -"@turf/helpers@npm:^6.5.0": - version: 6.5.0 - resolution: "@turf/helpers@npm:6.5.0" - checksum: d57f746351357838c654e0a9b98be3285a14b447504fd6d59753d90c6d437410bb24805d61c65b612827f07f6c2ade823bb7e56e41a1a946217abccfbd64c117 - languageName: node - linkType: hard - -"@turf/meta@npm:^6.5.0": - version: 6.5.0 - resolution: "@turf/meta@npm:6.5.0" - dependencies: - "@turf/helpers": ^6.5.0 - checksum: c6bb936aa92bf3365e87a50dc65f248e070c5767a36fac390754c00c89bf2d1583418686ab19a10332bfa9340b8cac6aaf2c55dad7f5fcf77f1a2dda75ccf363 - languageName: node - linkType: hard - "@types/chroma-js@npm:2.1.3": version: 2.1.3 resolution: "@types/chroma-js@npm:2.1.3" @@ -7884,7 +7858,6 @@ __metadata: "@tanstack/react-query": ^4.2.1 "@tanstack/react-table": 8.13.2 "@tanstack/react-virtual": 3.0.1 - "@turf/bbox": ^6.5.0 "@types/chroma-js": 2.1.3 "@types/d3-format": 3.0.1 "@types/d3-scale": 4.0.2