Skip to content

Commit

Permalink
Merge pull request #325 from Vizzuality/feature/terrestrial-countries…
Browse files Browse the repository at this point in the history
…-popup

[SKY30-470|472] fetches data for terrestrial regions and countries popups
  • Loading branch information
clementprdhomme authored Oct 16, 2024
2 parents ad4950f + 945afad commit 761ffc4
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 322 deletions.
6 changes: 2 additions & 4 deletions frontend/src/containers/map/content/map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ import LabelsManager from '@/containers/map/content/map/labels-manager';
import LayersToolbox from '@/containers/map/content/map/layers-toolbox';
import Modelling from '@/containers/map/content/map/modelling';
import Popup from '@/containers/map/content/map/popup';
import EEZLayerPopup from '@/containers/map/content/map/popup/eez';
import BoundariesPopup from '@/containers/map/content/map/popup/boundaries';
import GenericPopup from '@/containers/map/content/map/popup/generic';
import ProtectedAreaPopup from '@/containers/map/content/map/popup/protected-area';
import RegionsPopup from '@/containers/map/content/map/popup/regions';
import { useSyncMapLayers, useSyncMapSettings } from '@/containers/map/content/map/sync-settings';
import { layersAtom, sidebarAtom } from '@/containers/map/store';
import {
Expand Down Expand Up @@ -349,10 +348,9 @@ MainMap.messages = [
...LayersToolbox.messages,
...ZoomControls.messages,
// Indirectly imported by the layer manager
...EEZLayerPopup.messages,
...GenericPopup.messages,
...ProtectedAreaPopup.messages,
...RegionsPopup.messages,
...BoundariesPopup.messages,
];

export default MainMap;
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { ReactElement, isValidElement, useMemo } from 'react';

import TooltipButton from '@/components/tooltip-button';
import Icon from '@/components/ui/icon';
import EEZLayerPopup from '@/containers/map/content/map/popup/eez';
import BoundariesPopup from '@/containers/map/content/map/popup/boundaries';
import GenericPopup from '@/containers/map/content/map/popup/generic';
import ProtectedAreaPopup from '@/containers/map/content/map/popup/protected-area';
import RegionsPopup from '@/containers/map/content/map/popup/regions';
import { cn } from '@/lib/classnames';
import { parseConfig } from '@/lib/json-converter';
import CircleWithDottedRedStrokeIcon from '@/styles/icons/circle-with-dotted-red-stroke.svg';
Expand Down Expand Up @@ -157,10 +156,9 @@ const LegendItem: FCWithMessages<LegendItemsProps> = ({ config }) => {

LegendItem.messages = [
// Imported by `parseConfig`
...EEZLayerPopup.messages,
...GenericPopup.messages,
...ProtectedAreaPopup.messages,
...RegionsPopup.messages,
...BoundariesPopup.messages,
];

export default LegendItem;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useAtom, useAtomValue } from 'jotai';
import { useLocale, useTranslations } from 'next-intl';

import { PAGES } from '@/constants/pages';
import { useMapSearchParams } from '@/containers/map/content/map/sync-settings';
import { useMapSearchParams, useSyncMapLayers } from '@/containers/map/content/map/sync-settings';
import { layersInteractiveIdsAtom, popupAtom } from '@/containers/map/store';
import { formatPercentage, formatKM } from '@/lib/utils/formats';
import { FCWithMessages } from '@/types';
Expand All @@ -18,45 +18,57 @@ import { useGetProtectionCoverageStats } from '@/types/generated/protection-cove
import { ProtectionCoverageStat } from '@/types/generated/strapi.schemas';
import { LayerTyped } from '@/types/layers';

const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
import { POPUP_BUTTON_CONTENT_BY_SOURCE, POPUP_PROPERTIES_BY_SOURCE } from '../constants';

const BoundariesPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
const t = useTranslations('containers.map');
const locale = useLocale();

const [rendered, setRendered] = useState(false);
const DATA_REF = useRef<Feature['properties'] | undefined>();

const geometryDataRef = useRef<Feature['properties'] | undefined>();
const { default: map } = useMap();

const searchParams = useMapSearchParams();
const [activeLayers] = useSyncMapLayers();

const { push } = useRouter();
const [popup, setPopup] = useAtom(popupAtom);

const [popup, setPopup] = useAtom(popupAtom);
const layersInteractiveIds = useAtomValue(layersInteractiveIdsAtom);

const layerQuery = useGetLayersId<{
const {
data: { source, environment },
} = useGetLayersId<{
source: LayerTyped['config']['source'];
click: LayerTyped['interaction_config']['events'][0];
environment: string;
}>(
layerId,
{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
locale,
populate: 'metadata',
fields: ['config'],
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
populate: {
environment: {
fields: ['slug'],
},
},
},
{
query: {
placeholderData: { data: {} },
select: ({ data }) => ({
source: (data.attributes as LayerTyped).config?.source,
click: (data.attributes as LayerTyped)?.interaction_config?.events.find(
(ev) => ev.type === 'click'
),
source: (data.attributes as LayerTyped)?.config?.source,
environment: data.attributes?.environment?.data.attributes.slug,
}),
},
}
);

const { source } = layerQuery.data;

const DATA = useMemo(() => {
const geometryData = useMemo(() => {
if (source?.type === 'vector' && rendered && popup && map) {
const point = map.project(popup.lngLat);

Expand All @@ -67,7 +79,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
point.y < 0 ||
point.y > map.getCanvas().height
) {
return DATA_REF.current;
return geometryDataRef.current;
}
const query = map.queryRenderedFeatures(point, {
layers: layersInteractiveIds,
Expand All @@ -77,40 +89,55 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
return d.source === source.id;
})?.properties;

DATA_REF.current = d;
geometryDataRef.current = d;

if (d) {
return DATA_REF.current;
return geometryDataRef.current;
}
}

return DATA_REF.current;
return geometryDataRef.current;
}, [popup, source, layersInteractiveIds, map, rendered]);

// ? I had to type the data ad hoc because the generated type is wrong when we are adding
// ? the `sort` query param
const locationCode = useMemo(
() => geometryData?.[POPUP_PROPERTIES_BY_SOURCE[source?.['id']]?.id],
[geometryData, source]
);

const localizedLocationName = useMemo(
() => geometryData?.[POPUP_PROPERTIES_BY_SOURCE[source?.['id']]?.name[locale]],
[geometryData, locale, source]
);

const { data: protectionCoverageStats, isFetching } =
useGetProtectionCoverageStats<ProtectionCoverageStat>(
{
locale,
filters: {
location: {
code: DATA?.region_id,
code: locationCode,
},
is_last_year: {
$eq: true,
},
environment: {
slug: {
$eq: 'marine',
$eq: environment,
},
},
},
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
populate: {
location: {
fields: ['name', 'name_es', 'name_fr', 'code', 'total_marine_area'],
fields: [
...(locale === 'en' ? ['name'] : []),
...(locale === 'es' ? ['name_es'] : []),
...(locale === 'fr' ? ['name_fr'] : []),
'code',
'total_marine_area',
'total_terrestrial_area',
],
},
},
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand All @@ -121,7 +148,7 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
{
query: {
select: ({ data }) => data?.[0].attributes,
enabled: !!DATA?.region_id,
enabled: !!geometryData,
},
}
);
Expand All @@ -146,35 +173,15 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
};
}, [locale, protectionCoverageStats]);

const localizedLocationName = useMemo(() => {
if (!protectionCoverageStats) {
return null;
}

if (locale === 'es') {
return protectionCoverageStats.location.data.attributes.name_es;
}

if (locale === 'fr') {
return protectionCoverageStats.location.data.attributes.name_fr;
}

return protectionCoverageStats.location.data.attributes.name;
}, [locale, protectionCoverageStats]);

// handle renderer
const handleMapRender = useCallback(() => {
setRendered(map?.loaded() && map?.areTilesLoaded());
}, [map]);

const handleLocationSelected = useCallback(async () => {
if (!protectionCoverageStats?.location?.data.attributes) return undefined;

const { code } = protectionCoverageStats.location.data.attributes;

await push(`${PAGES.progressTracker}/${code.toUpperCase()}?${searchParams.toString()}`);
await push(`${PAGES.progressTracker}/${locationCode.toUpperCase()}?${searchParams.toString()}`);
setPopup({});
}, [push, searchParams, protectionCoverageStats, setPopup]);
}, [push, locationCode, searchParams, setPopup]);

useEffect(() => {
map?.on('render', handleMapRender);
Expand All @@ -186,7 +193,14 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
};
}, [map, handleMapRender]);

if (!DATA) return null;
// Close the tooltip if the layer that was clicked is not active anymore
useEffect(() => {
if (!activeLayers.includes(layerId)) {
setPopup({});
}
}, [layerId, activeLayers, setPopup]);

if (!geometryData) return null;

return (
<div className="space-y-2">
Expand All @@ -198,7 +212,11 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
{!isFetching && !!protectionCoverageStats && (
<>
<div className="space-y-2">
<div className="my-4 max-w-[95%] font-mono">{t('marine-conservation-coverage')}</div>
<div className="my-4 max-w-[95%] font-mono">
{environment === 'marine'
? t('marine-conservation-coverage')
: t('terrestrial-conservation-coverage')}
</div>
<div className="space-x-1 font-mono tracking-tighter text-black">
{formattedStats.percentage !== '-' &&
t.rich('percentage-bold', {
Expand All @@ -215,11 +233,15 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
)}
</div>
<div className="space-x-1 font-mono font-medium text-black">
{t('marine-protected-area', {
{t('protected-area', {
protectedArea: formattedStats.protectedArea,
totalArea: formatKM(
locale,
Number(protectionCoverageStats?.location.data.attributes.total_marine_area)
Number(
protectionCoverageStats?.location.data.attributes[
environment === 'marine' ? 'total_marine_area' : 'total_terrestrial_area'
]
)
),
})}
</div>
Expand All @@ -229,14 +251,14 @@ const RegionsPopup: FCWithMessages<{ layerId: number }> = ({ layerId }) => {
className="block w-full border border-black p-4 text-center font-mono uppercase"
onClick={handleLocationSelected}
>
{t('open-region-insights')}
{t(POPUP_BUTTON_CONTENT_BY_SOURCE[source?.['id']])}
</button>
</>
)}
</div>
);
};

RegionsPopup.messages = ['containers.map'];
BoundariesPopup.messages = ['containers.map'];

export default RegionsPopup;
export default BoundariesPopup;
47 changes: 33 additions & 14 deletions frontend/src/containers/map/content/map/popup/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,51 @@ import { IconProps } from '@/components/ui/icon';
import Mountain from '@/styles/icons/mountain.svg';
import Wave from '@/styles/icons/wave.svg';

export const HOVER_POPUP_PROPERTIES_BY_SOURCE = {
export const POPUP_PROPERTIES_BY_SOURCE = {
'ezz-source': {
en: 'GEONAME',
es: 'GEONAME_ES',
fr: 'GEONAME_FR',
id: 'ISO_SOV1',
name: {
en: 'GEONAME',
es: 'GEONAME_ES',
fr: 'GEONAME_FR',
},
},
'regions-source': {
en: 'name',
es: 'name_es',
fr: 'name_fr',
id: 'region_id',
name: {
en: 'name',
es: 'name_es',
fr: 'name_fr',
},
},
'gadm-countries': {
en: 'COUNTRY',
es: 'name_es',
fr: 'name_fr',
id: 'GID_0',
name: {
en: 'COUNTRY',
es: 'name_es',
fr: 'name_fr',
},
},
'gadm-regions': {
en: 'name',
es: 'name_es',
fr: 'name_fr',
id: 'region_id',
name: {
en: 'name',
es: 'name_es',
fr: 'name_fr',
},
},
};

export const HOVER_POPUP_ICON_BY_SOURCE = {
export const POPUP_ICON_BY_SOURCE = {
'ezz-source': Wave as IconProps['icon'],
'regions-source': Wave as IconProps['icon'],
'gadm-countries': Mountain as IconProps['icon'],
'gadm-regions': Mountain as IconProps['icon'],
};

export const POPUP_BUTTON_CONTENT_BY_SOURCE = {
'ezz-source': 'open-country-insights',
'regions-source': 'open-region-insights',
'gadm-countries': 'open-country-insights',
'gadm-regions': 'open-region-insights',
};
Loading

0 comments on commit 761ffc4

Please sign in to comment.