Skip to content

Commit

Permalink
feat(frontend): all uploaded submission features/geometries on submis…
Browse files Browse the repository at this point in the history
…sion instance page (#1931)

* fix(main): shift osmStyle to baselayers constant

* fix(layer-switcher): user added layer vanish issue on switching to initial selected layer issue solve

* fix(clusterLayer): refactor clusterLayer component

* fix(submissionsTable): use link instead of useNavigate for redirection to submission instance page

* fix(ISubmissions): geojson type add

* feat(extractGeojsonFromObject): function to extract geojson from objects

* fix(submissionDetials): update logic to display feature on submissionmap

* fix(submissionInstanceMap): add clusterLayer to display feature points

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
NSUWAL123 and pre-commit-ci[bot] authored Dec 2, 2024
1 parent e545136 commit 0b6e8b6
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import Stroke from 'ol/style/Stroke';
import Style from 'ol/style/Style';
import Fill from 'ol/style/Fill';
import { Cluster, OSM as OSMSource } from 'ol/source';
import { Text, Circle, Icon } from 'ol/style';
import { Text, Circle } from 'ol/style';
import VectorSource from 'ol/source/Vector';
import SelectCluster from 'ol-ext/interaction/SelectCluster';
import { hexToRgba } from '@/components/MapComponent/OpenLayersComponent/helpers/styleUtils';
import MarkerIcon from '@/assets/images/red_marker.png';

function setAsyncStyle(style, feature, getIndividualStyle) {
const styleCache = {};
Expand Down Expand Up @@ -186,23 +185,7 @@ const ClusterLayer = ({
if (isExpandedFeature) {
const feature = clusterMember.getProperties().features[0];
const featureProperty = feature?.getProperties();
console.log(featureProperty, 'featureProperty');
console.log(feature, 'isExpandedFeature');
const style = new Style({
image: new Icon({
src: MarkerIcon,
scale: 0.06,
}),
text: new Text({
text: featureProperty?.project_id,
fill: new Fill({
color: 'black',
}),
offsetY: 25,
font: '15px Times New Roman',
}),
});
fillColor = '#96bfff';
const style = getIndividualStyle(featureProperty);
return style;
} else {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Button from '@/components/common/Button';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/common/Dropdown';
import { ConvertXMLToJOSM, getDownloadProjectSubmission } from '@/api/task';
import { Modal } from '@/components/common/Modal';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import filterParams from '@/utilfunctions/filterParams';
import UpdateReviewStatusModal from '@/components/ProjectSubmissions/UpdateReviewStatusModal';
import { useAppSelector } from '@/types/reduxTypes';
Expand Down Expand Up @@ -467,14 +467,9 @@ const SubmissionsTable = ({ toggleView }) => {
const taskUid = taskList?.find((task) => task?.id == row?.task_id)?.id;
return (
<div className="fmtm-w-[5rem] fmtm-overflow-hidden fmtm-truncate fmtm-text-center">
<AssetModules.VisibilityOutlinedIcon
className="fmtm-text-[#545454] hover:fmtm-text-primaryRed"
onClick={() => {
navigate(
`/project-submissions/${projectId}/tasks/${taskUid}/submission/${row?.meta?.instanceID}`,
);
}}
/>{' '}
<Link to={`/project-submissions/${projectId}/tasks/${taskUid}/submission/${row?.meta?.instanceID}`}>
<AssetModules.VisibilityOutlinedIcon className="fmtm-text-[#545454] hover:fmtm-text-primaryRed" />
</Link>
<span className="fmtm-text-primaryRed fmtm-border-[1px] fmtm-border-primaryRed fmtm-mx-1"></span>{' '}
<AssetModules.CheckOutlinedIcon
className="fmtm-text-[#545454] hover:fmtm-text-primaryRed"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,32 @@ import LayerSwitcherControl from '@/components/MapComponent/OpenLayersComponent/
import { VectorLayer } from '@/components/MapComponent/OpenLayersComponent/Layers';
import { defaultStyles } from '@/components/MapComponent/OpenLayersComponent/helpers/styleUtils';
import LayerSwitchMenu from '../MapComponent/OpenLayersComponent/LayerSwitcher/LayerSwitchMenu';
import { ClusterLayer } from '@/components/MapComponent/OpenLayersComponent/Layers';
import { Style, Circle } from 'ol/style';
import { Stroke } from 'ol/style';
import { hexToRgba } from '@/components/MapComponent/OpenLayersComponent/helpers/styleUtils';
import { Fill } from 'ol/style';
import { geojsonType } from '@/store/types/ISubmissions';

type submissionInstanceMapPropType = {
featureGeojson: Record<string, any>;
featureGeojson: { vectorLayerGeojson: geojsonType; clusterLayerGeojson: geojsonType };
};

const getIndividualStyle = (featureProperty) => {
const style = new Style({
image: new Circle({
radius: 10,
stroke: new Stroke({
color: hexToRgba('#D73F37'),
width: 2,
}),
fill: new Fill({
color: hexToRgba('#eb9f9f'),
width: 40,
}),
}),
});
return style;
};

const SubmissionInstanceMap = ({ featureGeojson }: submissionInstanceMapPropType) => {
Expand Down Expand Up @@ -39,9 +62,9 @@ const SubmissionInstanceMap = ({ featureGeojson }: submissionInstanceMapPropType
<LayerSwitchMenu map={map} />
</div>
<LayerSwitcherControl visible={'osm'} />
{featureGeojson?.type && (
{featureGeojson?.vectorLayerGeojson?.type && (
<VectorLayer
geojson={featureGeojson}
geojson={featureGeojson?.vectorLayerGeojson}
viewProperties={{
size: map?.getSize(),
padding: [50, 50, 50, 50],
Expand All @@ -54,6 +77,22 @@ const SubmissionInstanceMap = ({ featureGeojson }: submissionInstanceMapPropType
style={{ ...defaultStyles, lineColor: '#D73F37', lineThickness: 2, circleRadius: 10, fillColor: '#D73F37' }}
/>
)}
{featureGeojson?.clusterLayerGeojson?.type && (
<ClusterLayer
map={map}
source={featureGeojson?.clusterLayerGeojson}
zIndex={100}
visibleOnMap={true}
style={{
...defaultStyles,
background_color: '#D73F37',
color: '#eb9f9f',
opacity: 90,
}}
mapOnClick={() => {}}
getIndividualStyle={getIndividualStyle}
/>
)}
</MapComponent>
</div>
);
Expand Down
14 changes: 14 additions & 0 deletions src/frontend/src/store/types/ISubmissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,17 @@ type mappedVsValidatedTaskType = {
validated: number;
label: string;
};

export type featureType = {
type: 'Feature';
geometry: Partial<{
type: string;
coordinates: any[];
}>;
properties: Record<string, any>;
};

export type geojsonType = {
type: 'FeatureCollection';
features: featureType[];
};
106 changes: 106 additions & 0 deletions src/frontend/src/utilfunctions/extractGeojsonFromObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { featureType, geojsonType } from '@/store/types/ISubmissions';

// convert JavaRosa string to a GeoJson
const convertCoordinateStringToFeature = (coordinateString: string) => {
let feature: featureType = {
type: 'Feature',
geometry: {},
properties: {},
};

// if feature is LineString in JavaRosa format it contains string of array separated by ';'
if (coordinateString?.includes(';')) {
let coordinates = coordinateString?.split(';')?.map((coord) => {
let coordinate = coord
.trim()
.split(' ')
.slice(0, 2)
.map((value: string) => {
return parseFloat(value);
});
return [coordinate[1], coordinate[0]];
});
feature = { ...feature, geometry: { type: 'LineString', coordinates: coordinates } };
} else {
// if feature is Point in JavaRosa format it contains string of array
const splittedCoord = coordinateString?.split(' ');
let coordinates = [+splittedCoord[1], +splittedCoord[0]];
feature = { ...feature, geometry: { type: 'Point', coordinates: coordinates } };
}
return feature;
};

export function extractGeojsonFromObject(data: Record<string, any>) {
/*{ all feature of geometry points are stored in clusterLayerGeojson
reason: more than one gps point can be on the exact same coordinate, so clustering and point expand on cluster click is important for visualization
}*/
let clusterLayerGeojson: geojsonType = {
type: 'FeatureCollection',
features: [],
};

// feature other than of geometry type points are stored in vectorLayerGeojson
let vectorLayerGeojson: any = {
type: 'FeatureCollection',
features: [],
};

// function called recursively until the object is traversed
function traverse(data: Record<string, any>) {
typeof data === 'object' &&
Object.entries(data)?.map(([key, value]) => {
if (value && typeof value === 'object' && Object.keys(value).includes('coordinates')) {
const feature = value as featureType['geometry'];
if (feature?.type === 'Point') {
clusterLayerGeojson = {
...clusterLayerGeojson,
features: [...clusterLayerGeojson.features, { type: 'Feature', geometry: feature, properties: {} }],
};
} else {
vectorLayerGeojson = {
...vectorLayerGeojson,
features: [...vectorLayerGeojson.features, { type: 'Feature', geometry: value, properties: {} }],
};
}
}
// if value is object then traverse
else if (value && typeof value === 'object') {
traverse(value);
}
// check if value is JavaRosa LineString
// the JavaRosa LineString is separated by ';'
else if (
value &&
typeof value === 'string' &&
value?.includes(';') &&
value
?.split(';')?.[0]
?.split(' ')
?.every((item) => !isNaN(+item))
) {
const convertedFeature = convertCoordinateStringToFeature(value);
vectorLayerGeojson = {
...vectorLayerGeojson,
features: [...vectorLayerGeojson.features, convertedFeature],
};
}
// check if value is JavaRosa Point
// point contains 4 floating point data
else if (
value &&
typeof value === 'string' &&
value?.split(' ')?.every((item) => !isNaN(+item)) &&
value?.split(' ')?.length === 4
) {
const convertedFeature = convertCoordinateStringToFeature(value);
clusterLayerGeojson = {
...clusterLayerGeojson,
features: [...clusterLayerGeojson.features, convertedFeature],
};
}
});
}

traverse(data);
return { clusterLayerGeojson, vectorLayerGeojson };
}
58 changes: 2 additions & 56 deletions src/frontend/src/views/SubmissionDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Accordion from '@/components/common/Accordion';
import { GetProjectComments } from '@/api/Project';
import SubmissionComments from '@/components/SubmissionInstance/SubmissionComments';
import ImageSlider from '@/components/common/ImageSlider';
import { extractGeojsonFromObject } from '@/utilfunctions/extractGeojsonFromObject';

const renderValue = (value: any, key: string = '') => {
if (key === 'start' || key === 'end') {
Expand Down Expand Up @@ -130,49 +131,6 @@ const SubmissionDetails = () => {

const filteredData = restSubmissionDetails ? removeNullValues(restSubmissionDetails) : {};

const coordinatesArray: [number, number][] = restSubmissionDetails?.xlocation?.split(';').map(function (
coord: string,
) {
let coordinate = coord
.trim()
.split(' ')
.slice(0, 2)
.map((value: string) => {
return parseFloat(value);
});
return [coordinate[1], coordinate[0]];
});

const geojsonFeature: Record<string, any> = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: coordinatesArray,
},
properties: {},
},
],
};

const pointFeature = {
type: 'Feature',
geometry: {
...restSubmissionDetails?.point,
},
properties: {},
};

const newFeaturePoint = {
type: 'Feature',
geometry: {
...restSubmissionDetails?.new_feature,
},
properties: {},
};

return (
<>
<UpdateReviewStatusModal />
Expand Down Expand Up @@ -256,19 +214,7 @@ const SubmissionDetails = () => {
</div>
<div className="fmtm-flex fmtm-flex-grow fmtm-justify-center fmtm-mt-10 md:fmtm-mt-0">
<div className="fmtm-w-full fmtm-h-[20rem] md:fmtm-h-full fmtm-rounded-lg fmtm-overflow-hidden">
<SubmissionInstanceMap
featureGeojson={
submissionDetailsLoading
? {}
: restSubmissionDetails?.new_feature
? newFeaturePoint
: coordinatesArray
? geojsonFeature
: restSubmissionDetails?.point
? pointFeature
: {}
}
/>
<SubmissionInstanceMap featureGeojson={extractGeojsonFromObject(restSubmissionDetails)} />
</div>
</div>
</div>
Expand Down

0 comments on commit 0b6e8b6

Please sign in to comment.