Skip to content
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

Feature/live monitoring #6151

Closed
wants to merge 64 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
e7da777
Remove references to rapid context methods that no longer exist.
Bonkles Apr 11, 2023
8c0e283
Rapid v2: Show the GPX data
tsmock Apr 12, 2023
d2212be
Fix NPE in rapidEditor
tsmock Apr 12, 2023
e000b70
Rapid updates
tsmock Apr 17, 2023
5fc041d
Bump rapid version to 2.0.3, also make use of imagery and init promis…
Bonkles May 3, 2023
e8e5031
Fix bug that would cause the 'custom editor' useEffect code to fire e…
Bonkles May 3, 2023
8b014b1
Update yarn.lock
tsmock Jun 8, 2023
f392ca1
Rapid: Update for next Rapid version
tsmock Jul 27, 2023
3748ace
Update Rapid to v2.1.1
tsmock Aug 30, 2023
99e2fe7
Rapid: Use system interface for setting up Rapid
tsmock Aug 30, 2023
4076030
Rapid: Load css and data resources from CDN
tsmock Aug 31, 2023
369cbea
Rapid: Fix issue where toggling sidebar would lose data, ensure mapvi…
tsmock Aug 31, 2023
8bc4c34
Don't try to upload to sentry if we aren't logged in
tsmock Aug 31, 2023
2853556
Rapid: Fix preauth
tsmock Aug 31, 2023
8ccc027
Rapid: Use URL hash parameters instead of function calls
tsmock Sep 5, 2023
46a7d83
Rapid: Get it working between task switches
tsmock Sep 8, 2023
afbada4
Rapid: Store dom node in state for reuse
tsmock Sep 8, 2023
3b54de2
Rapid: Export functions that may be useful for iD and add basic tests…
tsmock Sep 8, 2023
0a88b11
Listen for reset events to update task state disable state
tsmock Sep 11, 2023
95417b3
Merge branch 'chore/update-rapid' of github.com:facebook/OSM-HOT-Task…
royallsilwallz Oct 16, 2023
5475b68
fix test case fail error
royallsilwallz Oct 16, 2023
3420409
Merge branch 'develop' of github.com:hotosm/tasking-manager into develop
royallsilwallz Nov 29, 2023
94524a2
Merge branch 'chore/update-rapid' of github.com:facebook/OSM-HOT-Task…
royallsilwallz Nov 29, 2023
8c1de4b
+ Add a new view for live quality monitoring
emi420 Dec 6, 2023
a69ab25
Add a TODO message for enable/disable live monitoring button
emi420 Dec 6, 2023
4f574bb
Remove log message
emi420 Dec 6, 2023
9cb3dff
Fix for project's area
emi420 Dec 14, 2023
5ca8a63
Fix for project's area / center
emi420 Dec 14, 2023
8ce1499
Remove log msg
emi420 Dec 14, 2023
35a4490
'building' as default tag
emi420 Dec 14, 2023
6e5809c
Merge branch 'develop' of github.com:hotosm/tasking-manager into develop
royallsilwallz Dec 15, 2023
77b2316
fix: removed unwanted type of Object
varun2948 Dec 15, 2023
efa4727
fix: mapbox token used from config
varun2948 Dec 15, 2023
8e3b30d
tmpfix: yarn deps lodash fails during prep script
mahesh-naxa Dec 15, 2023
8272c9b
Merge branch 'feature/liveMonitoring' of github.com:hotosm/tasking-ma…
royallsilwallz Dec 18, 2023
24186da
Ignore eslint errors in project live monitoring
royallsilwallz Dec 18, 2023
c914477
Use `UNDERPASS_URL` in `UnderpassMap` config from env in `ProjectLive…
royallsilwallz Jan 3, 2024
9a50e26
Add `usePriorityGeojsonQuery` hook for fetching priority area of live…
royallsilwallz Jan 3, 2024
184f340
Make `ProjectLiveMonitoring` button conditional based on supported areas
royallsilwallz Jan 3, 2024
9437bc2
Add `UNDERPASS_URL` to example.env
royallsilwallz Jan 3, 2024
8218c0f
Use `available.json` instead of `priority.geojson` in `Live Monitoring`
royallsilwallz Jan 8, 2024
3913a11
resolve merge conflicts
royallsilwallz Jan 8, 2024
112fc5a
Merge branch 'develop' of github.com:hotosm/tasking-manager into feat…
royallsilwallz Jan 9, 2024
70e56fc
Updates following Underpass API changes. Remove hashtag filter. Add f…
emi420 Jan 9, 2024
baabdac
Merge branch 'feature/liveMonitoring' of github.com:hotosm/tasking-ma…
royallsilwallz Jan 10, 2024
49180f6
Upgrade `Underpass UI`
royallsilwallz Jan 10, 2024
a424c7a
Add same background imagery as project in Live Monitoring Map View
royallsilwallz Jan 13, 2024
bfa4171
Fix console error for `unsupported style property` in `Live Monitoring`
royallsilwallz Jan 13, 2024
b8932b8
fix: header removed, search bar adjusted
manjitapandey Jan 17, 2024
dc893a0
padding changes
manjitapandey Jan 17, 2024
caa68ca
Fixes for styling, more compact design
emi420 Jan 17, 2024
da9d7f5
Add `OSM` as default layer in `Live Monitoring`
royallsilwallz Jan 18, 2024
559efa6
Fix crash issue when `imagery` not selected in `Live Monitoring`
royallsilwallz Jan 18, 2024
5e84581
Fix console error for `unsupported style property` in `Live Monitoring`
royallsilwallz Jan 18, 2024
d64f883
Show `Live Monitoring` button only for `PUBLISHED` projects
royallsilwallz Jan 18, 2024
e7ca921
Change `Bing Imagery URL` to `https` in `Live Monitoring`
royallsilwallz Jan 18, 2024
f3d8c90
Style fixes in `Live Monitoring` feature
royallsilwallz Jan 22, 2024
a2784fe
Update for Underpass API URLs
emi420 Jan 23, 2024
aebc222
Fix for Underpass API URL env variable on config file
emi420 Jan 24, 2024
beb3578
Data Quality: + Option for list all features or only invalid ones
emi420 Jan 24, 2024
66ed8a6
Remove `Footer` & `Organization Banner` in `Live Monitoring`
royallsilwallz Jan 25, 2024
c1a294f
Adjust `Live Monitoring` view to fit screen view
royallsilwallz Jan 25, 2024
9c5e4f1
Fix `Live Monitoring` design issues
royallsilwallz Jan 29, 2024
c9e9b19
Upgrade `@hotosm/underpass-ui` package
royallsilwallz Feb 14, 2024
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
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
"@formatjs/macro": "^0.2.8",
"@hotosm/id": "^2.21.1",
"@hotosm/iso-countries-languages": "^1.1.2",
"@hotosm/underpass-ui": "https://github.com/hotosm/underpass-ui.git",
"@mapbox/mapbox-gl-draw": "^1.4.1",
"@mapbox/mapbox-gl-geocoder": "^5.0.1",
"@mapbox/mapbox-gl-language": "^0.10.1",
"@placemarkio/geo-viewport": "^1.0.1",
"@rapideditor/rapid": "^2.1.1",
"@sentry/react": "^7.60.1",
"@tmcw/togeojson": "^4.7.0",
"@tanstack/react-query": "^4.29.7",
"@tanstack/react-query-devtools": "^4.29.7",
"@tmcw/togeojson": "^4.7.0",
"@turf/area": "^6.5.0",
"@turf/bbox": "^6.5.0",
"@turf/bbox-polygon": "^6.5.0",
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/components/projectDetail/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { PermissionBox } from './permissionBox';
import { CustomButton } from '../button';
import { ProjectInfoPanel } from './infoPanel';
import { OSMChaButton } from './osmchaButton';
import { LiveViewButton } from './liveViewButton';
import { useSetProjectPageTitleTag } from '../../hooks/UseMetaTags';
import { useProjectContributionsQuery, useProjectTimelineQuery } from '../../api/projects';
import { Alert } from '../alert';
Expand Down Expand Up @@ -317,6 +318,15 @@ export const ProjectDetail = (props) => {
project={props.project}
className="bg-white blue-dark ba b--grey-light pa3"
/>

{/* TODO: Enable/disable this button depending of
Underpass availability for the project's area.
https://underpass.hotosm.org/priority.geojson */}
<LiveViewButton
projectId={props.project.projectId}
className="bg-white blue-dark ba b--grey-light pa3"
/>

<DownloadAOIButton
projectId={props.project.projectId}
className="bg-white blue-dark ba b--grey-light pa3"
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/components/projectDetail/liveViewButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';

import messages from './messages';
import { CustomButton } from '../button';

export const LiveViewButton = ({ projectId, className, compact = false }: Object) => (
emi420 marked this conversation as resolved.
Show resolved Hide resolved
<Link to={`/projects/${projectId}/live`} className="pr2">
{
<CustomButton className={className}>
{compact ? (
<FormattedMessage {...messages.live} />
) : (
<FormattedMessage {...messages.liveMonitoring} />
)}
</CustomButton>
}
</Link>
);
8 changes: 8 additions & 0 deletions frontend/src/components/projectDetail/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,14 @@ export default defineMessages({
id: 'project.detail.sections.contributions.osmcha',
defaultMessage: 'Changesets in OSMCha',
},
live: {
id: 'project.detail.sections.contributions.live',
defaultMessage: 'Live',
},
liveMonitoring: {
id: 'project.detail.sections.contributions.liveMonitoring',
defaultMessage: 'Live monitoring',
},
changesets: {
id: 'project.detail.sections.contributions.changesets',
defaultMessage: 'Changesets',
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ export const router = createBrowserRouter(
}}
ErrorBoundary={FallbackComponent}
/>
<Route
path="projects/:id/live"
lazy={async () => {
const { ProjectLiveMonitoring } = await import(
'./views/projectLiveMonitoring' /* webpackChunkName: "projectLiveMonitoring" */
);
return { Component: ProjectLiveMonitoring };
}}
ErrorBoundary={FallbackComponent}
/>
<Route
path="organisations/:id/stats/"
lazy={async () => {
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/views/projectLiveMonitoring.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@import "@hotosm/underpass-ui/dist/index.css";

.maplibregl-map {
height: 100vh;
}

.top {
position: absolute;
top: 390px;
left: 20px;
z-index: 999;
}

233 changes: 233 additions & 0 deletions frontend/src/views/projectLiveMonitoring.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import React, { useState, useRef, useEffect } from "react";
import { UnderpassFeatureList, UnderpassMap, HOTTheme, UnderpassFeatureStats, UnderpassValidationStats } from "@hotosm/underpass-ui";
import { ProjectHeader } from '../components/projectDetail/header';
import { useSetTitleTag } from '../hooks/UseMetaTags';
import { useParams } from 'react-router-dom';
import { useFetch } from '../hooks/UseFetch';
import ReactPlaceholder from 'react-placeholder';
import centroid from '@turf/centroid';
import "./projectLiveMonitoring.css";

const config = {
API_URL: "https://underpass.hotosm.org:8000",
emi420 marked this conversation as resolved.
Show resolved Hide resolved
MAPBOX_TOKEN: "pk.eyJ1IjoiZW1pNDIwIiwiYSI6ImNqZW9leG5pZTAxYWwyeG83bHU0eHM0ZXcifQ.YWmk4Rp8FBGCLmpx_huJYw"
};

const statusList = {
ALL: "",
UNSQUARED: "badgeom",
OVERLAPPING: "overlapping",
BADVALUE: "badvalue",
}

const mappingTypesTags = {
ROADS: "highway",
BUILDINGS: "building",
WATERWAYS: "waterway"
}

export function ProjectLiveMonitoring() {
const { id } = useParams();
const [coords, setCoords] = useState([0,0]);
const [activeFeature, setActiveFeature] = useState(null);
const [tags, setTags] = useState("building");
const [hashtag, setHashtag] = useState("hotosm-project-" + id);
const [mapSource, setMapSource] = useState("osm");
const [realtimeList, setRealtimeList] = useState(false);
const [realtimeMap, setRealtimeMap] = useState(false);
const [status, setStatus] = useState(statusList.UNSQUARED);
const [area, setArea] = useState(null);
const tagsInputRef = useRef("");
const hashtagInputRef = useRef("");
const styleSelectRef = useRef();

useSetTitleTag(`Project #${id} Live Monitoring`);
const [error, loading, data] = useFetch(`projects/${id}/`, id);

const [areaOfInterest, setAreaOfInterest] = useState(null);
const [project, setProject] = useState(null);

useEffect(() => {
setProject(data);
}, [data])

useEffect(() => {
if (project && project.areaOfInterest) {
setCoords(centroid(project.areaOfInterest).geometry.coordinates.reverse());
setAreaOfInterest([
].join(",")
);
setTags(mappingTypesTags[project.mappingTypes]);
}
}, [project]);

const hottheme = HOTTheme();

const defaultMapStyle = {
waysLine: {
...hottheme.map.waysLine,
"line-opacity": .8,
},
waysFill: {
...hottheme.map.waysFill,
"fill-opacity":
[
"match",
["get", "type"],
"LineString", 0, .3
]
},
nodesSymbol: {
...hottheme.map.nodesSymbol,
"icon-opacity": [
"match",
["get", "type"],
"Point", .8, 0
],
},
};

const [demoTheme, setDemoTheme] = useState({
map: defaultMapStyle
});

const handleFilterClick = (e) => {
e.preventDefault();
setTags(tagsInputRef.current.value);
setHashtag(hashtagInputRef.current.value);
return false;
}

const handleMapSourceSelect = (e) => {
setMapSource(e.target.options[e.target.selectedIndex].value);
}

const handleMapMove = ({ bbox }) => {
setArea(bbox);
}
const handleMapLoad = ({ bbox }) => {
setArea(bbox);
}

return (
<ReactPlaceholder
showLoadingAnimation={true}
rows={26}
ready={!error && !loading}
className="pr3"
>
<div>
<div className="w-100 fl pv3 ph2 ph4-ns bg-white blue-dark">
<ProjectHeader project={project} showEditLink={true} />
</div>
<div className="flex p-2">
<div style={{flex: 2}}>
<div className="top">
<form>
<input
className="border px-2 py-2 text-sm"
type="text"
placeholder="key (ex: building=yes)"
ref={tagsInputRef}
defaultValue="building"
/>
&nbsp;
<input
className="border px-2 py-2 text-sm"
type="text"
placeholder="hashtag (ex: hotosm-project)"
ref={hashtagInputRef}
defaultValue={"hotosm-project-" + id}
/>
&nbsp;
<button className="inline-flex items-center rounded bg-primary px-2 py-2 text-sm font-medium text-white" onClick={handleFilterClick}>Search</button>
</form>
<select onChange={handleMapSourceSelect} ref={styleSelectRef} className="border mt-2 bg-white px-2 py-2 text-sm">
<option value="osm">OSM</option>
<option value="bing">Bing</option>
<option value="esri">ESRI</option>
<option value="mapbox">Mapbox</option>
<option value="oam">OAM</option>
</select>
</div>
<UnderpassMap
center={coords}
tags={tags}
hashtag={hashtag}
highlightDataQualityIssues
popupFeature={activeFeature}
source={mapSource}
config={config}
realtime={realtimeMap}
theme={demoTheme}
zoom={17}
onMove={handleMapMove}
onLoad={handleMapLoad}
/>
</div>
<div style={{
flex: 1,
padding: 10,
backgroundColor: `rgb(${hottheme.colors.white})`}}>
<div className="border-b-2 pb-5 space-y-3">
<UnderpassFeatureStats
tags={tags}
hashtag={hashtag}
apiUrl={config.API_URL}
area={areaOfInterest}
/>
<UnderpassValidationStats
tags={tags}
hashtag={hashtag}
apiUrl={config.API_URL}
status="badgeom"
area={areaOfInterest}
/>
</div>
<div className="border-b-2 py-5 mb-5">
<form className="space-x-2 mb-3">
<input onChange={() => { setRealtimeList(!realtimeList)}} name="liveListCheckbox" type="checkbox" />
<label target="liveListCheckbox">Live list</label>
<input onChange={() => { setRealtimeMap(!realtimeMap)}} name="liveMapCheckbox" type="checkbox" />
<label target="liveMapCheckbox">Live map</label>
</form>
<form className="space-x-2">
<input checked={status === statusList.ALL} onChange={() => { setStatus(statusList.ALL) }} name="allCheckbox" id="allCheckbox" type="radio" />
<label htmlFor="allCheckbox">All</label>
<input checked={status === statusList.UNSQUARED} onChange={() => { setStatus(statusList.UNSQUARED) }} name="geospatialCheckbox" id="geospatialCheckbox" type="radio" />
<label htmlFor="geospatialCheckbox">Geospatial</label>
<input checked={status === statusList.BADVALUE} onChange={() => { setStatus(statusList.BADVALUE) }} name="semanticCheckbox" id="semanticCheckbox" type="radio" />
<label htmlFor="semanticCheckbox">Semantic</label>
</form>
</div>
<div style={{ height: "512px", overflow: "hidden" }}>
<UnderpassFeatureList
tags={tags}
hashtag={hashtag}
page={0}
onSelect={(feature) => {
setCoords([feature.lat, feature.lon]);
const tags = JSON.stringify(feature.tags);
const status = feature.status;
setActiveFeature({properties: { tags, status } , ...feature});
}}
realtime={realtimeList}
config={config}
status={status}
orderBy="created_at"
onFetchFirstTime={(mostRecentFeature) => {
if (mostRecentFeature) {
setCoords([mostRecentFeature.lat, mostRecentFeature.lon]);
}
}}
/>
</div>
</div>
</div>
</div>
</ReactPlaceholder>
);
}

export default ProjectLiveMonitoring;

Loading
Loading