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

Added table of content to open layer details in map view. #1661

Merged
merged 17 commits into from
Dec 4, 2024
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
22 changes: 22 additions & 0 deletions geonode_mapstore_client/client/js/actions/gnresource.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export const SET_MAP_VIEWER_LINKED_RESOURCE = 'GEONODE:SET_MAP_VIEWER_LINKED_RES
export const MANAGE_LINKED_RESOURCE = 'GEONODE:MANAGE_LINKED_RESOURCE';
export const SET_DEFAULT_VIEWER_PLUGINS = 'GEONODE:SET_DEFAULT_VIEWER_PLUGINS';
export const SET_SELECTED_LAYER = 'GEONODE:SET_SELECTED_LAYER';
export const UPDATE_LAYER_DATASET = 'GEONODE:UPDATE_LAYER_DATASET';
export const SET_SELECTED_LAYER_DATASET = 'GEONODE:SET_SELECTED_LAYER_DATASET';

/**
* Actions for GeoNode resource
Expand Down Expand Up @@ -372,3 +374,23 @@ export function setSelectedLayer(layer) {
layer
};
}

/**
* Update layer dataset
*/
export function updateLayerDataset(layer) {
return {
type: UPDATE_LAYER_DATASET,
layer
};
}

/**
* Get layer dataset
*/
export function setLayerDataset(layerId) {
return {
type: SET_SELECTED_LAYER_DATASET,
layerId
};
}
2 changes: 2 additions & 0 deletions geonode_mapstore_client/client/js/apps/gn-catalogue.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
import { CATALOGUE_ROUTES, appRouteComponentTypes } from '@js/utils/AppRoutesUtils';
import { updateGeoNodeSettings } from '@js/actions/gnsettings';
import {
gnFetchMissingLayerData,
gnCheckSelectedDatasetPermissions,
gnSetDatasetsPermissions,
// to make the current layout work we need this epic
Expand Down Expand Up @@ -140,6 +141,7 @@ getEndpoints()
const appEpics = cleanEpics({
...standardEpics,
...configEpics,
gnFetchMissingLayerData,
gnCheckSelectedDatasetPermissions,
gnSetDatasetsPermissions,
...pluginsDefinition.epics,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const EditTitle = ({ title, onEdit, disabled }) => {
setTextValue(evt.target.value);
onEdit(evt.target.value);
}}
value={textValue}
value={onEdit ? textValue : title}
disabled={disabled}
/>
</div>);
Expand Down
36 changes: 34 additions & 2 deletions geonode_mapstore_client/client/js/epics/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import Rx from "rxjs";
import { setEditPermissionStyleEditor, INIT_STYLE_SERVICE } from "@mapstore/framework/actions/styleeditor";
import { getSelectedLayer, layersSelector } from "@mapstore/framework/selectors/layers";
import { getConfigProp } from "@mapstore/framework/utils/ConfigUtils";
import { getDatasetByName, getDatasetsByName } from '@js/api/geonode/v2';
import { getDatasetByName, getDatasetsByName, getDatasetByPk } from '@js/api/geonode/v2';
import { MAP_CONFIG_LOADED } from '@mapstore/framework/actions/config';
import { setPermission } from '@mapstore/framework/actions/featuregrid';
import { SELECT_NODE, updateNode, ADD_LAYER } from '@mapstore/framework/actions/layers';
import { setSelectedDatasetPermissions, setSelectedLayer } from '@js/actions/gnresource';
import { setSelectedDatasetPermissions, setSelectedLayer, updateLayerDataset, setLayerDataset } from '@js/actions/gnresource';
import { updateMapLayoutEpic as msUpdateMapLayoutEpic } from '@mapstore/framework/epics/maplayout';
import isEmpty from 'lodash/isEmpty';

// We need to include missing epics. The plugins that normally include this epic is not used.

Expand Down Expand Up @@ -55,6 +56,37 @@ export const gnCheckSelectedDatasetPermissions = (action$, { getState } = {}) =>
);
});

/**
* Fetches missing values for selected layers
*/
export const gnFetchMissingLayerData = (action$, { getState } = {}) =>
action$.ofType(SELECT_NODE)
.filter(({ nodeType }) => nodeType && nodeType === "layer")
.switchMap(() => {
const state = getState() || {};
const layer = getSelectedLayer(state);
const layerResourceId = layer?.extendedParams?.pk;
const layerResourceDataset = state.gnresource.data?.maplayers?.find(mapLayer => mapLayer.dataset?.pk === parseInt(layerResourceId, 10))?.dataset;
return layerResourceDataset
? isEmpty(layerResourceDataset?.linkedResources)
? Rx.Observable.defer(() =>
getDatasetByPk(layerResourceId)
.then((layerDataset) => layerDataset)
.catch(() => [])
).switchMap((layerDataset) =>
Rx.Observable.of(
updateLayerDataset(layerDataset),
setLayerDataset(layerResourceId)
)
)
: Rx.Observable.of(
setLayerDataset(layerResourceId)
)
: Rx.Observable.of(
setLayerDataset()
)
});


/**
* Checks the permissions for layers when a map is loaded and when a new layer is added
Expand Down
161 changes: 161 additions & 0 deletions geonode_mapstore_client/client/js/plugins/LayerDetailViewer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright 2021, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import { createPlugin, getMonitoredState } from '@mapstore/framework/utils/PluginsUtils';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { getConfigProp } from '@mapstore/framework/utils/ConfigUtils';
import DetailsPanel from '@js/components/DetailsPanel';
import { enableMapThumbnailViewer } from '@js/actions/gnresource';
import FaIcon from '@js/components/FaIcon/FaIcon';
import controls from '@mapstore/framework/reducers/controls';
import { setControlProperty } from '@mapstore/framework/actions/controls';
import gnresource from '@js/reducers/gnresource';
import { getSelectedLayerDataset } from '@js/selectors/resource';
import GNButton from '@js/components/Button';
import useDetectClickOut from '@js/hooks/useDetectClickOut';
import OverlayContainer from '@js/components/OverlayContainer';
import { withRouter } from 'react-router';
import { hashLocationToHref } from '@js/utils/SearchUtils';
import { mapSelector } from '@mapstore/framework/selectors/map';
import { parsePluginConfigExpressions } from '@js/utils/MenuUtils';
import tooltip from '@mapstore/framework/components/misc/enhancers/tooltip';
import tabComponents from '@js/plugins/detailviewer/tabComponents';

const Button = tooltip(GNButton);

const ConnectedDetailsPanel = connect(
createSelector([
state => state?.gnresource?.selectedLayerDataset || null,
state => state?.gnresource?.loading || false,
mapSelector,
state => state?.gnresource?.showMapThumbnail || false
], (resource, loading, mapData, showMapThumbnail) => ({
resource,
loading,
initialBbox: mapData?.bbox,
enableMapViewer: showMapThumbnail,
resourceId: resource?.pk,
tabComponents
})),
{
closePanel: setControlProperty.bind(null, 'rightOverlay', 'enabled', false),
onClose: enableMapThumbnailViewer
}
)(DetailsPanel);

const ButtonViewer = ({ onClick, layer, size, status }) => {
const layerResourceId = layer?.pk;
const handleClickButton = () => {
onClick();
};
return layerResourceId && status === 'LAYER' ? (
<Button
variant="primary"
size={size}
onClick={handleClickButton}
>
<FaIcon name={'info-circle'} />
</Button>
) : null;
};

const ConnectedButton = connect(
createSelector([
getSelectedLayerDataset
], (layer) => ({
layer
})),
{
onClick: setControlProperty.bind(
null,
'rightOverlay',
'enabled',
'LayerDetailViewer'
)
}
)((ButtonViewer));

function LayerDetailViewer({
location,
enabled,
onClose,
monitoredState,
queryPathname = '/',
tabs = []
}) {
const parsedConfig = parsePluginConfigExpressions(monitoredState, { tabs });

const node = useDetectClickOut({
disabled: !enabled,
onClickOut: () => {
onClose();
}
});

const handleFormatHref = (options) => {
return hashLocationToHref({
location,
...options
});
};

return (
<OverlayContainer
enabled={enabled}
ref={node}
className="gn-overlay-wrapper"
>
<ConnectedDetailsPanel
editTitle={null}
editAbstract={null}
editThumbnail={() => {}}
activeEditMode={false}
enableFavorite={false}
formatHref={handleFormatHref}
tabs={parsedConfig.tabs}
pathname={queryPathname}
/>
</OverlayContainer>
);
}

const LayerDetailViewerPlugin = connect(
createSelector(
[
(state) =>
state?.controls?.rightOverlay?.enabled === 'LayerDetailViewer',
getSelectedLayerDataset,
state => getMonitoredState(state, getConfigProp('monitorState'))
],
(enabled, layer, monitoredState) => ({
enabled,
layer,
monitoredState
})
),
{
onClose: setControlProperty.bind(null, 'rightOverlay', 'enabled', false)
}
)(withRouter(LayerDetailViewer));

export default createPlugin('LayerDetailViewer', {
component: LayerDetailViewerPlugin,
containers: {
TOC: {
target: 'toolbar',
name: 'LayerDetailViewerButton',
Component: ConnectedButton
}
},
reducers: {
gnresource,
controls
}
});
4 changes: 4 additions & 0 deletions geonode_mapstore_client/client/js/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,10 @@ export const plugins = {
'VisualStyleEditor',
() => import(/* webpackChunkName: 'plugins/visual-style-editor-plugin' */ '@js/plugins/VisualStyleEditor')
),
LayerDetailViewerPlugin: toModulePlugin(
'LayerDetailViewer',
() => import(/* webpackChunkName: 'plugins/detail-viewer-plugin' */ '@js/plugins/LayerDetailViewer')
),
LegendPlugin: toModulePlugin(
'Legend',
() => import(/* webpackChunkName: 'plugins/legend-plugin' */ '@js/plugins/Legend')
Expand Down
36 changes: 35 additions & 1 deletion geonode_mapstore_client/client/js/reducers/gnresource.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ import {
SET_RESOURCE_PATH_PARAMETERS,
SET_MAP_VIEWER_LINKED_RESOURCE,
SET_DEFAULT_VIEWER_PLUGINS,
SET_SELECTED_LAYER
SET_SELECTED_LAYER,
UPDATE_LAYER_DATASET,
SET_SELECTED_LAYER_DATASET
} from '@js/actions/gnresource';
import {
cleanCompactPermissions,
Expand Down Expand Up @@ -253,6 +255,38 @@ function gnresource(state = defaultState, action) {
...state,
selectedLayer: action.layer
};
case SET_SELECTED_LAYER_DATASET:
return {
...state,
selectedLayerDataset: state.data?.maplayers?.find(layer => layer.dataset?.pk === parseInt(action.layerId, 10))?.dataset
};
case UPDATE_LAYER_DATASET:
const { pk, ...newData } = action.layer;
let linkedResources = action.layer.linked_resources ?? {};
if (!isEmpty(linkedResources)) {
const linkedTo = linkedResources.linked_to ?? [];
const linkedBy = linkedResources.linked_by ?? [];
linkedResources = isEmpty(linkedTo) && isEmpty(linkedBy) ? {} : ({ linkedTo, linkedBy });
}
return {
...state,
data: {
...state.data,
maplayers: state.data?.maplayers?.map(layer => {
if (layer.dataset?.pk === parseInt(pk, 10)) {
return {
...layer,
dataset: {
...layer.dataset,
...newData,
linkedResources
}
};
}
return layer;
})
}
};
default:
return state;
}
Expand Down
12 changes: 12 additions & 0 deletions geonode_mapstore_client/client/js/selectors/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ export const getResourceData = (state) => {
return state?.gnresource?.data;
};

export const getLayerResourceData = (state) => {
return state?.gnresource?.layerDataset;
};

export const getSelectedLayer = (state) => {
return state?.gnresource?.selectedLayer;
};

export const getSelectedLayerDataset = (state) => {
return state?.gnresource?.selectedLayerDataset;
};

export const getCompactPermissions = (state) => {
const compactPermissions = state?.gnresource?.compactPermissions || {};
return compactPermissions;
Expand Down
Loading
Loading