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

Render points on both frontends, with colour determined by status #2174

Merged
merged 5 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions src/backend/app/projects/project_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ async def get_or_set_data_extract_url(
str: URL to fgb file in S3.
"""
project_id = db_project.id
# If url, get extract
# If not url, get new extract / set in db
# If url passed, get extract
# If no url passed, get new extract / set in db
if not url:
existing_url = db_project.data_extract_url

Expand Down
4 changes: 3 additions & 1 deletion src/backend/app/projects/project_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

from app.central.central_schemas import ODKCentralDecrypted, ODKCentralIn
from app.config import decrypt_value, encrypt_value, settings
from app.db.enums import BackgroundTaskStatus, GeomStatus, ProjectPriority
from app.db.enums import BackgroundTaskStatus, DbGeomType, GeomStatus, ProjectPriority
from app.db.models import DbBackgroundTask, DbBasemap, DbProject, slugify
from app.db.postgis_utils import (
geojson_to_featcol,
Expand Down Expand Up @@ -182,6 +182,8 @@ class ProjectIn(ProjectInBase, ODKCentralIn):
xform_category: str
# Ensure geojson_pydantic.Polygon
outline: Polygon
# Omit new_geom_type as we calculate this automatically
new_geom_type: Annotated[Optional[DbGeomType], Field(exclude=True)] = None

@model_validator(mode="after")
def generate_location_str(self) -> Self:
Expand Down
Binary file added src/frontend/src/assets/images/map-pin-grey.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed src/frontend/src/assets/images/red_marker.png
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -437,32 +437,4 @@ const VectorLayer = ({
return null;
};

// TODO replace with typescript
// VectorLayer.propTypes = {
// // Ensure either geojson or fgbUrl is provided
// geojson: (props, propName, componentName) => {
// if (!props.geojson && !props.fgbUrl) {
// return new Error(`One of 'geojson' or 'fgbUrl' is required in '${componentName}'`);
// }
// if (props.geojson && props.fgbUrl) {
// return new Error(`Only one of 'geojson' or 'fgbUrl' should be provided in '${componentName}'`);
// }
// },
// fgbUrl: (props, propName, componentName) => {
// if (!props.geojson && !props.fgbUrl) {
// return new Error(`One of 'geojson' or 'fgbUrl' is required in '${componentName}'`);
// }
// if (props.geojson && props.fgbUrl) {
// return new Error(`Only one of 'geojson' or 'fgbUrl' should be provided in '${componentName}'`);
// }
// },
// fgbExtent: PropTypes.object,
// style: PropTypes.object,
// zIndex: PropTypes.number,
// zoomToLayer: PropTypes.bool,
// viewProperties: PropTypes.object,
// mapOnClick: PropTypes.func,
// onModify: PropTypes.func,
// };

export default VectorLayer;
4 changes: 0 additions & 4 deletions src/frontend/src/components/createnewproject/SplitTasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,6 @@ const SplitTasks = ({ flag, setGeojsonFile, customDataExtractUpload, additionalF
// Create a file object from the Blob
const taskAreaGeojsonFile = new File([taskAreaBlob], 'data.json', { type: 'application/json' });

// FIXME for now hardcoded as Polygon projects (add project creation UI for user selection)
// projectData = { ...projectData, new_geom_type: projectDetails.new_geom_type };
projectData = { ...projectData, new_geom_type: 'POLYGON' };

dispatch(
CreateProjectService(
`${import.meta.env.VITE_API_URL}/projects?org_id=${projectDetails.organisation_id}`,
Expand Down
13 changes: 8 additions & 5 deletions src/frontend/src/components/home/ProjectListMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,27 @@ import { ClusterLayer } from '@/components/MapComponent/OpenLayersComponent/Laye
import CoreModules from '@/shared/CoreModules';
import { geojsonObjectModel, geojsonObjectModelType } from '@/constants/geojsonObjectModal';
import { defaultStyles } from '@/components/MapComponent/OpenLayersComponent/helpers/styleUtils';
import MarkerIcon from '@/assets/images/red_marker.png';
import MarkerIcon from '@/assets/images/map-pin-primary.png';
import { useNavigate } from 'react-router-dom';
import { Style, Text, Icon, Fill } from 'ol/style';
import { projectType } from '@/models/home/homeModel';
import LayerSwitchMenu from '../MapComponent/OpenLayersComponent/LayerSwitcher/LayerSwitchMenu';

const getIndividualStyle = (featureProperty) => {
const getIndividualClusterPointStyle = (featureProperty) => {
const style = new Style({
image: new Icon({
anchor: [0.5, 1],
scale: 1.1,
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: MarkerIcon,
scale: 0.08,
}),
text: new Text({
text: featureProperty?.project_id,
fill: new Fill({
color: 'black',
}),
offsetY: 35,
offsetY: 42,
font: '20px Times New Roman',
}),
});
Expand Down Expand Up @@ -93,7 +96,7 @@ const ProjectListMap = () => {
opacity: 90,
}}
mapOnClick={projectClickOnMap}
getIndividualStyle={getIndividualStyle}
getIndividualStyle={getIndividualClusterPointStyle}
/>
)}
</MapComponent>
Expand Down
1 change: 0 additions & 1 deletion src/frontend/src/models/project/projectModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export type projectInfoType = {
bbox: [number, number, number, number];
last_active: string;
num_contributors: number | null;
new_geom_type: NewGeomTypes;
};

export type taskType = {
Expand Down
4 changes: 4 additions & 0 deletions src/frontend/src/shared/AssetModules.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ import {
} from '@mui/icons-material';
import LockPng from '@/assets/images/lock.png';
import RedLockPng from '@/assets/images/red-lock.png';
import MapPinRed from '@/assets/images/map-pin-primary.png';
import MapPinGrey from '@/assets/images/map-pin-grey.png';
import { styled, alpha } from '@mui/material/styles';
export default {
ExitToAppIcon,
Expand Down Expand Up @@ -135,6 +137,8 @@ export default {
ArrowBackIcon,
LockPng,
RedLockPng,
MapPinRed,
MapPinGrey,
TaskIcon,
SubmissionIcon,
FeatureIcon,
Expand Down
2 changes: 0 additions & 2 deletions src/frontend/src/store/types/ICreateProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export type CreateProjectStateTypes = {
descriptionToFocus: string | null;
task_num_buildings: number | null;
task_split_dimension: number | null;
new_geom_type: NewGeomTypes;
};
export type ValidateCustomFormResponse = {
detail: { message: string; possible_reason: string };
Expand Down Expand Up @@ -111,7 +110,6 @@ export type ProjectDetailsTypes = {
hasCustomTMS: boolean;
customFormUpload: any;
hasAdditionalFeature: boolean;
new_geom_type: NewGeomTypes;
project_admins: number[];
};

Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/types/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ export type NewGeomTypes = {
LINESTRING: 'LINESTRING';
};

export enum GeoGeomTypesEnum {
POINT = 'Point',
POLYGON = 'Polygon',
LINESTRING = 'LineString',
}

export enum submission_status {
null = 'Received',
hasIssues = 'Has issues',
Expand Down
123 changes: 66 additions & 57 deletions src/frontend/src/utilfunctions/getTaskStatusStyle.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Fill, Icon, Stroke, Style } from 'ol/style';
import { asArray, asString } from 'ol/color';
import { getCenter } from 'ol/extent';
import { Point } from 'ol/geom';
import AssetModules from '@/shared/AssetModules';
import { entity_state } from '@/types/enums';
import { EntityOsmMap } from '@/store/types/IProject';
import { GeoGeomTypesEnum } from '@/types/enums';

function createPolygonStyle(fillColor: string, strokeColor: string) {
return new Style({
Expand All @@ -18,14 +18,29 @@ function createPolygonStyle(fillColor: string, strokeColor: string) {
});
}

function createIconStyle(iconSrc: string) {
function createFeaturePolygonStyle(color: string, strokeOpacity: number = 1) {
return new Style({
stroke: new Stroke({
color: 'rgb(0,0,0,0.5)',
width: 1,
opacity: strokeOpacity,
}),
fill: new Fill({
color: color,
}),
});
}

function createIconStyle(iconSrc: string, scale: number = 0.8, color: any = 'red') {
return new Style({
image: new Icon({
anchor: [0.5, 1],
scale: 0.8,
scale: scale,
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: iconSrc,
color: color,
opacity: 1,
}),
geometry: function (feature) {
const polygonCentroid = getCenter(feature.getGeometry().getExtent());
Expand All @@ -34,6 +49,11 @@ function createIconStyle(iconSrc: string) {
});
}

function updateRbgAlpha(colorString: string, alphaVal: number) {
const val = asArray(colorString);
return `rgb(${val.slice(0, 3).join(',')},${alphaVal})`;
}

const strokeColor = 'rgb(0,0,0,0.3)';
const secondaryStrokeColor = 'rgb(0,0,0,1)';

Expand Down Expand Up @@ -88,61 +108,50 @@ const getTaskStatusStyle = (feature: Record<string, any>, mapTheme: Record<strin
return geojsonStyles[status];
};

export const getFeatureStatusStyle = (osmId: string, mapTheme: Record<string, any>, entityOsmMap: EntityOsmMap[]) => {
const entity = entityOsmMap?.find((entity) => entity?.osm_id === osmId) as EntityOsmMap;
export const getFeatureStatusStyle = (geomType: string, mapTheme: Record<string, any>, mappingStatus: string) => {
let geojsonStyles;

let status = entity_state[entity?.status];
if (geomType === GeoGeomTypesEnum.POINT) {
geojsonStyles = {
READY: createIconStyle(
AssetModules.MapPinGrey,
1.1,
updateRbgAlpha(mapTheme.palette.entityStatusColors.ready, 1),
),
OPENED_IN_ODK: createIconStyle(
AssetModules.MapPinGrey,
1.1,
updateRbgAlpha(mapTheme.palette.entityStatusColors.opened_in_odk, 1),
),
SURVEY_SUBMITTED: createIconStyle(
AssetModules.MapPinGrey,
1.1,
updateRbgAlpha(mapTheme.palette.entityStatusColors.survey_submitted, 1),
),
MARKED_BAD: createIconStyle(
AssetModules.MapPinGrey,
1.1,
updateRbgAlpha(mapTheme.palette.entityStatusColors.marked_bad, 1),
),
VALIDATED: createIconStyle(
AssetModules.MapPinGrey,
1.1,
updateRbgAlpha(mapTheme.palette.entityStatusColors.validated, 1),
),
};
} else if (geomType === GeoGeomTypesEnum.POLYGON) {
geojsonStyles = {
READY: createFeaturePolygonStyle(mapTheme.palette.entityStatusColors.ready, 0.2),
OPENED_IN_ODK: createFeaturePolygonStyle(mapTheme.palette.entityStatusColors.opened_in_odk, 0.2),
SURVEY_SUBMITTED: createFeaturePolygonStyle(mapTheme.palette.entityStatusColors.survey_submitted),
MARKED_BAD: createFeaturePolygonStyle(mapTheme.palette.entityStatusColors.marked_bad),
VALIDATED: createFeaturePolygonStyle(mapTheme.palette.entityStatusColors.validated),
};
} else if (geomType === GeoGeomTypesEnum.LINESTRING) {
console.log('linestring style not set');
}

const borderStrokeColor = 'rgb(0,0,0,0.5)';

const strokeStyle = new Stroke({
color: borderStrokeColor,
width: 1,
opacity: 0.2,
});

const geojsonStyles = {
READY: new Style({
stroke: strokeStyle,
fill: new Fill({
color: mapTheme.palette.entityStatusColors.ready,
}),
}),
OPENED_IN_ODK: new Style({
stroke: strokeStyle,
fill: new Fill({
color: mapTheme.palette.entityStatusColors.opened_in_odk,
}),
}),
SURVEY_SUBMITTED: new Style({
stroke: new Stroke({
color: borderStrokeColor,
width: 1,
}),
fill: new Fill({
color: mapTheme.palette.entityStatusColors.survey_submitted,
}),
}),
MARKED_BAD: new Style({
stroke: new Stroke({
color: borderStrokeColor,
width: 1,
}),
fill: new Fill({
color: mapTheme.palette.entityStatusColors.marked_bad,
}),
}),
VALIDATED: new Style({
stroke: new Stroke({
color: borderStrokeColor,
width: 1,
}),
fill: new Fill({
color: mapTheme.palette.entityStatusColors.validated,
}),
}),
};
return geojsonStyles[status];
return geojsonStyles[mappingStatus];
};

export default getTaskStatusStyle;
11 changes: 9 additions & 2 deletions src/frontend/src/views/ProjectDetailsV2.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
import '../../node_modules/ol/ol.css';
import '../styles/home.scss';
import { Style, Stroke } from 'ol/style';
import WindowDimension from '@/hooks/WindowDimension';
import ActivitiesPanel from '@/components/ProjectDetailsV2/ActivitiesPanel';
import { ProjectById, GetEntityStatusList, GetGeometryLog, SyncTaskState } from '@/api/Project';
Expand Down Expand Up @@ -35,8 +36,9 @@ import Comments from '@/components/ProjectDetailsV2/Comments';
import { Geolocation } from '@/utilfunctions/Geolocation';
import Instructions from '@/components/ProjectDetailsV2/Instructions';
import useDocumentTitle from '@/utilfunctions/useDocumentTitle';
import { Style, Stroke } from 'ol/style';
import MapStyles from '@/hooks/MapStyles';
import { EntityOsmMap } from '@/store/types/IProject';
import { entity_state } from '@/types/enums';

const ProjectDetailsV2 = () => {
useDocumentTitle('Project Details');
Expand Down Expand Up @@ -517,7 +519,12 @@ const ProjectDetailsV2 = () => {
fgbUrl={dataExtractUrl}
fgbExtent={dataExtractExtent}
getTaskStatusStyle={(feature) => {
return getFeatureStatusStyle(feature?.getProperties()?.osm_id, mapTheme, entityOsmMap);
const geomType = feature.getGeometry().getType();
const entity = entityOsmMap?.find(
(entity) => entity?.osm_id === feature?.getProperties()?.osm_id,
) as EntityOsmMap;
const status = entity_state[entity?.status];
return getFeatureStatusStyle(geomType, mapTheme, status);
}}
viewProperties={{
size: map?.getSize(),
Expand Down
Binary file added src/mapper/src/assets/images/map-pin-blue.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/mapper/src/assets/images/map-pin-green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/mapper/src/assets/images/map-pin-grey.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/mapper/src/assets/images/map-pin-red.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/mapper/src/assets/images/map-pin-yellow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading