Skip to content

[FLAG-997] GADM country list #4891

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 36 commits into
base: epic/gadm
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
562fb2f
fix(map): Fix sluggish map when panning or zooming
clementprdhomme Mar 14, 2025
0cef10c
feat(jsdoc): add JSDoc
willian-viana Oct 28, 2024
346f660
chore(logs): remove console.logs
willian-viana Oct 28, 2024
b6f6e22
feat(metadata): add new geostore query for admin level 0
willian-viana Oct 1, 2024
a3d250d
feat(countries): migrate queries from gadm 3.6 to 4.1
willian-viana Nov 15, 2024
370243c
chore(comments): remove old comments
willian-viana Nov 15, 2024
ba1948a
chore(gadm): replace parseGadm36Id method name to parseGadmId
willian-viana Jan 9, 2025
dc214fb
fix(countryProvider): fix country provider implementation
willian-viana Jan 23, 2025
3b14425
feat(location-service): migrate countryConfig request from Carto to D…
willian-viana Jan 23, 2025
2577f64
chore(location-service): extract search by Iso and ID logic
willian-viana Jan 23, 2025
f3d2753
feat(location-service): add location tests
willian-viana Jan 23, 2025
903f555
feat(country-service): add tests for country service
willian-viana Jan 23, 2025
cfa7374
fix(gadm): fix setSubRegions actions
willian-viana Jan 24, 2025
a33a935
chore(gadm): remove 3.6 from GADM methods name
willian-viana Jan 24, 2025
70d760e
feat(gadm): add tests for gadm utils
willian-viana Jan 24, 2025
87f566c
chore(fao): remove WHERE 1 = 1
willian-viana Jan 27, 2025
5d387a2
chore(location-service): refact countryConfig to avoid providers dupl…
willian-viana Jan 27, 2025
3618a54
chore(country-service): improve tests adding SQL queries directly
willian-viana Jan 27, 2025
15938ba
fix(gadm): fix adm level validation
willian-viana Jan 30, 2025
a54e8ad
fix(gadm): add name_0 as a fallback for country name
willian-viana Jan 30, 2025
92f1c22
fix(gadm): remove conflicted areas from country list
willian-viana Jan 30, 2025
38b88d5
fix(country-list): fix country order
willian-viana Feb 4, 2025
2d276ae
feat(gadm): remove Taiwan and Caspian Sea from the GADM country list
willian-viana Feb 27, 2025
1474ceb
feat(gadm): set new dataset versions
willian-viana Feb 28, 2025
57b42af
chore(geostore): set gadm version to 4.1
willian-viana Mar 6, 2025
247f3b7
chore(gadm): set gadm version to 4.1 inside getAreas
willian-viana Mar 7, 2025
6f4e126
feat(aoi-card): display outdated areas warning and modal for gadm 3.6…
wri7tno Mar 17, 2025
55c3bff
chore(country-service): enforce long names for countries larger than …
willian-viana Mar 26, 2025
3ce241e
feat(search): remove carto from geocoding serach location
willian-viana Mar 28, 2025
917ec76
feat(search): remove interaction when user clicks on search result
willian-viana Apr 1, 2025
2b2ce96
chore(gadm): set gadm version to 4.1 and remove hard coded endpoint f…
willian-viana Apr 1, 2025
efe7beb
chore(cache): add timestamp to sentence requets to invalidate cache i…
willian-viana Apr 2, 2025
d7ea04c
fix(cache): add cache control to dataRequest axios method
willian-viana Apr 7, 2025
f3f4509
chore(country-service): bump dataset version to v4.1.75
willian-viana Apr 9, 2025
d7a3bd3
chore(cache): remove interceptor to rollback data api cache
willian-viana Apr 10, 2025
304eab9
chore(mapbox): remove types from mapbox search query
willian-viana Apr 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions components/aoi-card/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const getLatestAlerts = ({ location, params }) =>

class AoICard extends PureComponent {
static propTypes = {
admin: PropTypes.object,
name: PropTypes.string,
tags: PropTypes.array,
application: PropTypes.string,
Expand All @@ -64,6 +65,7 @@ class AoICard extends PureComponent {
onFetchAlerts: PropTypes.func,
status: PropTypes.string,
setConfirmSubscriptionModalSettings: PropTypes.func,
openOutdatedAreaModal: PropTypes.func,
confirmed: PropTypes.bool,
id: PropTypes.string,
};
Expand Down Expand Up @@ -131,6 +133,7 @@ class AoICard extends PureComponent {

render() {
const {
admin,
tags,
name,
application,
Expand All @@ -142,9 +145,13 @@ class AoICard extends PureComponent {
location,
status,
setConfirmSubscriptionModalSettings,
openOutdatedAreaModal,
id,
confirmed,
} = this.props;

const isOutdatedGadmArea =
admin?.source?.provider === 'gadm' && admin?.source?.version === '3.6';
const {
loading,
alerts: { glads, fires, error: dataError },
Expand Down Expand Up @@ -237,6 +244,20 @@ class AoICard extends PureComponent {
)}
</div>
)}
{isOutdatedGadmArea && (
<div className="subscribed">
<Icon icon={warningIcon} className="warning-icon" />
<button
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
openOutdatedAreaModal();
}}
>
Outdated area, notifications paused
</button>
</div>
)}
{!simple && !isPending && (
<div className="activity">
<span className="activity-intro">
Expand Down
2 changes: 1 addition & 1 deletion components/forms/area-of-interest/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const saveAreaOfInterest = createThunkAction(
admin: {
source: {
provider: 'gadm',
version: '3.6',
version: '4.1',
},
adm0,
adm1,
Expand Down
86 changes: 46 additions & 40 deletions components/map-menu/actions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createAction, createThunkAction } from 'redux/actions';
import { fetchGeocodeLocations } from 'services/geocoding';

import { setMapSettings, setMapInteractions } from 'components/map/actions';
import { setMapSettings } from 'components/map/actions';
import { setAnalysisSettings } from 'components/analysis/actions';

export const setLocationsData = createAction('setLocationsData');
Expand All @@ -10,58 +10,64 @@ export const setMenuSettings = createAction('setMenuSettings');

export const getLocationFromSearch = createThunkAction(
'getLocationFromSearch',
({ search, token, lang }) => (dispatch) => {
dispatch(setMenuLoading(true));
if (search) {
fetchGeocodeLocations(search, lang, token)
.then((locations) => {
if (locations?.length) {
dispatch(setLocationsData(locations));
} else {
dispatch(setLocationsData([]));
}
dispatch(setMenuLoading(false));
})
.catch(() => {
dispatch(setMenuLoading(false));
});
({ search, token, lang }) =>
(dispatch) => {
dispatch(setMenuLoading(true));
if (search) {
fetchGeocodeLocations(search, lang, token)
.then((locations) => {
if (locations?.length) {
dispatch(setLocationsData(locations));
} else {
dispatch(setLocationsData([]));
}
dispatch(setMenuLoading(false));
})
.catch(() => {
dispatch(setMenuLoading(false));
});
}
}
}
);

export const handleClickLocation = createThunkAction(
'handleClickLocation',
({ center, bbox: featureBbox, ...feature }) => (dispatch) => {
if (featureBbox) {
dispatch(setMapSettings({ canBound: true, bbox: featureBbox }));
} else {
dispatch(
setMapSettings({ center: { lat: center[1], lng: center[0] }, zoom: 12 })
);
({ center, bbox: featureBbox }) =>
(dispatch) => {
if (featureBbox) {
dispatch(setMapSettings({ canBound: true, bbox: featureBbox }));
} else {
dispatch(
setMapSettings({
center: { lat: center[1], lng: center[0] },
zoom: 12,
})
);
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I'm glad removing the analysis modal wasn't complicated, @willian-viana . 😅

dispatch(setMenuSettings({ menuSection: '' }));
}
dispatch(setMapInteractions({ features: [feature], lngLat: center }));
dispatch(setMenuSettings({ menuSection: '' }));
}
);

export const handleViewOnMap = createThunkAction(
'handleViewOnMap',
({ analysis, mapMenu, map }) => (dispatch) => {
if (map) {
dispatch(setMapSettings({ ...map, canBound: true }));
}
({ analysis, mapMenu, map }) =>
(dispatch) => {
if (map) {
dispatch(setMapSettings({ ...map, canBound: true }));
}

dispatch(
setMenuSettings({
...mapMenu,
menuSection: '',
})
);
dispatch(
setMenuSettings({
...mapMenu,
menuSection: '',
})
);

if (analysis) {
dispatch(setAnalysisSettings(analysis));
if (analysis) {
dispatch(setAnalysisSettings(analysis));
}
}
}
);

export const showAnalysis = createThunkAction(
Expand Down
19 changes: 18 additions & 1 deletion components/map-menu/components/sections/my-gfw/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Pill from 'components/ui/pill';
import Loader from 'components/ui/loader';
import Paginate from 'components/paginate';
import ConfirmSubscriptionModal from 'components/modals/confirm-subscription';
import OutdatedAreaModal from 'components/modals/outdated-area/OutdatedAreaModal';

import editIcon from 'assets/icons/edit.svg?sprite';
import shareIcon from 'assets/icons/share.svg?sprite';
Expand Down Expand Up @@ -51,6 +52,7 @@ class MapMenuMyGFW extends PureComponent {
unselectedTags: [],
pageSize: 6,
pageNum: 0,
outdatedAreaModalOpen: false,
};

static getDerivedStateFromProps(prevProps, prevState) {
Expand Down Expand Up @@ -294,7 +296,15 @@ class MapMenuMyGFW extends PureComponent {
tabIndex={0}
key={area.id}
>
<AoICard index={i} {...area} simple />
<AoICard
index={i}
{...area}
simple
openOutdatedAreaModal={() =>
this.setState({
outdatedAreaModalOpen: true,
})}
/>
{active && this.renderAoiActions()}
</div>
);
Expand Down Expand Up @@ -370,6 +380,13 @@ class MapMenuMyGFW extends PureComponent {
/>
)}
<ConfirmSubscriptionModal />
<OutdatedAreaModal
isOpen={this.state.outdatedAreaModalOpen}
handleCloseModal={() =>
this.setState({
outdatedAreaModalOpen: false,
})}
/>
</div>
);
}
Expand Down
4 changes: 2 additions & 2 deletions components/map-menu/components/sections/search/selectors.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createSelector, createStructuredSelector } from 'reselect';
import { deburrUpper } from 'utils/strings';
import { getGadm36Id } from 'utils/gadm';
import { getGadmId } from 'utils/gadm';
import sortBy from 'lodash/sortBy';
import { translateText, selectActiveLang } from 'utils/lang';

Expand Down Expand Up @@ -49,7 +49,7 @@ const getLocations = createSelector(
(locations, location) => {
if (!locations) return null;
const { adm0, adm1, adm2 } = location;
const gadmId = getGadm36Id(adm0, adm1, adm2);
const gadmId = getGadmId(adm0, adm1, adm2);

return locations
.map((l) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ const getInteractionData = (state, { data }) => data;

/**
* Returns an object with the selected location name, its area and a sentence do be displayed.
* @param {method} createSelector - return a memoized outut selector.
* @param {method} createSelector - return a memoized output selector.
* @see https://reselect.js.org/introduction/getting-started/#output-selector for implementation details
* @param {selector} getInteractionData - data from the area clicked by user
* @return {object} sentence, location name and area.
*/
export const getSentence = createSelector(
[getInteractionData],
({ data } = {}) => {
const { adm_level, gid_0, name_1, country } = data;
let name = adm_level > 0 ? data[`name_${adm_level}`] : country;
const { adm_level, gid_0, name_1, name_0, country } = data;
let name = adm_level > 0 ? data[`name_${adm_level}`] : country || name_0;

if (!gid_0) {
name = data[Object.keys(data).find((k) => k.includes('name'))];
Expand All @@ -30,12 +30,15 @@ export const getSentence = createSelector(
locationNames = [
locationNameTranslated,
translateText(name_1),
translateText(country),
translateText(country || name_0),
];
}

if (Number(adm_level) === '1') {
locationNames = [locationNameTranslated, translateText(country)];
if (Number(adm_level) === 1) {
locationNames = [
locationNameTranslated,
translateText(country || name_0),
];
}

const locationName = locationNames.join(', ');
Expand Down
4 changes: 3 additions & 1 deletion components/map/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ export const getMapViewport = createSelector([getMapSettings], (settings) => {
pitch,
latitude: center?.lat,
longitude: center?.lng,
transitionDuration: 500,
// The map transition needs to always be 0 otherwise the map becomes sluggish when panned or zoomed. Only set a
// different value when flying between locations and only temporarily.
transitionDuration: 0,
};
});

Expand Down
47 changes: 47 additions & 0 deletions components/modals/outdated-area/OutdatedAreaModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
import Modal from 'components/modal';

const OutdatedAreaModal = ({ isOpen, handleCloseModal }) => {
return (
<Modal
open={isOpen}
contentLabel="Outdated area"
onRequestClose={handleCloseModal}
className="c-confirm-subscription-modal"
title=" "
>
<p>
This area uses an outdated version of political boundaries. As a result,
alert notification emails can no longer be sent for this area. To
continue receiving alerts, please navigate to this area on the map and
re-save the area. Instructions on how to save an area and subscribe to
alerts are available via in{' '}
<a
href="https://www.globalforestwatch.org/help/map/guides/save-area-subscribe-forest-change-notifications/"
target="_blank"
rel="noreferrer"
>
this Help Center article
</a>
. Please read through our{' '}
<a
href="https://www.globalforestwatch.org/blog/data-and-tools/updated-political-boundaries-gadm?utm_medium=email&utm_source=email1&utm_campaign=gadmupdate"
target="_blank"
rel="noreferrer"
>
blog outlining these changes
</a>{' '}
and let us know if you have any questions at{' '}
<a href="mailto:[email protected]">[email protected]</a>.
</p>
</Modal>
);
};

OutdatedAreaModal.propTypes = {
isOpen: PropTypes.bool,
handleCloseModal: PropTypes.func,
};

export default OutdatedAreaModal;
3 changes: 3 additions & 0 deletions components/ui/map/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ class Map extends Component {
onResize={this.onResize}
onLoad={this.onLoad}
getCursor={getCursor}
// If the `transitionDuration` is not 0, then the map becomes sluggish when panned or zoomed. Nevertheless,
// we still want a transition when flying between locations.
transitionDuration={flying ? viewport.transitionDuration || 0 : 0}
transitionInterpolator={new FlyToInterpolator()}
transitionEasing={easeCubic}
preventStyleDiffing
Expand Down
Loading