diff --git a/django_project/frontend/src/pages/Admin/Dashboard/Form/Filters/index.jsx b/django_project/frontend/src/pages/Admin/Dashboard/Form/Filters/index.jsx index 455492b86..77dc3374f 100644 --- a/django_project/frontend/src/pages/Admin/Dashboard/Form/Filters/index.jsx +++ b/django_project/frontend/src/pages/Admin/Dashboard/Form/Filters/index.jsx @@ -16,6 +16,7 @@ import React from 'react'; import { useDispatch, useSelector } from "react-redux"; import Checkbox from "@mui/material/Checkbox"; +import { FormControlLabel } from "@mui/material"; import FiltersAccordion from "../../../../Dashboard/LeftPanel/Filters"; import { Actions } from "../../../../../store/dashboard/index"; @@ -27,13 +28,26 @@ import './style.scss'; export default function FiltersForm() { const dispatch = useDispatch(); const { - filtersAllowModify + filtersAllowModify, + auto_zoom_to_filter } = useSelector(state => state.dashboard.data); return
- { - dispatch(Actions.Dashboard.updateFiltersAllowModify()) - }}/> Allow users to modify filters in dashboard + } + onChange={evt => { + dispatch(Actions.Dashboard.updateFiltersAllowModify()) + }} + label={'Allow users to modify filters in dashboard'}/> +
+ } + onChange={evt => { + dispatch(Actions.Dashboard.updateAutoZoomToFilter()) + }} + label={'Zoom in automatically to filtered area'}/>
} \ No newline at end of file diff --git a/django_project/frontend/src/pages/Admin/Dashboard/Form/index.jsx b/django_project/frontend/src/pages/Admin/Dashboard/Form/index.jsx index eed6b4895..c9ab07832 100644 --- a/django_project/frontend/src/pages/Admin/Dashboard/Form/index.jsx +++ b/django_project/frontend/src/pages/Admin/Dashboard/Form/index.jsx @@ -180,6 +180,7 @@ export function DashboardSaveForm( extent, filters, filtersAllowModify, + auto_zoom_to_filter, permission, geoField, levelConfig @@ -298,6 +299,7 @@ export function DashboardSaveForm( 'widgets_structure': widgetsStructure, 'filters': filtersData ? filtersData : filters, 'filters_allow_modify': filtersAllowModify, + 'auto_zoom_to_filter': auto_zoom_to_filter, 'permission': permission, 'show_splash_first_open': splashScreen, 'truncate_indicator_layer_name': truncateIndicatorName, diff --git a/django_project/frontend/src/pages/Dashboard/MapLibre/index.jsx b/django_project/frontend/src/pages/Dashboard/MapLibre/index.jsx index e432101f6..fde631da4 100644 --- a/django_project/frontend/src/pages/Dashboard/MapLibre/index.jsx +++ b/django_project/frontend/src/pages/Dashboard/MapLibre/index.jsx @@ -44,6 +44,7 @@ import { SearchGeometryInput, TiltControl, ToggleSidePanel, + ZoomToFilteredGeometries } from '../Toolbars' import { EmbedConfig } from "../../../utils/embed"; import { Actions } from "../../../store/dashboard"; @@ -247,6 +248,7 @@ export default function MapLibre( {/* Embed */}
+
diff --git a/django_project/frontend/src/pages/Dashboard/Toolbars/ZoomToFilteredGeometries/index.jsx b/django_project/frontend/src/pages/Dashboard/Toolbars/ZoomToFilteredGeometries/index.jsx new file mode 100644 index 000000000..7565a1a2b --- /dev/null +++ b/django_project/frontend/src/pages/Dashboard/Toolbars/ZoomToFilteredGeometries/index.jsx @@ -0,0 +1,90 @@ +/** + * GeoSight is UNICEF's geospatial web-based business intelligence platform. + * + * Contact : geosight-no-reply@unicef.org + * + * .. note:: This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * __author__ = 'irwan@kartoza.com' + * __date__ = '07/09/2023' + * __copyright__ = ('Copyright 2023, Unicef') + */ + +/* ========================================================================== + Zoom To Geometries by Filters + This is called and triggered when we do filters + And "Zoom in automatically in filtered areas" is true + ========================================================================== */ + +import React, { useEffect } from 'react'; + +import { useSelector } from "react-redux"; +import { returnWhere } from "../../../../utils/queryExtraction"; +import { fetchJson } from "../../../../utils/georepo"; +import { Session } from "../../../../utils/Sessions"; +import { GeometriesBBOX } from "../../../../utils/geometry"; +import { dictDeepCopy } from "../../../../utils/main"; + +import './style.scss'; + +/** + * Zoom To Geometries by Filters. + */ +export default function ZoomToFilteredGeometries({ map }) { + const { + referenceLayer, + auto_zoom_to_filter + } = useSelector(state => state.dashboard.data); + const filteredGeometries = useSelector(state => state.filteredGeometries); + const geometries = useSelector(state => state.geometries); + const filtersData = useSelector(state => state.filtersData); + const selectedAdminLevel = useSelector(state => state.selectedAdminLevel); + + useEffect(() => { + ( + async () => { + if (!map || !auto_zoom_to_filter) { + return + } + const session = new Session('ZoomToGeometriesByFilters', 1000) + + const where = returnWhere(filtersData ? filtersData : []) + const level = selectedAdminLevel?.level + if (!filteredGeometries || !geometries[level] || !where) { + return + } + const geoms = dictDeepCopy(geometries[level]); + const usedGeometries = [] + for (let i = 0; i < filteredGeometries.length; i++) { + const geom = filteredGeometries[i] + const found = geoms[geom] + if (found) { + try { + const response = await fetchJson(`/operation/view/${referenceLayer.identifier}/bbox/concept_uuid/${found.concept_uuid}/`) + found.bbox = response + usedGeometries.push(found) + } catch (err) { + + } + } + } + if (session.isValid) { + const extent = GeometriesBBOX(usedGeometries) + if (extent) { + map.fitBounds([ + [extent[0], extent[1]], + [extent[2], extent[3]] + ], + { padding: 20 } + ) + } + } + } + )() + }, [filteredGeometries, filtersData]); + + return null +} \ No newline at end of file diff --git a/django_project/frontend/src/pages/Dashboard/Toolbars/ZoomToFilteredGeometries/style.scss b/django_project/frontend/src/pages/Dashboard/Toolbars/ZoomToFilteredGeometries/style.scss new file mode 100644 index 000000000..e0d2ae695 --- /dev/null +++ b/django_project/frontend/src/pages/Dashboard/Toolbars/ZoomToFilteredGeometries/style.scss @@ -0,0 +1,14 @@ +/** +* GeoSight is UNICEF's geospatial web-based business intelligence platform. +* +* Contact : geosight-no-reply@unicef.org +* +* .. note:: This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as published by +* the Free Software Foundation; either version 3 of the License, or +* (at your option) any later version. +* +* __author__ = 'irwan@kartoza.com' +* __date__ = '07/09/2023' +* __copyright__ = ('Copyright 2023, Unicef') +*/ \ No newline at end of file diff --git a/django_project/frontend/src/pages/Dashboard/Toolbars/index.jsx b/django_project/frontend/src/pages/Dashboard/Toolbars/index.jsx index 7ce0d13f3..841714d87 100644 --- a/django_project/frontend/src/pages/Dashboard/Toolbars/index.jsx +++ b/django_project/frontend/src/pages/Dashboard/Toolbars/index.jsx @@ -25,4 +25,5 @@ export { default as EmbedControl } from "./Embed"; export { default as LabelToggler } from "./LabelToggler"; export { default as ProjectOverview } from "./ProjectOverview"; export { default as SearchGeometryInput } from "./SearchGeometryInput"; -export { default as ToggleSidePanel } from "./ToggleSidePanel"; \ No newline at end of file +export { default as ToggleSidePanel } from "./ToggleSidePanel"; +export { default as ZoomToFilteredGeometries } from "./ZoomToFilteredGeometries"; \ No newline at end of file diff --git a/django_project/frontend/src/store/dashboard/reducers/dashboard/actions.jsx b/django_project/frontend/src/store/dashboard/reducers/dashboard/actions.jsx index cb8b885b7..e5cd66490 100644 --- a/django_project/frontend/src/store/dashboard/reducers/dashboard/actions.jsx +++ b/django_project/frontend/src/store/dashboard/reducers/dashboard/actions.jsx @@ -17,6 +17,7 @@ import { fetchingData } from "../../../../Requests"; import { DASHBOARD_ACTION_NAME, + DASHBOARD_ACTION_TYPE_AUTO_ZOOM_TO_FILTER, DASHBOARD_ACTION_TYPE_FILTERS_ALLOW_MODIFY, DASHBOARD_ACTION_TYPE_UPDATE, DASHBOARD_ACTION_TYPE_UPDATE_GEOFIELD, @@ -192,6 +193,16 @@ export function updateFiltersAllowModify() { }; } +/** + * Update auto zoom to filter. + */ +export function updateAutoZoomToFilter() { + return { + name: DASHBOARD_ACTION_NAME, + type: DASHBOARD_ACTION_TYPE_AUTO_ZOOM_TO_FILTER + }; +} + /** * Change geofield */ @@ -224,6 +235,7 @@ export default { update, updateProps, updateFiltersAllowModify, + updateAutoZoomToFilter, updatePermission, changeGeoField, updateStructure diff --git a/django_project/frontend/src/store/dashboard/reducers/dashboard/index.js b/django_project/frontend/src/store/dashboard/reducers/dashboard/index.js index a0d746779..8f9ef744f 100644 --- a/django_project/frontend/src/store/dashboard/reducers/dashboard/index.js +++ b/django_project/frontend/src/store/dashboard/reducers/dashboard/index.js @@ -39,6 +39,7 @@ export const DASHBOARD_ACTION_NAME = 'DASHBOARD'; export const DASHBOARD_ACTION_TYPE_UPDATE = 'DASHBOARD/UPDATE'; export const DASHBOARD_ACTION_TYPE_UPDATE_PROPS = 'DASHBOARD/UPDATE_PROPS'; export const DASHBOARD_ACTION_TYPE_FILTERS_ALLOW_MODIFY = 'DASHBOARD/FILTERS_ALLOW_MODIFY'; +export const DASHBOARD_ACTION_TYPE_AUTO_ZOOM_TO_FILTER = 'DASHBOARD/AUTO_ZOOM_TO_FILTER'; export const DASHBOARD_ACTION_TYPE_UPDATE_SHARE = 'DASHBOARD/UPDATE_SHARE'; export const DASHBOARD_ACTION_TYPE_UPDATE_GEOFIELD = 'DASHBOARD/UPDATE_GEOFIELD'; export const DASHBOARD_ACTION_TYPE_UPDATE_STRUCTURE = 'DASHBOARD/UPDATE_STRUCTURE'; @@ -80,6 +81,15 @@ export default function dashboardReducer( } } } + case DASHBOARD_ACTION_TYPE_AUTO_ZOOM_TO_FILTER: { + return { + ...state, + data: { + ...state.data, + auto_zoom_to_filter: !state.data.auto_zoom_to_filter + } + } + } case DASHBOARD_ACTION_TYPE_UPDATE_SHARE: { return { ...state, diff --git a/django_project/geosight/data/forms/dashboard.py b/django_project/geosight/data/forms/dashboard.py index 848e6a518..b85115275 100644 --- a/django_project/geosight/data/forms/dashboard.py +++ b/django_project/geosight/data/forms/dashboard.py @@ -117,5 +117,7 @@ def update_data(data): data['filters'] = json.dumps(other_data['filters']) data['filters_allow_modify'] = other_data.get( 'filters_allow_modify', False) + data['auto_zoom_to_filter'] = other_data.get( + 'auto_zoom_to_filter', False) data['permission'] = other_data['permission'] return data diff --git a/django_project/geosight/data/migrations/0086_dashboard_auto_zoom_to_filter.py b/django_project/geosight/data/migrations/0086_dashboard_auto_zoom_to_filter.py new file mode 100644 index 000000000..105777ed8 --- /dev/null +++ b/django_project/geosight/data/migrations/0086_dashboard_auto_zoom_to_filter.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2023-09-07 05:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('geosight_data', '0085_dashboard_enable_geometry_search'), + ] + + operations = [ + migrations.AddField( + model_name='dashboard', + name='auto_zoom_to_filter', + field=models.BooleanField(default=False), + ), + ] diff --git a/django_project/geosight/data/models/dashboard/dashboard.py b/django_project/geosight/data/models/dashboard/dashboard.py index 60e8801af..762e149cc 100644 --- a/django_project/geosight/data/models/dashboard/dashboard.py +++ b/django_project/geosight/data/models/dashboard/dashboard.py @@ -70,6 +70,9 @@ class Dashboard(SlugTerm, IconTerm, AbstractEditData): filters_allow_modify = models.BooleanField( default=False ) + auto_zoom_to_filter = models.BooleanField( + default=False + ) geo_field = models.CharField( max_length=64, default='concept_uuid' diff --git a/django_project/geosight/data/serializer/dashboard.py b/django_project/geosight/data/serializer/dashboard.py index 744d9b23f..d9a507307 100644 --- a/django_project/geosight/data/serializer/dashboard.py +++ b/django_project/geosight/data/serializer/dashboard.py @@ -237,7 +237,7 @@ class Meta: # noqa: D106 fields = ( 'id', 'slug', 'icon', 'name', 'description', 'category', 'group', - 'extent', 'filters', 'filters_allow_modify', + 'extent', 'filters', 'filters_allow_modify', 'auto_zoom_to_filter', 'reference_layer', 'level_config', 'indicators', 'indicator_layers', 'indicator_layers_structure', 'context_layers', 'context_layers_structure',