Skip to content
This repository has been archived by the owner on Apr 6, 2023. It is now read-only.

Added mapillary overlay to task selection map #280

Open
wants to merge 26 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bb0389d
Added date selector for street imagery
ezzhang8 Jun 29, 2022
12951a7
Frontend legend visuals
ezzhang8 Jun 30, 2022
660563f
Set date filters as default
ezzhang8 Jul 6, 2022
904e61f
Frontend imagery form revamp
ezzhang8 Jul 7, 2022
9c1db88
iD/RapiD editor fromdate filter
ezzhang8 Jul 7, 2022
2056c8e
Backend add fields of imageCaptureMode and mapillaryOrganizationId
ezzhang8 Jul 7, 2022
0af3cd3
Mapillary API organization get call
ezzhang8 Jul 7, 2022
9a51f7a
Map legend update
ezzhang8 Jul 7, 2022
c5986dd
Update editor/rapideditor
ezzhang8 Jul 7, 2022
18cf0d8
Update map.js
ezzhang8 Jul 11, 2022
065521a
Merge branch 'develop' into mapillaryui
ezzhang8 Aug 4, 2022
8cfe6a1
Update action.js
ezzhang8 Aug 4, 2022
e38d9c0
Addressing comments
ezzhang8 Aug 8, 2022
15b4b2a
Removed mapillary token
ezzhang8 Aug 8, 2022
b5eca82
Ran linter commands & addressed comments
ezzhang8 Aug 8, 2022
e68b1c7
Mapillary overlay on task map
ezzhang8 Aug 4, 2022
762c995
Moved compass icon loading to load event
ezzhang8 Aug 4, 2022
1e32e74
Update map.js
ezzhang8 Aug 4, 2022
9edb7be
Visibility toggle
ezzhang8 Aug 4, 2022
c4b94ed
Redirect to mapillary app on ios/android
ezzhang8 Aug 4, 2022
f11b29a
Fixed react warnings, ran yarn prettier
ezzhang8 Aug 8, 2022
857807d
Update frontend/src/components/taskSelection/footer.js
ezzhang8 Aug 10, 2022
da14707
Mapillary tokens in env; mapbox reorganize
ezzhang8 Aug 10, 2022
5b142ea
Changed mapillary layer toggle to state-based component
ezzhang8 Aug 10, 2022
5537dfe
Replaced graph url with env
ezzhang8 Aug 10, 2022
cd01f2b
Rerun checks
ezzhang8 Aug 18, 2022
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
11 changes: 10 additions & 1 deletion backend/models/dtos/project_dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,11 @@ class ProjectDTO(Model):
enforce_random_task_selection = BooleanType(
required=False, default=False, serialized_name="enforceRandomTaskSelection"
)

earliest_street_imagery = UTCDateTimeType(serialized_name="earliestStreetImagery")
image_capture_mode = BooleanType(
required=False, default=False, serialized_name="imageCaptureMode"
)
mapillary_organization_id = StringType(serialized_name="mapillaryOrganizationId")
private = BooleanType(required=True)
changeset_comment = StringType(serialized_name="changesetComment")
osmcha_filter_id = StringType(serialized_name="osmchaFilterId")
Expand Down Expand Up @@ -462,6 +466,11 @@ class ProjectSummary(Model):
author = StringType()
created = UTCDateTimeType()
due_date = UTCDateTimeType(serialized_name="dueDate")
earliest_street_imagery = UTCDateTimeType(serialized_name="earliestStreetImagery")
image_capture_mode = BooleanType(
required=False, default=False, serialized_name="imageCaptureMode"
)
mapillary_organization_id = StringType(serialized_name="mapillaryOrganizationId")
last_updated = UTCDateTimeType(serialized_name="lastUpdated")
priority = StringType(serialized_name="projectPriority")
campaigns = ListType(ModelType(CampaignDTO), default=[])
Expand Down
12 changes: 12 additions & 0 deletions backend/models/postgis/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ class Project(db.Model):
db.String
) # Optional custom filter id for filtering on OSMCha
due_date = db.Column(db.DateTime)
earliest_street_imagery = db.Column(db.DateTime)
imagery = db.Column(db.String)
image_capture_mode = db.Column(db.Boolean, default=False)
mapillary_organization_id = db.Column(db.String)
josm_preset = db.Column(db.String)
id_presets = db.Column(ARRAY(db.String))
extra_id_params = db.Column(db.String)
Expand Down Expand Up @@ -378,6 +381,9 @@ def update(self, project_dto: ProjectDTO):
self.mapper_level = MappingLevel[project_dto.mapper_level.upper()].value
self.changeset_comment = project_dto.changeset_comment
self.due_date = project_dto.due_date
self.earliest_street_imagery = project_dto.earliest_street_imagery
self.image_capture_mode = project_dto.image_capture_mode
self.mapillary_organization_id = project_dto.mapillary_organization_id
self.imagery = project_dto.imagery
self.josm_preset = project_dto.josm_preset
self.id_presets = project_dto.id_presets
Expand Down Expand Up @@ -836,6 +842,9 @@ def get_project_summary(self, preferred_locale) -> ProjectSummary:
summary.country_tag = self.country
summary.changeset_comment = self.changeset_comment
summary.due_date = self.due_date
summary.earliest_street_imagery = self.earliest_street_imagery
summary.image_capture_mode = self.image_capture_mode
summary.mapillary_organization_id = self.mapillary_organization_id
summary.created = self.created
summary.last_updated = self.last_updated
summary.osmcha_filter_id = self.osmcha_filter_id
Expand Down Expand Up @@ -1011,6 +1020,9 @@ def _get_project_and_base_dto(self):
base_dto.changeset_comment = self.changeset_comment
base_dto.osmcha_filter_id = self.osmcha_filter_id
base_dto.due_date = self.due_date
base_dto.earliest_street_imagery = self.earliest_street_imagery
base_dto.image_capture_mode = self.image_capture_mode
base_dto.mapillary_organization_id = self.mapillary_organization_id
base_dto.imagery = self.imagery
base_dto.josm_preset = self.josm_preset
base_dto.id_presets = self.id_presets
Expand Down
1 change: 1 addition & 0 deletions frontend/.env.expand
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ REACT_APP_SENTRY_FRONTEND_DSN=$TM_SENTRY_FRONTEND_DSN
REACT_APP_ENVIRONMENT=$TM_ENVIRONMENT
REACT_APP_TM_DEFAULT_CHANGESET_COMMENT=$TM_DEFAULT_CHANGESET_COMMENT
REACT_APP_RAPID_EDITOR_URL=$RAPID_EDITOR_URL
REACT_APP_MAPILLARY_TOKEN=$MAPILLARY_ACCESS_TOKEN
Binary file added frontend/src/assets/img/mapillary-compass.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion frontend/src/assets/styles/_extra.scss
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@ a[href="https://www.mapbox.com/map-feedback/"] {
}
}

div.react-datepicker-popper {
z-index: 5;
}

.comment-textarea {
box-sizing: border-box;

Expand All @@ -398,4 +402,4 @@ a[href="https://www.mapbox.com/map-feedback/"] {
.sticky-top {
position: sticky !important;
top: 0;
}
}
29 changes: 27 additions & 2 deletions frontend/src/components/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import '@hotosm/id/dist/iD.css';

import { OSM_CONSUMER_KEY, OSM_CONSUMER_SECRET, OSM_SERVER_URL } from '../config';

export default function Editor({ setDisable, comment, presets, imagery, gpxUrl }) {
export default function Editor({
setDisable,
comment,
presets,
imagery,
gpxUrl,
earliestStreetImagery,
imageCaptureMode,
}) {
const dispatch = useDispatch();
const session = useSelector((state) => state.auth.get('session'));
const iDContext = useSelector((state) => state.editor.context);
Expand Down Expand Up @@ -105,8 +113,25 @@ export default function Editor({ setDisable, comment, presets, imagery, gpxUrl }
setDisable(false);
}
});

if (imageCaptureMode) {
if (earliestStreetImagery) {
iDContext.photos().setDateFilter('fromDate', earliestStreetImagery.substr(0, 10), false);
}
window.location.href =
window.location.href + '&photo_overlay=mapillary,mapillary-map-features,mapillary-signs';
}
}
}, [session, iDContext, setDisable, presets, locale, gpxUrl]);
}, [
session,
iDContext,
setDisable,
presets,
locale,
gpxUrl,
earliestStreetImagery,
imageCaptureMode,
]);

return <div className="w-100 vh-minus-77-ns" id="id-container"></div>;
}
8 changes: 8 additions & 0 deletions frontend/src/components/projectCreate/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ export default defineMessages({
id: 'management.projects.create.trim_tasks.trim_to_aoi',
defaultMessage: 'Trim the tasks to define the exact Area of Interest for mapping.',
},
trimExcludeWater: {
id: 'management.projects.create.trim_tasks.trim_exclude_water',
defaultMessage: 'Trim the tasks to exclude water areas.',
},
trimCoverPathsRoads: {
id: 'management.projects.create.trim_tasks.trim_cover_paths_roads',
defaultMessage: 'Trim the tasks to only cover paths and roads.',
},
tinyTasks: {
id: 'management.projects.create.trim_tasks.tiny_tasks',
defaultMessage:
Expand Down
25 changes: 25 additions & 0 deletions frontend/src/components/projectCreate/trimProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const removeTinyTasks = (metadata, updateMetadata) => {
export default function TrimProject({ metadata, mapObj, updateMetadata }) {
const token = useSelector((state) => state.auth.get('token'));
const [clipStatus, setClipStatus] = useState(false);
const [roadStatus, setRoadStatus] = useState(false);
const [waterStatus, setWaterStatus] = useState(false);

const [tinyTasksNumber, setTinyTasksNumber] = useState(0);

const trimTaskGridAsync = useAsync(trimTaskGrid);
Expand Down Expand Up @@ -72,6 +75,28 @@ export default function TrimProject({ metadata, mapObj, updateMetadata }) {
onChange={() => setClipStatus(!clipStatus)}
label={<FormattedMessage {...messages.trimToAOI} />}
/>

<div className="pt3">
<SwitchToggle
isChecked={waterStatus}
labelPosition="right"
onChange={() => {
setWaterStatus(!waterStatus);
}}
label={<FormattedMessage {...messages.trimExcludeWater} />}
/>
</div>
<div className="pt3">
<SwitchToggle
isChecked={roadStatus}
labelPosition="right"
onChange={() => {
setRoadStatus(!roadStatus);
}}
label={<FormattedMessage {...messages.trimCoverPathsRoads} />}
/>
</div>

<div className="pt3">
<CustomButton
onClick={() =>
Expand Down
87 changes: 86 additions & 1 deletion frontend/src/components/projectEdit/imageryForm.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import React, { useContext, useState, useLayoutEffect } from 'react';
import Select from 'react-select';
import { FormattedMessage } from 'react-intl';

import DatePicker from 'react-datepicker';
import { SwitchToggle } from '../formInputs';
import messages from './messages';
import { StateContext, styleClasses } from '../../views/projectEdit';
import { Code } from '../code';
import { fetchLocalJSONAPI } from '../../network/genericJSONRequest';
import { useImageryOption, IMAGERY_OPTIONS } from '../../hooks/UseImageryOption';
import { MAPILLARY_TOKEN, MAPILLARY_GRAPH_URL } from '../../config';
import axios from 'axios';

export const ImageryForm = () => {
const { projectInfo, setProjectInfo } = useContext(StateContext);
const [licenses, setLicenses] = useState(null);

const [organization, setOrganization] = useState(null);

useLayoutEffect(() => {
const fetchLicenses = async () => {
fetchLocalJSONAPI('licenses/')
Expand All @@ -21,6 +26,15 @@ export const ImageryForm = () => {
fetchLicenses();
}, [setLicenses]);

if (projectInfo.mapillaryOrganizationId) {
axios
.get(
`${MAPILLARY_GRAPH_URL}${projectInfo.mapillaryOrganizationId}?access_token=${MAPILLARY_TOKEN}&fields=name`,
)
.then((resp) => setOrganization(resp.data.name))
.catch(() => setOrganization(null));
}

let defaultValue = null;
if (licenses !== null && projectInfo.licenseId !== null) {
defaultValue = licenses.filter((l) => l.licenseId === projectInfo.licenseId)[0];
Expand All @@ -34,6 +48,7 @@ export const ImageryForm = () => {
</label>
<ImageryField imagery={projectInfo.imagery} setProjectInfo={setProjectInfo} />
</div>

<div className={styleClasses.divClass}>
<label className={styleClasses.labelClass}>
<FormattedMessage {...messages.license} />
Expand All @@ -54,6 +69,76 @@ export const ImageryForm = () => {
className="w-50 z-1"
/>
</div>

<div className={styleClasses.divClass}>
<label className={styleClasses.labelClass}>
<FormattedMessage {...messages.imageCaptureMode} />
</label>
<p className={styleClasses.pClass}>
<FormattedMessage {...messages.imageCaptureModeInfo} />
</p>
<SwitchToggle
label={<FormattedMessage {...messages.imageCaptureMode} />}
labelPosition="right"
isChecked={projectInfo.imageCaptureMode}
onChange={() =>
setProjectInfo({ ...projectInfo, imageCaptureMode: !projectInfo.imageCaptureMode })
}
/>
</div>

{projectInfo.imageCaptureMode && (
<>
<div className={styleClasses.divClass}>
<label className={styleClasses.labelClass}>
<FormattedMessage {...messages.imageryCaptureDate} />
</label>
<FormattedMessage {...messages.imageryCaptureDateAfter} />
<span>: &nbsp;&nbsp;</span>
<DatePicker
selected={Date.parse(projectInfo.earliestStreetImagery)}
onChange={(date) =>
setProjectInfo({
...projectInfo,
earliestStreetImagery: date,
})
}
placeholderText="DD/MM/YYYY"
dateFormat="dd/MM/yyyy"
className={styleClasses.inputClass}
showYearDropdown
scrollableYearDropdown
/>
</div>

<div className={styleClasses.divClass}>
<label className={styleClasses.labelClass}>
<FormattedMessage {...messages.mapillaryOrganizationId} />
</label>
<p className={styleClasses.pClass}>
<FormattedMessage {...messages.mapillaryOrganizationIdInfo} />
</p>

<b>
<FormattedMessage {...messages.mapillaryOrganizationSelected} />
</b>
<span>:&nbsp;{organization}</span>

<input
className={styleClasses.inputClass}
type="text"
name="mapillaryOrganizationId"
value={projectInfo.mapillaryOrganizationId || ''}
onChange={(e) => {
setProjectInfo({
...projectInfo,
mapillaryOrganizationId: e.target.value,
});
}}
/>
</div>
</>
)}
</div>
);
};
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/components/projectEdit/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,34 @@ export default defineMessages({
defaultMessage:
'This will remove the custom editor from the project. Are you sure you don\'t want to disable the custom editor by toggling the "Enabled" checkbox above?',
},
imageCaptureMode: {
id: 'projects.formInputs.imageCaptureMode',
defaultMessage: 'Image capture mode',
},
imageCaptureModeInfo: {
id: 'projects.formInputs.imageCaptureMode.info',
defaultMessage: 'Adapts Tasking Manager to street imagery capture workflow.',
},
imageryCaptureDate: {
id: 'projects.formInputs.imageryCaptureDate',
defaultMessage: 'Imagery capture date',
},
imageryCaptureDateAfter: {
id: 'projects.formInputs.imageryCaptureDate.after',
defaultMessage: 'After',
},
mapillaryOrganizationId: {
id: 'projects.formInputs.mapillaryOrganizationId',
defaultMessage: 'Mapillary organization ID',
},
mapillaryOrganizationIdInfo: {
id: 'projects.formInputs.mapillaryOrganizationId.info',
defaultMessage: '15-digit identifier to filter Mapillary contributions.',
},
mapillaryOrganizationSelected: {
id: 'projects.formInputs.mapillaryOrganizationId.selected',
defaultMessage: 'Organization',
},
noMappingEditor: {
id: 'projects.formInputs.noMappingEditor',
defaultMessage: 'At least one editor must be enabled for mapping',
Expand Down
27 changes: 26 additions & 1 deletion frontend/src/components/rapidEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export default function RapidEditor({
imagery,
gpxUrl,
powerUser = false,
earliestStreetImagery,
imageCaptureMode,
}) {
const dispatch = useDispatch();
const session = useSelector((state) => state.auth.get('session'));
Expand Down Expand Up @@ -113,8 +115,31 @@ export default function RapidEditor({
setDisable(false);
}
});

if (imageCaptureMode) {
if (earliestStreetImagery) {
RapiDContext.photos().setDateFilter(
'fromDate',
earliestStreetImagery.substr(0, 10),
false,
);
}

window.location.href =
window.location.href + '&photo_overlay=mapillary,mapillary-map-features,mapillary-signs';
}
}
}, [session, RapiDContext, setDisable, presets, locale, gpxUrl, powerUser]);
}, [
session,
RapiDContext,
setDisable,
presets,
locale,
gpxUrl,
powerUser,
earliestStreetImagery,
imageCaptureMode,
]);

return <div className="w-100 vh-minus-77-ns" id="rapid-container"></div>;
}
Loading