diff --git a/api/src/paths/v2/activities.ts b/api/src/paths/v2/activities.ts
index 3c7a36078..6df410a3e 100644
--- a/api/src/paths/v2/activities.ts
+++ b/api/src/paths/v2/activities.ts
@@ -100,8 +100,7 @@ function sanitizeActivityFilterObject(filterObject: any, req: any) {
}
if (!isAuth) {
sanitizedSearchCriteria.serverSideNamedFilters.hideEditedByFields = true;
- }
- else {
+ } else {
sanitizedSearchCriteria.serverSideNamedFilters.hideEditedByFields = false;
}
@@ -122,12 +121,12 @@ function sanitizeActivityFilterObject(filterObject: any, req: any) {
break;
/* NOTE: any payload columns need this: */
case 'project_code':
- if(!selectColumns.includes('activity_payload')){
+ if (!selectColumns.includes('activity_payload')) {
selectColumns.push('activity_payload');
}
break;
case 'activity_date':
- if(!selectColumns.includes('activity_payload')){
+ if (!selectColumns.includes('activity_payload')) {
selectColumns.push('activity_payload');
}
break;
@@ -151,41 +150,61 @@ function sanitizeActivityFilterObject(filterObject: any, req: any) {
offset = filterObject.page * filterObject.limit;
}
-
sanitizedSearchCriteria.limit = limit;
sanitizedSearchCriteria.offset = offset;
sanitizedSearchCriteria.selectColumns = selectColumns;
let sanitizedTableFilters = [];
+ //sanitize serverFilterGeometries
+ let serverFilterGeometries = [];
+
+ //sanitize clientFilterGeometries
+ let clientFilterGeometries = [];
if (filterObject?.tableFilters?.length > 0) {
filterObject.tableFilters.forEach((filter) => {
- switch (filter.field) {
- case 'created_by':
- if (!sanitizedSearchCriteria.serverSideNamedFilters.hideEditedByFields) {
- sanitizedTableFilters.push(filter);
+ switch (filter.filterType) {
+ case 'tableFilter':
+ switch (filter.field) {
+ case 'created_by':
+ if (!sanitizedSearchCriteria.serverSideNamedFilters.hideEditedByFields) {
+ sanitizedTableFilters.push(filter);
+ }
+ break;
+ case 'updated_by':
+ if (!sanitizedSearchCriteria.serverSideNamedFilters.hideEditedByFields) {
+ sanitizedTableFilters.push(filter);
+ }
+ break;
+ default:
+ sanitizedTableFilters.push(filter);
+ break;
}
break;
- case 'updated_by':
- if (!sanitizedSearchCriteria.serverSideNamedFilters.hideEditedByFields) {
- sanitizedTableFilters.push(filter);
+ case 'spatialFilterDrawn':
+ if (filter.filter) {
+ clientFilterGeometries.push(filter.filter?.geometry);
+ }
+ break;
+ case 'spatialFilterUploaded':
+ if (!isNaN(parseInt(filter?.filter))) {
+ serverFilterGeometries.push(parseInt(filter.filter));
}
break;
default:
- sanitizedTableFilters.push(filter);
break;
}
});
}
-
-
+ sanitizedSearchCriteria.serverFilterGeometries = serverFilterGeometries;
+ sanitizedSearchCriteria.clientFilterGeometries = clientFilterGeometries;
sanitizedSearchCriteria.clientReqTableFilters = sanitizedTableFilters;
defaultLog.debug({
label: 'getActivitiesBySearchFilterCriteria',
message: 'sanitizedObject',
- body: sanitizedSearchCriteria
+ body: JSON.stringify(sanitizedSearchCriteria, null, 2)
});
return sanitizedSearchCriteria;
@@ -243,6 +262,9 @@ function getActivitiesBySearchFilterCriteria(): RequestHandler {
}
function getActivitiesSQLv2(filterObject: any) {
+ try
+ {
+
let sqlStatement: SQLStatement = SQL``;
sqlStatement = initialWithStatement(sqlStatement);
sqlStatement = additionalCTEStatements(sqlStatement, filterObject);
@@ -252,10 +274,16 @@ function getActivitiesSQLv2(filterObject: any) {
sqlStatement = groupByStatement(sqlStatement, filterObject);
sqlStatement = orderByStatement(sqlStatement, filterObject);
sqlStatement = limitStatement(sqlStatement, filterObject);
- sqlStatement = offSetStatement(sqlStatement, filterObject)
+ sqlStatement = offSetStatement(sqlStatement, filterObject);
defaultLog.debug({ label: 'getActivitiesBySearchFilterCriteria', message: 'sql', body: sqlStatement });
return sqlStatement;
+ }
+
+ catch(e) {
+ defaultLog.debug({ label: 'getActivitiesBySearchFilterCriteria', message: 'error', body: e.message });
+ throw e
+ }
}
function initialWithStatement(sqlStatement: SQLStatement) {
@@ -285,20 +313,120 @@ CurrentNegativeObservations AS (
invasivesbc.current_negative_observations cno
GROUP BY
cno.activity_incoming_data_id),
+`);
+
+ if (filterObject?.serverFilterGeometries?.length > 0) {
+ sqlStatement.append(`
+
+ serverFilterGeometryIDs as (
+
+ select unnest(array[${filterObject?.serverFilterGeometries.join(',')}]) as id
+
+ ),
+ serverFilterGeometries AS (
+ select a.id, title, st_subdivide(geog::geometry) as geo
+ from invasivesbc.admin_defined_shapes a
+ inner join serverFilterGeometryIDs b on a.id = b.id
+ ),
+
+ serverFilterGeometriesIntersecting as (
+
+ select a.activity_incoming_data_id, b.id
+ from not_deleted_activities a
+ inner join serverFilterGeometries b on st_intersects((a.geog::geometry), b.geo)
+ group by a.activity_incoming_data_id, b.id
+
+
+ ),
+ serverFilterGeometriesIntersectingAll as (
+
+ select a.activity_incoming_data_id, count(*)
+ from not_deleted_activities a
+ inner join serverFilterGeometriesIntersecting b on a.activity_incoming_data_id = b.activity_incoming_data_id
+ group by a.activity_incoming_data_id
+
+ having count(*) = (select count(*) from serverFilterGeometryIDs)
+ ),
+ `);
+ }
+ if (filterObject?.clientFilterGeometries?.length > 0) {
+ sqlStatement.append(`
+ clientFilterGeometries AS (
+ SELECT
+ unnest(array[${filterObject.clientFilterGeometries
+ .map((geometry) => `st_setsrid(st_geomfromgeojson(${geometry?.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)
+
+ ),
+ clientFilterGeometriesIntersectingAll as (
+
+ select a.activity_incoming_data_id, count(*)
+ from not_deleted_activities a
+ inner join clientFilterGeometriesIntersecting b on a.activity_incoming_data_id = b.activity_incoming_data_id
+ group by a.activity_incoming_data_id
+
+ having count(*) = (select count(*) from clientFilterGeometries)
+ ),
+ `);
+ }
+
+ sqlStatement.append(`
activities as (
- select not_deleted_activities.*, CurrentPositiveObservations.current_positive_species, CurrentNegativeObservations.current_negative_species,
+ select a.*, CurrentPositiveObservations.current_positive_species, CurrentNegativeObservations.current_negative_species,
case when CurrentPositiveObservations.current_positive_species is null then false else true end as has_current_positive,
case when CurrentNegativeObservations.current_negative_species is null then false else true end as has_current_negative
- from not_deleted_activities
- left join CurrentPositiveObservations on CurrentPositiveObservations.activity_incoming_data_id = not_deleted_activities.activity_incoming_data_id
- left join CurrentNegativeObservations on CurrentNegativeObservations.activity_incoming_data_id = not_deleted_activities.activity_incoming_data_id
-) `);
+ `);
+
+ /*if (filterObject?.serverFilterGeometries?.length > 0) {
+ sqlStatement.append(`
+ ,case when ServerBoundariesToIntersect.geog is null then false else true end as intersects_server_boundary
+ `);
+ }
+ if (filterObject?.clientFilterGeometries?.length > 0) {
+ sqlStatement.append(`
+ ,case when ClientBoundariesToIntersect.geog is null then false else true end as intersects_client_boundary
+ `);
+ }*/
+
+ sqlStatement.append(`
+ from not_deleted_activities a
+ left join CurrentPositiveObservations on CurrentPositiveObservations.activity_incoming_data_id = a.activity_incoming_data_id
+ left join CurrentNegativeObservations on CurrentNegativeObservations.activity_incoming_data_id = a.activity_incoming_data_id
+
+ `);
+
+ if (filterObject?.serverFilterGeometries?.length > 0) {
+ sqlStatement.append(`
+ inner join serverFilterGeometriesIntersectingAll c on a.activity_incoming_data_id = c.activity_incoming_data_id
+ `);
+ }
+
+ if (filterObject?.clientFilterGeometries?.length > 0) {
+ sqlStatement.append(`
+ inner join clientFilterGeometriesIntersectingAll d on a.activity_incoming_data_id = d.activity_incoming_data_id
+ `);
+ }
+
+ sqlStatement.append(`
+ ) `);
+
+ defaultLog.debug({ label: 'getActivitiesBySearchFilterCriteria', message: 'sql', body: sqlStatement });
+
return cte;
}
function selectStatement(sqlStatement: SQLStatement, filterObject: any) {
if (filterObject.selectColumns) {
- const select = sqlStatement.append(`select ${filterObject.selectColumns.map((column) => `activities.${column}`).join(',')} `);
+ const select = sqlStatement.append(
+ `select ${filterObject.selectColumns.map((column) => `activities.${column}`).join(',')} `
+ );
return select;
} else {
const select = sqlStatement.append(`select * `);
@@ -320,72 +448,138 @@ function whereStatement(sqlStatement: SQLStatement, filterObject: any) {
filterObject.clientReqTableFilters.forEach((filter) => {
switch (filter.field) {
case 'activity_id':
- where.append(`and activities.activity_id ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.activity_id ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${filter.filter}%' `
+ );
break;
case 'short_id':
- where.append(`and activities.short_id ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.short_id ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${filter.filter}%' `
+ );
break;
case 'activity_type':
- where.append(`and activities.activity_type ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.activity_type ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${filter.filter}%' `
+ );
break;
case 'activity_subtype':
- where.append(`and activities.activity_subtype ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.activity_subtype ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${
+ filter.filter
+ }%' `
+ );
break;
case 'activity_date':
- where.append(`and substring((activities.activity_payload::json->'form_data'->'activity_data'->'activity_date_time'::text)::text, 2, 10) ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `)
+ where.append(
+ `and substring((activities.activity_payload::json->'form_data'->'activity_data'->'activity_date_time'::text)::text, 2, 10) ${
+ filter.operator === 'CONTAINS' ? 'like' : 'not like'
+ } '%${filter.filter}%' `
+ );
break;
case 'project_code':
where.append(
- `and (activities.activity_payload::json->'form_data'->'activity_data'->'project_code'::text)::text ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `
+ `and (activities.activity_payload::json->'form_data'->'activity_data'->'project_code'::text)::text ${
+ filter.operator === 'CONTAINS' ? 'like' : 'not like'
+ } '%${filter.filter}%' `
);
break;
case 'jurisdiction_display':
- where.append(`and activities.jurisdiction_display ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.jurisdiction_display ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${
+ filter.filter
+ }%' `
+ );
break;
case 'species_positive_full':
- where.append(`and activities.species_positive_full ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.species_positive_full ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${
+ filter.filter
+ }%' `
+ );
break;
case 'species_negative_full':
- where.append(`and activities.species_negative_full ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.species_negative_full ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${
+ filter.filter
+ }%' `
+ );
break;
case 'has_current_positive':
- where.append(`and activities.current_positive_species ${filter.operator === 'CONTAINS'? 'is not': 'is'} null `);
+ where.append(
+ `and activities.current_positive_species ${filter.operator === 'CONTAINS' ? 'is not' : 'is'} null `
+ );
break;
case 'has_current_negative':
- where.append(`and activities.current_negative_species ${filter.operator === 'CONTAINS'? 'is not': 'is'} null `);
+ where.append(
+ `and activities.current_negative_species ${filter.operator === 'CONTAINS' ? 'is not' : 'is'} null `
+ );
break;
case 'current_positive_species':
- where.append(`and activities.current_positive_species ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.current_positive_species ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${
+ filter.filter
+ }%' `
+ );
break;
case 'current_negative_species':
- where.append(`and activities.current_negative_species ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.current_negative_species ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${
+ filter.filter
+ }%' `
+ );
break;
case 'species_treated_full':
- where.append(`and activities.species_treated_full ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.species_treated_full ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${
+ filter.filter
+ }%' `
+ );
break;
case 'created_by':
- where.append(`and activities.created_by ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.created_by ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${filter.filter}%' `
+ );
break;
case 'updated_by':
- where.append(`and activities.updated_by ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.updated_by ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${filter.filter}%' `
+ );
break;
case 'agency':
- where.append(`and activities.agency ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.agency ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${filter.filter}%' `
+ );
break;
case 'regional_invasive_species_organization_areas':
- where.append(`and activities.regional_invasive_species_organization_areas ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.regional_invasive_species_organization_areas ${
+ filter.operator === 'CONTAINS' ? 'like' : 'not like'
+ } '%${filter.filter}%' `
+ );
break;
case 'regional_districts':
- where.append(`and activities.regional_districts ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.regional_districts ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${
+ filter.filter
+ }%' `
+ );
break;
case 'biogeoclimatic_zones':
- where.append(`and activities.biogeoclimatic_zones ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.biogeoclimatic_zones ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${
+ filter.filter
+ }%' `
+ );
break;
case 'elevation':
- where.append(`and activities.elevation ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.elevation ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${filter.filter}%' `
+ );
break;
case 'batch_id':
- where.append(`and activities.batch_id::text ${filter.operator === 'CONTAINS'? 'like': 'not like'} '%${filter.filter}%' `);
+ where.append(
+ `and activities.batch_id::text ${filter.operator === 'CONTAINS' ? 'like' : 'not like'} '%${filter.filter}%' `
+ );
break;
default:
break;
diff --git a/appv2/src/UI/Map/OnHoverActivity.tsx b/appv2/src/UI/Map/OnHoverActivity.tsx
index 4c50acfc3..05eee2573 100644
--- a/appv2/src/UI/Map/OnHoverActivity.tsx
+++ b/appv2/src/UI/Map/OnHoverActivity.tsx
@@ -1,4 +1,4 @@
-import { GeoJSON } from 'react-leaflet';
+import { GeoJSON, useMap } from 'react-leaflet';
import React, { useEffect, useLayoutEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import center from '@turf/center';
@@ -10,6 +10,7 @@ export const OnHoverActivity = (props: any) => {
const geometresForActivity = useSelector((state: any) => state.Map?.userRecordOnHoverRecordRow?.geometry);
const [centerPointGeometry, setCenterPointGeometry] = useState(null);
const popupRef = React.useRef(null);
+ const map = useMap();
useEffect(() => {
try {
@@ -45,5 +46,12 @@ export const OnHoverActivity = (props: any) => {
}
}, [centerPointGeometry]);
+
+ map.on('popupopen', function(e) {
+ var px = map.project(e.target._popup._latlng); // find the pixel location on the map where the popup anchor is
+ px.y += (e.target._popup._container.clientHeight * 6 )
+ map.panTo(map.unproject(px),{animate: true}); // pan to new center
+});
+
return centerPointGeometry !== null ?