Skip to content

Commit

Permalink
Enhance the existing projects AoIs on project creation map (hotosm#5270)
Browse files Browse the repository at this point in the history
* Enhance the existing projects AoIs on project creation map

Add existing project centroid points, decrease zoom level to enable AoIs layer

* Update test case to match updated checkbox label
  • Loading branch information
HelNershingThapa authored Jul 27, 2022
1 parent 11f4c13 commit 2938f95
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 15 deletions.
2 changes: 1 addition & 1 deletion frontend/src/components/button.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Link } from '@reach/router';
import { LoadingIcon } from './svgIcons';

const IconSpace = ({ children }) => <span className="mr2">{children}</span>;
const AnimatedLoadingIcon = () => (
export const AnimatedLoadingIcon = () => (
<IconSpace>
<LoadingIcon className="h1 w1 v-mid" style={{ animation: 'spin 1s linear infinite' }} />
</IconSpace>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/projectCreate/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export default defineMessages({
},
showProjectsAOILayer: {
id: 'management.projects.create.show_aois',
defaultMessage: 'Show existing projects',
defaultMessage: 'Show existing projects AoIs',
},
disabledAOILayer: {
id: 'management.projects.create.show_aois.disabled',
Expand Down
55 changes: 50 additions & 5 deletions frontend/src/components/projectCreate/projectCreationMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import MapboxLanguage from '@mapbox/mapbox-gl-language';
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css';
import { useDropzone } from 'react-dropzone';
import { mapboxLayerDefn } from '../projects/projectsMap';

import {
MAPBOX_TOKEN,
Expand All @@ -32,23 +33,31 @@ const ProjectCreationMap = ({ mapObj, setMapObj, metadata, updateMetadata, step,
const mapRef = React.createRef();
const locale = useSelector((state) => state.preferences['locale']);
const token = useSelector((state) => state.auth.get('token'));
const [showProjectsAOILayer, setShowProjectsAOILayer] = useState(false);
const [showProjectsAOILayer, setShowProjectsAOILayer] = useState(true);
const [aoiCanBeActivated, setAOICanBeActivated] = useState(false);
const [existingProjectsList, setExistingProjectsList] = useState([]);
const [isAoiLoading, setIsAoiLoading] = useState(false);
const [debouncedGetProjectsAOI] = useDebouncedCallback(() => getProjectsAOI(), 1500);
const { getRootProps, getInputProps } = useDropzone({
onDrop: step === 1 ? uploadFile : () => {}, // drag&drop is activated only on the first step
noClick: true,
noKeyboard: true,
});
const minZoomLevelToAOIVisualization = 11;
const minZoomLevelToAOIVisualization = 9;

useEffect(() => {
fetchLocalJSONAPI('projects/').then((res) => setExistingProjectsList(res.mapResults));
}, []);

const getProjectsAOI = () => {
if (aoiCanBeActivated && showProjectsAOILayer && step === 1) {
setIsAoiLoading(true);
let bounds = mapObj.map.getBounds();
let bbox = `${bounds._sw.lng},${bounds._sw.lat},${bounds._ne.lng},${bounds._ne.lat}`;
fetchLocalJSONAPI(`projects/queries/bbox/?bbox=${bbox}&srid=4326`, token).then((res) =>
mapObj.map.getSource('otherProjects').setData(res),
);
fetchLocalJSONAPI(`projects/queries/bbox/?bbox=${bbox}&srid=4326`, token).then((res) => {
mapObj.map.getSource('otherProjects').setData(res);
setIsAoiLoading(false);
});
}
};

Expand Down Expand Up @@ -187,6 +196,41 @@ const ProjectCreationMap = ({ mapObj, setMapObj, metadata, updateMetadata, step,
}
};

const noop = () => {};

useLayoutEffect(() => {
/* docs: https://docs.mapbox.com/mapbox-gl-js/example/cluster/ */
const { map } = mapObj;

const someResultsReady =
existingProjectsList &&
existingProjectsList.features &&
existingProjectsList.features.length > 0;

const mapReadyProjectsReady =
map !== null &&
map.isStyleLoaded() &&
map.getSource('projects') === undefined &&
someResultsReady;
const projectsReadyMapLoading =
map !== null &&
!map.isStyleLoaded() &&
map.getSource('projects') === undefined &&
someResultsReady;

/* set up style/sources for the map, either immediately or on base load */
if (mapReadyProjectsReady) {
mapboxLayerDefn(map, existingProjectsList, noop, true);
} else if (projectsReadyMapLoading) {
map.on('load', () => mapboxLayerDefn(map, existingProjectsList, noop, true));
}

/* refill the source on existingProjectsList changes */
if (map !== null && map.getSource('projects') !== undefined && someResultsReady) {
map.getSource('projects').setData(existingProjectsList);
}
}, [mapObj, existingProjectsList]);

useLayoutEffect(() => {
if (mapObj.map !== null && mapboxgl.supported()) {
mapObj.map.on('moveend', (event) => {
Expand Down Expand Up @@ -247,6 +291,7 @@ const ProjectCreationMap = ({ mapObj, setMapObj, metadata, updateMetadata, step,
isActive={showProjectsAOILayer}
setActive={setShowProjectsAOILayer}
disabled={!aoiCanBeActivated}
isAoiLoading={isAoiLoading}
/>
)}
<BasemapMenu map={mapObj.map} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import ReactTooltip from 'react-tooltip';
import messages from './messages';
import statusMessages from '../projectDetail/messages';
import { TASK_COLOURS } from '../../config';
import { AnimatedLoadingIcon } from '../button';

export const ProjectsAOILayerCheckBox = ({ isActive, setActive, disabled }) => {
export const ProjectsAOILayerCheckBox = ({ isActive, setActive, disabled, isAoiLoading }) => {
return (
<>
<div className="bg-white fl dib pv1 ph2 blue-dark mt2 mh2 f7 br1 shadow-1">
Expand All @@ -19,6 +20,7 @@ export const ProjectsAOILayerCheckBox = ({ isActive, setActive, disabled }) => {
<span className="di v-mid" data-tip>
<FormattedMessage {...messages.showProjectsAOILayer} />
</span>
<span className="ml1">{isAoiLoading && <AnimatedLoadingIcon />}</span>
<ReactTooltip place="bottom">
{disabled ? (
<FormattedMessage {...messages.disabledAOILayer} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ describe('ProjectsAOILayerCheckBox', () => {
<ProjectsAOILayerCheckBox isActive={false} setActive={testFn} disabled={true} />
</IntlProviders>,
);
expect(screen.getByText('Show existing projects')).toBeInTheDocument();
expect(screen.getByText('Show existing projects AoIs')).toBeInTheDocument();
expect(screen.getByRole('checkbox').className).toContain('b--grey-light');
expect(screen.getByRole('checkbox').className).not.toContain('b--red');
userEvent.hover(screen.getByText('Show existing projects'));
userEvent.hover(screen.getByText('Show existing projects AoIs'));
expect(
screen.getByText(
"Zoom in to be able to activate the visualization of other projects' areas of interest.",
Expand All @@ -31,10 +31,10 @@ describe('ProjectsAOILayerCheckBox', () => {
<ProjectsAOILayerCheckBox isActive={false} setActive={testFn} disabled={false} />
</IntlProviders>,
);
expect(screen.getByText('Show existing projects')).toBeInTheDocument();
expect(screen.getByText('Show existing projects AoIs')).toBeInTheDocument();
expect(screen.getByRole('checkbox').className).not.toContain('b--grey-light');
expect(screen.getByRole('checkbox').className).toContain('b--red');
userEvent.hover(screen.getByText('Show existing projects'));
userEvent.hover(screen.getByText('Show existing projects AoIs'));
expect(
screen.getByText("Enable the visualization of the existing projects' areas of interest."),
).toBeInTheDocument();
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/projects/projectsMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const licensedFonts = MAPBOX_TOKEN
? ['DIN Offc Pro Medium', 'Arial Unicode MS Bold']
: ['Open Sans Semibold'];

export const mapboxLayerDefn = (map, mapResults, clickOnProjectID) => {
export const mapboxLayerDefn = (map, mapResults, clickOnProjectID, disablePoiClick = false) => {
map.addImage('mapMarker', markerIcon, { width: 15, height: 15, data: markerIcon });
map.addSource('projects', {
type: 'geojson',
Expand Down Expand Up @@ -80,7 +80,9 @@ export const mapboxLayerDefn = (map, mapResults, clickOnProjectID) => {
});
map.on('mouseenter', 'projects-unclustered-points', function (e) {
// Change the cursor style as a UI indicator.
map.getCanvas().style.cursor = 'pointer';
if (!disablePoiClick) {
map.getCanvas().style.cursor = 'pointer';
}
});
map.on('mouseleave', 'projects-unclustered-points', function (e) {
// Change the cursor style as a UI indicator.
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@
"management.projects.create.errors.fileSize": "We only accept files up to {fileSize} MB. Please reduce the size of your file and try again.",
"management.projects.create.split_task.description": "Make tasks smaller by clicking on specific tasks or drawing an area on the map.",
"management.projects.create.reset.button": "Reset",
"management.projects.create.show_aois": "Show existing projects",
"management.projects.create.show_aois": "Show existing projects AoIs",
"management.projects.create.show_aois.disabled": "Zoom in to be able to activate the visualization of other projects' areas of interest.",
"management.projects.create.show_aois.enable": "Enable the visualization of the existing projects' areas of interest.",
"management.projects.create.show_aois.legend": "Color legend:",
Expand Down Expand Up @@ -359,6 +359,7 @@
"projects.formInputs.campaign.title": "Campaign",
"projects.formInputs.categories.title": "Categories",
"projects.formInputs.organisation.description": "Organization that is coordinating the project, if there is any. The managers of that organization will have administration rights over the project.",
"projects.formInputs.admins.title": "TM Admins",
"projects.formInputs.imagery.select": "Select imagery",
"projects.formInputs.license.select": "Select license",
"projects.formInputs.organisation.select": "Select organization",
Expand Down Expand Up @@ -855,6 +856,7 @@
"management.teams.visibility.public": "Public",
"management.teams.visibility.private": "Private",
"management.teams.invite_only.description": "Managers need to approve a member's request to join.",
"management.teams.newJoinRequestNotification": "Enable for team managers to receive (email) notifications each time a new join request is made",
"teamsAndOrgs.management.teams.messages.waiting_approval": "Your request to join this team is waiting for approval.",
"management.projects.no_found": "This {entity} doesn't have projects yet.",
"management.organisation.teams.no_found": "No teams found.",
Expand Down

0 comments on commit 2938f95

Please sign in to comment.