Skip to content

Commit

Permalink
Merge pull request #32 from naxa-developers/feat/frontend-req-tokha
Browse files Browse the repository at this point in the history
Feat/frontend req tokha
  • Loading branch information
Sujanadh authored Mar 4, 2025
2 parents 9f67a6d + 1e78754 commit 53b8f09
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const FeatureSelectionPopup = ({ featureProperties, taskId }: FeatureSelectionPo
const taskModalStatus = useAppSelector((state) => state.project.taskModalStatus);
const entityOsmMap = useAppSelector((state) => state.project.entityOsmMap);
const projectId = params.id || '';
const entity = entityOsmMap.find((x) => x.osm_id === featureProperties?.osm_id);
const entity = entityOsmMap.find(
(x) => x.osm_id === featureProperties?.osm_id || x.id === featureProperties?.entity_id,
);
const submissionIds = entity?.submission_ids ? entity?.submission_ids?.split(',') : [];

return (
Expand All @@ -36,7 +38,9 @@ const FeatureSelectionPopup = ({ featureProperties, taskId }: FeatureSelectionPo
} fmtm-rounded-t-2xl md:fmtm-rounded-tr-none md:fmtm-rounded-l-2xl`}
>
<div className="fmtm-flex fmtm-justify-between fmtm-items-center fmtm-gap-2 fmtm-px-3 sm:fmtm-px-5 fmtm-py-2">
<h4 className="fmtm-text-lg fmtm-font-bold">Feature: {featureProperties?.osm_id}</h4>
<h4 className="fmtm-text-lg fmtm-font-bold">
{entity?.osm_id ? `Feature: ${entity.osm_id}` : `Entity: ${entity?.id}`}
</h4>
<div title="Close">
<AssetModules.CloseIcon
style={{ width: '20px' }}
Expand All @@ -45,26 +49,28 @@ const FeatureSelectionPopup = ({ featureProperties, taskId }: FeatureSelectionPo
/>
</div>
</div>
<div className="fmtm-h-fit fmtm-px-2 sm:fmtm-px-5 fmtm-py-2 fmtm-border-t">
<div className="fmtm-flex fmtm-flex-col fmtm-gap-1 fmtm-mt-1">
<p>
<span>Tags: </span>
<span className="fmtm-overflow-hidden fmtm-line-clamp-2">{featureProperties?.tags}</span>
</p>
<p>
<span>Timestamp: </span>
<span>{featureProperties?.timestamp}</span>
</p>
<p>
<span>Changeset: </span>
<span>{featureProperties?.changeset}</span>
</p>
<p>
<span>Version: </span>
<span>{featureProperties?.version}</span>
</p>
{featureProperties?.changeset && (
<div className="fmtm-h-fit fmtm-px-2 sm:fmtm-px-5 fmtm-py-2 fmtm-border-t">
<div className="fmtm-flex fmtm-flex-col fmtm-gap-1 fmtm-mt-1">
<p>
<span>Tags: </span>
<span className="fmtm-overflow-hidden fmtm-line-clamp-2">{featureProperties?.tags}</span>
</p>
<p>
<span>Timestamp: </span>
<span>{featureProperties?.timestamp}</span>
</p>
<p>
<span>Changeset: </span>
<span>{featureProperties?.changeset}</span>
</p>
<p>
<span>Version: </span>
<span>{featureProperties?.version}</span>
</p>
</div>
</div>
</div>
)}
{!submissionIds ||
(submissionIds?.length !== 0 && (
<div className="fmtm-px-2 sm:fmtm-px-5 fmtm-py-3 fmtm-border-t fmtm-flex fmtm-flex-col fmtm-gap-3">
Expand Down
11 changes: 6 additions & 5 deletions src/frontend/src/store/types/ITask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ type downloadSubmissionLoadingTypes = {
};

export type TaskFeatureSelectionProperties = {
osm_id: number;
tags: string;
timestamp: string;
version: number;
changeset: number;
osm_id?: number;
tags?: string;
timestamp?: string;
version?: number;
changeset?: number;
entity_id?: string;
};
13 changes: 6 additions & 7 deletions src/frontend/src/utilfunctions/getTaskStatusStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { Fill, Icon, Stroke, Style } from 'ol/style';
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 { Text } from 'ol/style';

function createPolygonStyle(fillColor: string, strokeColor: string) {
return new Style({
Expand Down Expand Up @@ -106,11 +105,7 @@ 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;

let status = entity_state[entity?.status];

export const getFeatureStatusStyle = (mapTheme: Record<string, any>, status: string, osm_id: number) => {
const borderStrokeColor = 'rgb(0,0,0,0.5)';

const strokeStyle = new Stroke({
Expand All @@ -125,6 +120,10 @@ export const getFeatureStatusStyle = (osmId: string, mapTheme: Record<string, an
fill: new Fill({
color: mapTheme.palette.mapFeatureColors.ready_rgb,
}),
text: new Text({
text: osm_id?.toString(),
font: '10px Arial',
}),
}),
OPENED_IN_ODK: new Style({
stroke: strokeStyle,
Expand Down
29 changes: 28 additions & 1 deletion src/frontend/src/views/ProjectDetailsV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ 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 '@/models/project/projectModel';
import { entity_state } from '@/types/enums';

const ProjectDetailsV2 = () => {
useDocumentTitle('Project Details');
Expand Down Expand Up @@ -71,6 +73,7 @@ const ProjectDetailsV2 = () => {
const entityOsmMap = useAppSelector((state) => state?.project?.entityOsmMap);
const entityOsmMapLoading = useAppSelector((state) => state?.project?.entityOsmMapLoading);
const badGeomFeatureCollection = useAppSelector((state) => state?.project?.badGeomFeatureCollection);
const newGeomFeatureCollection = useAppSelector((state) => state?.project?.newGeomFeatureCollection);
const getGeomLogLoading = useAppSelector((state) => state?.project?.getGeomLogLoading);
const syncTaskStateLoading = useAppSelector((state) => state?.project?.syncTaskStateLoading);

Expand Down Expand Up @@ -512,12 +515,36 @@ const ProjectDetailsV2 = () => {
zIndex={5}
style=""
/>
<VectorLayer
geojson={newGeomFeatureCollection}
viewProperties={{
size: map?.getSize(),
padding: [50, 50, 50, 50],
constrainResolution: true,
duration: 2000,
}}
layerProperties={{ name: 'new-entities' }}
zIndex={5}
style=""
mapOnClick={projectClickOnTaskFeature}
getTaskStatusStyle={(feature) => {
const entity = entityOsmMap?.find(
(entity) => entity?.id === feature?.getProperties()?.entity_id,
) as EntityOsmMap;
const status = entity_state[entity?.status];
return getFeatureStatusStyle(mapTheme, status, entity?.osm_id);
}}
/>
{dataExtractUrl && isValidUrl(dataExtractUrl) && dataExtractExtent && selectedTask && (
<VectorLayer
fgbUrl={dataExtractUrl}
fgbExtent={dataExtractExtent}
getTaskStatusStyle={(feature) => {
return getFeatureStatusStyle(feature?.getProperties()?.osm_id, mapTheme, entityOsmMap);
const osmId = feature?.getProperties()?.osm_id;
const entity = entityOsmMap?.find((entity) => entity?.osm_id === osmId) as EntityOsmMap;

const status = entity_state[entity?.status];
return getFeatureStatusStyle(mapTheme, status, entity?.osm_id);
}}
viewProperties={{
size: map?.getSize(),
Expand Down
126 changes: 105 additions & 21 deletions src/mapper/src/lib/components/map/main.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@
const clickedTaskFeature = map?.queryRenderedFeatures(e.point, {
layers: ['task-fill-layer'],
});
const clickedNewEntityFeature = map?.queryRenderedFeatures(e.point, {
layers: ['new-entity-polygon-layer'],
});
// if clicked point contains entity then set it's osm id else set null to store
if (clickedEntityFeature && clickedEntityFeature?.length > 0) {
const entityCentroid = centroid(clickedEntityFeature[0].geometry);
Expand All @@ -164,6 +167,14 @@
entityId: clickedEntityId,
coordinate: entityCentroid?.geometry?.coordinates,
});
} else if (clickedNewEntityFeature && clickedNewEntityFeature?.length > 0) {
const entityCentroid = centroid(clickedNewEntityFeature[0].geometry);
const clickedEntityId = clickedNewEntityFeature[0]?.properties?.entity_id;
entitiesStore.setSelectedEntity(clickedEntityId);
entitiesStore.setSelectedEntityCoordinate({
entityId: clickedEntityId,
coordinate: entityCentroid?.geometry?.coordinates,
});
} else {
entitiesStore.setSelectedEntity(null);
entitiesStore.setSelectedEntityCoordinate(null);
Expand All @@ -172,15 +183,18 @@
// if clicked point contains task layer
if (clickedTaskFeature && clickedTaskFeature?.length > 0) {
taskAreaClicked = true;
const clickedTaskId = clickedTaskFeature[0]?.properties?.fid;
taskStore.setSelectedTaskId(clickedTaskId, clickedTaskFeature[0]?.properties?.task_index);
const clickedTaskId = clickedTaskFeature?.[0]?.properties?.fid;
taskStore.setSelectedTaskId(clickedTaskId, clickedTaskFeature?.[0]?.properties?.task_index);
if (+(projectSetupStepStore.projectSetupStep || 0) === projectSetupStepEnum['task_selection']) {
localStorage.setItem(`project-${projectId}-setup`, projectSetupStepEnum['complete_setup']);
projectSetupStepStore.setProjectSetupStep(projectSetupStepEnum['complete_setup']);
}
}
if (clickedEntityFeature && clickedEntityFeature?.length > 0) {
if (
(clickedEntityFeature && clickedEntityFeature?.length > 0) ||
(clickedNewEntityFeature && clickedNewEntityFeature?.length > 0)
) {
toggleActionModal('entity-modal');
} else if (clickedTaskFeature && clickedTaskFeature?.length > 0) {
toggleActionModal('task-modal');
Expand Down Expand Up @@ -267,21 +281,42 @@
}
});
function addStatusToGeojsonProperty(geojsonData: FeatureCollection): FeatureCollection {
return {
...geojsonData,
features: geojsonData.features.map((feature) => {
const entity = entitiesStore.entitiesStatusList.find((entity) => entity.osmid === feature?.properties?.osm_id);
return {
...feature,
properties: {
...feature.properties,
status: entity?.status,
entity_id: entity?.entity_id,
},
};
}),
};
function addStatusToGeojsonProperty(geojsonData: FeatureCollection, entityType: '' | 'new'): FeatureCollection {
if (entityType === 'new') {
return {
...geojsonData,
features: geojsonData.features.map((feature) => {
const entity = entitiesStore.entitiesStatusList.find(
(entity) => entity.entity_id === feature?.properties?.entity_id,
);
return {
...feature,
properties: {
...feature.properties,
status: entity?.status,
entity_id: entity?.entity_id,
},
};
}),
};
} else {
return {
...geojsonData,
features: geojsonData.features.map((feature) => {
const entity = entitiesStore.entitiesStatusList.find(
(entity) => entity.osmid === feature?.properties?.osm_id,
);
return {
...feature,
properties: {
...feature.properties,
status: entity?.status,
entity_id: entity?.entity_id,
},
};
}),
};
}
}
function zoomToProject() {
Expand Down Expand Up @@ -487,7 +522,7 @@
extent={taskStore.selectedTaskGeom}
extractGeomCols={true}
promoteId="id"
processGeojson={(geojsonData) => addStatusToGeojsonProperty(geojsonData)}
processGeojson={(geojsonData) => addStatusToGeojsonProperty(geojsonData, '')}
geojsonUpdateDependency={entitiesStore.entitiesStatusList}
>
<SymbolLayer
Expand Down Expand Up @@ -573,7 +608,56 @@
/>
</GeoJSON>
{/if}

{#if showEntityLayer}
<GeoJSON id="new-geoms" data={addStatusToGeojsonProperty(entitiesStore.newGeomList, 'new')}>
<FillLayer
id="new-entity-polygon-layer"
paint={{
'fill-opacity': ['match', ['get', 'status'], 'MARKED_BAD', 0, 0.6],
'fill-color': [
'match',
['get', 'status'],
'READY',
'#9c9a9a',
'OPENED_IN_ODK',
'#fae15f',
'SURVEY_SUBMITTED',
'#71bf86',
'VALIDATED',
'#71bf86',
'MARKED_BAD',
'#fa1100',
'#c5fbf5',
],
'fill-outline-color': [
'match',
['get', 'status'],
'READY',
'#000000',
'OPENED_IN_ODK',
'#ffd603',
'SURVEY_SUBMITTED',
'#32a852',
'MARKED_BAD',
'#fa1100',
'#c5fbf5',
],
}}
beforeLayerType="symbol"
manageHoverState
/>
<LineLayer
layout={{ 'line-cap': 'round', 'line-join': 'round' }}
paint={{
'line-color': '#fa1100',
'line-width': ['case', ['==', ['get', 'osm_id'], entitiesStore.selectedEntity || ''], 1, 0],
'line-opacity': ['case', ['==', ['get', 'osm_id'], entitiesStore.selectedEntity || ''], 1, 0.35],
}}
beforeLayerType="symbol"
manageHoverState
/>
</GeoJSON>
{/if}
<!-- pulse effect layer representing rejected entities -->

<!-- Offline pmtiles, if present (alternative approach, not baselayer) -->
Expand Down Expand Up @@ -624,7 +708,7 @@
<LayerSwitcher
{map}
styles={allBaseLayers}
sourcesIdToReAdd={['tasks', 'entities', 'geolocation', 'maplibre-gl-directions', 'bad-geoms']}
sourcesIdToReAdd={['tasks', 'entities', 'geolocation', 'maplibre-gl-directions', 'bad-geoms', 'new-geoms']}
selectedStyleName={selectedBaselayer}
{selectedStyleUrl}
setSelectedStyleUrl={(style) => (selectedStyleUrl = style)}
Expand Down
8 changes: 3 additions & 5 deletions src/mapper/src/lib/odk/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@ import { geojsonGeomToJavarosa } from '$lib/odk/javarosa';

const alertStore = getAlertStore();

export function openOdkCollectNewFeature(xFormId: string, geom: GeoJSONGeometry, task_id: number | null) {
if (!xFormId || !geom) {
export function openOdkCollectNewFeature(xFormId: string, entityId: string) {
if (!xFormId || !entityId) {
return;
}

const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);

const javarosaGeom = geojsonGeomToJavarosa(geom);

if (isMobile) {
// TODO we need to update the form to support task_id=${}&
document.location.href = `odkcollect://form/${xFormId}?new_feature=${javarosaGeom}&task_id=${task_id}`;
document.location.href = `odkcollect://form/${xFormId}?feature=${entityId}`;
} else {
alertStore.setAlert({
variant: 'warning',
Expand Down
Loading

0 comments on commit 53b8f09

Please sign in to comment.