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

V2 Boundary Updates #2966

Merged
merged 13 commits into from
Nov 8, 2023
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
11 changes: 9 additions & 2 deletions api/src/paths/admin-defined-shapes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ GET.apiDoc = {

POST.apiDoc = {
description: 'Creates new Administratively-defined shapes from KML/KMZ data',
security: SECURITY_ON
? [
{
Bearer: ALL_ROLES
}
]
: [],
requestBody: {
description: 'Uploaded KML/KMZ file',
content: {
Expand Down Expand Up @@ -218,9 +225,9 @@ function getAdministrativelyDefinedShapes(): RequestHandler {
* @returns {RequestHandler}
*/
function uploadShape(): RequestHandler {
return async (req, res) => {
return async (req: InvasivesRequest, res) => {
const user_id = req.authContext.user.user_id;
const data = {...req.body};
const user_id = data.user_id;
const title = data.title;
let geoJSON: FeatureCollection;

Expand Down
6 changes: 3 additions & 3 deletions api/src/paths/v2/activities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ function sanitizeActivityFilterObject(filterObject: any, req: any) {
break;
case 'spatialFilterDrawn':
if (filter.filter) {
clientFilterGeometries.push(filter.filter?.geometry);
clientFilterGeometries.push(filter?.geojson?.geometry);
}
break;
case 'spatialFilterUploaded':
Expand Down Expand Up @@ -360,15 +360,15 @@ CurrentNegativeObservations AS (
clientFilterGeometries AS (
SELECT
unnest(array[${filterObject.clientFilterGeometries
.map((geometry) => `st_setsrid(st_geomfromgeojson(${geometry?.geometry}, 4326)`)
.map((geometry) => `st_setsrid(st_geomfromgeojson('${JSON.stringify(geometry)}'), 4326)`)
.join(',')}]) AS geojson
),

clientFilterGeometriesIntersecting as (

select a.activity_incoming_data_id
from not_deleted_activities a
left join clientFilterGeometries on st_intersects((a.geog::geometry), geojson)
inner join clientFilterGeometries on st_intersects((a.geog::geometry), geojson)

),
clientFilterGeometriesIntersectingAll as (
Expand Down
2 changes: 2 additions & 0 deletions appv2/src/UI/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { LayerPickerBasic } from './Map/LayerPickerBasic';
import NewRecordDialog from './Map/Buttons/NewRecordDialog';
import AccessRequestPage from './Overlay/AccessRequest/AccessRequestPage';
import CustomizeLayerMenu from './Map/Buttons/CustomizeLayerDialog';
import { DrawCustomLayer } from './Map/DrawCustomLayer';

const AppUrlListener: React.FC<any> = () => {
useEffect(() => {
Expand Down Expand Up @@ -149,6 +150,7 @@ const MapMemo = React.memo((props: any) => {
return (
<Map className="Map">
<ButtonContainer></ButtonContainer>
<DrawCustomLayer/>
<Route path="/Records/Activity:id" render={(props) => <ActivityGeo />} />
<Route exact={false} path="/Records" render={(props) => <OnHoverActivity />} />
<MapCenterSetter />
Expand Down
57 changes: 46 additions & 11 deletions appv2/src/UI/Map/Buttons/CustomizeLayerDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import { set } from 'lodash';
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { TOGGLE_CUSTOMIZE_LAYERS } from 'state/actions';
import {
DRAW_CUSTOM_LAYER,
REMOVE_CLIENT_BOUNDARY,
REMOVE_SERVER_BOUNDARY,
TOGGLE_CUSTOMIZE_LAYERS
} from 'state/actions';
import KMLShapesUpload from './KMLShapesUpload';

const useStyles = makeStyles((theme: Theme) => ({
Expand Down Expand Up @@ -59,9 +64,14 @@ const CustomizeLayerMenu = (props) => {
const [newLayerName, setNewLayerName] = useState('');
const [layerToDelete, setLayerToDelete] = useState(null);

const clientBoundaries = useSelector((state: any) => state.Map.clientBoundaries).map((boundary) => {
return { ...boundary, type: 'Client' };
});
const serverBoundaries = useSelector((state: any) => state.Map.serverBoundaries).map((boundary) => {
return { ...boundary, type: 'Server' };
});

// two of each type
const customLayers = [{ id: 1, type: 'Drawn', label: 'banana', data: '' }, {id: 2, type: 'Drawn', label: 'apple', data: ''}, {id: 3, type: 'WMS', label: 'orange', data: ''}, {id: 4, type: 'WMS', label: 'grape', data: ''}, {id: 5, type: 'WFS', label: 'pear', data: ''}, {id: 6, type: 'WFS', label: 'peach', data: ''}];
const customLayers = [...clientBoundaries, ...serverBoundaries];

const cleanup = () => {
setSubMenuType('Init');
Expand Down Expand Up @@ -108,12 +118,12 @@ const CustomizeLayerMenu = (props) => {
<InputLabel>Remove Layer</InputLabel>
<Select
className={classes.select}
value={optionVal}
value={layerToDelete}
onChange={(e) => setLayerToDelete(e.target.value)}
label="Choose Layer to remove">
{customLayers.map((option) => (
<MenuItem key={Math.random()} value={option.id}>
{option.label + ' - ' + option.type}
<MenuItem key={'customlayermenuitem' + option.id} value={option.id}>
{option.title + ' - ' + option.type}
</MenuItem>
))}
</Select>
Expand Down Expand Up @@ -148,10 +158,17 @@ const CustomizeLayerMenu = (props) => {
<>
<Button
onClick={() => {
if (optionVal === 'Upload KML/KMZ') {
setSubMenuType('Upload');
} else {
cleanup();
switch (optionVal) {
case 'Upload KML/KMZ':
setSubMenuType('Upload');
break;
case 'Draw':
dispatch({ type: DRAW_CUSTOM_LAYER, payload: { name: newLayerName } });
dispatch({ type: TOGGLE_CUSTOMIZE_LAYERS });
cleanup();
break;
default:
cleanup();
}
}}>
Create
Expand All @@ -166,7 +183,25 @@ const CustomizeLayerMenu = (props) => {
),
Remove: (
<>
<Button disabled={layerToDelete === null} onClick={() => {}}>
<Button
disabled={layerToDelete === null}
onClick={() => {
const type = customLayers.filter((layer) => layer.id === layerToDelete)?.[0]?.type;

switch (type) {
case 'Client':
dispatch({ type: REMOVE_CLIENT_BOUNDARY, payload: { id: layerToDelete } });
dispatch({ type: TOGGLE_CUSTOMIZE_LAYERS });
cleanup();
break;
case 'Server':
dispatch({ type: REMOVE_SERVER_BOUNDARY, payload: { id: layerToDelete } });
dispatch({ type: TOGGLE_CUSTOMIZE_LAYERS });
cleanup();
break;
}
cleanup();
}}>
Remove
</Button>
<Button
Expand Down
20 changes: 11 additions & 9 deletions appv2/src/UI/Map/Buttons/KMLShapesUpload.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useState, useContext } from 'react';
import React, { useState, useContext, useEffect } from 'react';
import { Box, Button, Theme, Typography } from '@mui/material';
import { DropzoneDialog } from 'mui-file-dropzone';
import makeStyles from '@mui/styles/makeStyles';
import { useInvasivesApi } from 'hooks/useInvasivesApi';

export interface IShapeUploadRequest {
data: string;
Expand All @@ -26,18 +27,23 @@ export const KMLShapesUpload: React.FC<any> = (props) => {
const classes = useStyles();
const [uploadRequests, setUploadRequests] = useState([]);
const [dialogOpen, setDialogOpen] = React.useState(false);
//const api = useInvasivesApi();
const api = useInvasivesApi();
const [resultMessage, setResultMessage] = useState('');
const [uploadClicked, setUploadClicked] = useState(false);

useEffect(() => {
if(uploadRequests.length > 0)
doUpload().then(() => {
props.whenDone();
console.log('done');
});
}, [uploadRequests]);

const doUpload = async () => {
let response;
try {
for (let i = 0; i < uploadRequests.length; i++) {
console.log();
//response = await api.postAdminUploadShape(uploadRequests[i]);
console.log(response);
response = await api.postAdminUploadShape(uploadRequests[i]);
if (response.code !== 201) {
throw new Error(response.message);
}
Expand Down Expand Up @@ -113,10 +119,6 @@ export const KMLShapesUpload: React.FC<any> = (props) => {
open={props.open}
onSave={(files: any) => {
acceptFiles(files);
doUpload().then(() => {
props.whenDone();
console.log('done')
});
}}
showPreviews={true}
previewText={'File will be uploaded to InvasivesBC as ' + props.title}
Expand Down
41 changes: 41 additions & 0 deletions appv2/src/UI/Map/DrawCustomLayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as L from 'leaflet';
import 'leaflet-draw';
import 'leaflet-draw/dist/leaflet.draw.css';
// Offline dependencies
import 'leaflet-editable';
import 'leaflet.offline';
import React, { useEffect, useRef } from 'react';
import { useMap, useMapEvent } from 'react-leaflet';
import { useDispatch, useSelector } from 'react-redux';
import { CUSTOM_LAYER_DRAWN } from 'state/actions';

//temporary fix to type is undefined error
(window as any).type = undefined;

export const DrawCustomLayer = (props) => {
const map = useMap();
const dispatch = useDispatch();
const ref = useRef();

//refactor stuff for topo button
const drawingCustomLayer = useSelector((state: any) => state.Map.drawingCustomLayer);

useEffect(() => {
if (drawingCustomLayer == true) {
ref.current = new (L as any).Draw.Polygon(map);
ref.current.enable();
}

return () => {
if (ref.current) ref.current.disable();
};
}, [drawingCustomLayer]);

useMapEvent('draw:created' as any, (e) => {
if (drawingCustomLayer)
dispatch({ type: CUSTOM_LAYER_DRAWN, payload: { feature: e.layer.toGeoJSON() } });
}
);

return <></>;
};
12 changes: 11 additions & 1 deletion appv2/src/UI/Map/LayerPickerBasic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const LayerPickerBasic = (props) => {
const simplePickerLayers = useSelector((state: any) => state.Map.simplePickerLayers);
const center: LatLngExpression = [51.505, -0.09];
const serverBoundariesToDisplay = useSelector((state: any) => state.Map.serverBoundaries);
const clientBoundariesToDisplay = useSelector((state: any) => state.Map.clientBoundaries);
const rectangle: LatLngBoundsExpression = [
[51.49, -0.08],
[51.5, -0.06]
Expand Down Expand Up @@ -231,7 +232,16 @@ const dispatch = useDispatch();
</LayersControl.Overlay>
{serverBoundariesToDisplay?.map((boundary) => {
return (
<LayersControl.Overlay checked={false} name={'KML/KMZ: ' + boundary.title}>
<LayersControl.Overlay checked={true} name={'KML/KMZ: ' + boundary.title}>
<LayerGroup>
<GeoJSON data={boundary.geojson} />
</LayerGroup>
</LayersControl.Overlay>
);
})}
{clientBoundariesToDisplay?.map((boundary) => {
return (
<LayersControl.Overlay checked={true} name={'Drawn locally' + boundary.title}>
<LayerGroup>
<GeoJSON data={boundary.geojson} />
</LayerGroup>
Expand Down
41 changes: 38 additions & 3 deletions appv2/src/UI/Overlay/Records/RecordSet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,10 @@ const Filter = (props) => {
const serverBoundariesToDisplay = useSelector((state: any) => state.Map.serverBoundaries)?.map((boundary) => {
return { label: boundary.title, value: boundary.id };
});
console.dir(userSettingsState);
const clientBoundariesToDisplay = useSelector((state: any) => state.Map.clientBoundaries)?.map((boundary) => {
return { label: boundary.title, value: boundary.id };
});


const filterColumns =
userSettingsState?.recordSets?.[props.setID].recordSetType === 'Activity'
Expand Down Expand Up @@ -294,6 +297,35 @@ const Filter = (props) => {
</select>
);

break;
case 'spatialFilterDrawn':
input = (
<select
className="filterSelect"
key={'filterType' + props.name}
value={valueInState}
onChange={(e) => {
console.dir(e.target);

dispatch({
type: RECORDSET_UPDATE_FILTER,
payload: {
setID: props.setID,
filterID: props.id,
filter: e.target.value
}
});
}}>
{clientBoundariesToDisplay?.map((option) => {
return (
<option key={option.value + option.label} value={option.value}>
{option.label}
</option>
);
})}
</select>
);

break;
default:
null;
Expand All @@ -317,6 +349,9 @@ const Filter = (props) => {
if(e.target.value === 'spatialFilterUploaded'){
payload.filter = serverBoundariesToDisplay[0].value
}
if(e.target.value === 'spatialFilterDrawn') {
payload.filter = clientBoundariesToDisplay[0].value
}

dispatch({
type: RECORDSET_UPDATE_FILTER,
Expand All @@ -328,10 +363,10 @@ const Filter = (props) => {
<option key={Math.random()} value={'tableFilter'} label={'Field/Column'}>
Field/Column
</option>
<option key={Math.random()} value={'spatialFilterDrawn'} label={'Spatial - Drawn'}>
<option disabled={clientBoundariesToDisplay.length < 1} key={Math.random()} value={'spatialFilterDrawn'} label={'Spatial - Drawn'}>
Spatial - Drawn
</option>
<option key={Math.random()} value={'spatialFilterUploaded'} label={'Spatial - Uploaded'}>
<option disabled={serverBoundariesToDisplay.length < 1} key={Math.random()} value={'spatialFilterUploaded'} label={'Spatial - Uploaded'}>
Spatial - Uploaded
</option>
</select>
Expand Down
6 changes: 6 additions & 0 deletions appv2/src/state/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export const SET_APP_MODE = 'SET_APP_MODE';
export const TOGGLE_PANEL = 'TOGGLE_PANEL';
export const OVERLAY_MENU_TOGGLE = 'OVERLAY_MENU_TOGGLE'
export const URL_CHANGE = 'URL_CHANGE';
export const SET_CURRENT_OPEN_SET = 'SET_CURRENT_OPEN_SET';

export const USER_CLICKED_RECORD = 'USER_CLICKED_RECORD'
export const USER_HOVERED_RECORD = 'USER_HOVERED_RECORD'
Expand Down Expand Up @@ -320,6 +321,11 @@ export const ACTIVITIES_GET_IDS_FOR_RECORDSET_ONLINE = 'ACTIVITIES_GET_IDS_FOR_R
export const ACTIVITIES_GET_IDS_FOR_RECORDSET_OFFLINE = 'ACTIVITIES_GET_IDS_FOR_RECORDSET_OFFLINE';
export const ACTIVITIES_GET_IDS_FOR_RECORDSET_SUCCESS = 'ACTIVITIES_GET_IDS_FOR_RECORDSET_SUCCESS';

export const DRAW_CUSTOM_LAYER = 'DRAW_CUSTOM_LAYER'
export const CUSTOM_LAYER_DRAWN = 'CUSTOM_LAYER_DRAWN'
export const REMOVE_CLIENT_BOUNDARY = 'REMOVE_CLIENT_BOUNDARY'
export const REMOVE_SERVER_BOUNDARY = 'REMOVE_SERVER_BOUNDARY'

export const BATCH_LIST_REQUEST = 'BATCH_LIST_REQUEST';
export const BATCH_LIST_SUCCESS = 'BATCH_LIST_SUCCESS';
export const BATCH_LIST_ERROR = 'BATCH_LIST_ERROR';
Expand Down
Loading
Loading