Skip to content

Commit ef2b38f

Browse files
committed
GeoNode#1985: Allow configuring dataset layer settings and remove custom style editor
1 parent c2229f3 commit ef2b38f

34 files changed

+477
-867
lines changed

geonode_mapstore_client/apps.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,8 @@ def run_setup_hooks(*args, **kwargs):
250250
"ptype",
251251
"store",
252252
"has_time",
253-
"attribute_set"
253+
"attribute_set",
254+
"data"
254255
]
255256
}
256257
settings.PROXY_ALLOWED_PARAMS_NEEDLES += (

geonode_mapstore_client/client/js/actions/gnresource.js

+8
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const SET_DEFAULT_VIEWER_PLUGINS = 'GEONODE:SET_DEFAULT_VIEWER_PLUGINS';
4242
export const SET_SELECTED_LAYER = 'GEONODE:SET_SELECTED_LAYER';
4343
export const UPDATE_LAYER_DATASET = 'GEONODE:UPDATE_LAYER_DATASET';
4444
export const SET_SELECTED_LAYER_DATASET = 'GEONODE:SET_SELECTED_LAYER_DATASET';
45+
export const UPDATE_DATASET_LAYER_SETTINGS = 'GEONODE:UPDATE_DATASET_LAYER_SETTINGS';
4546

4647
/**
4748
* Actions for GeoNode resource
@@ -394,3 +395,10 @@ export function setLayerDataset(layerId) {
394395
layerId
395396
};
396397
}
398+
399+
400+
export function applyLayerSettings() {
401+
return {
402+
type: UPDATE_DATASET_LAYER_SETTINGS
403+
};
404+
}

geonode_mapstore_client/client/js/epics/__tests__/gnresource-test.js

+83-1
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import {
1414
gnViewerSetNewResourceThumbnail,
1515
closeInfoPanelOnMapClick,
1616
closeDatasetCatalogPanel,
17-
gnZoomToFitBounds
17+
gnZoomToFitBounds,
18+
gnUpdateLayerSettings
1819
} from '@js/epics/gnresource';
1920
import {
21+
applyLayerSettings,
2022
setResourceThumbnail,
2123
UPDATE_RESOURCE_PROPERTIES,
2224
UPDATE_SINGLE_RESOURCE
@@ -196,4 +198,84 @@ describe('gnresource epics', () => {
196198
);
197199

198200
});
201+
it('gnUpdateLayerSettings when update results in error', (done) => {
202+
const NUM_ACTIONS = 4;
203+
const testState = {};
204+
testEpic(gnUpdateLayerSettings,
205+
NUM_ACTIONS,
206+
applyLayerSettings(),
207+
(actions) => {
208+
try {
209+
expect(actions.length).toBe(4);
210+
expect(actions.map(({ type }) => type))
211+
.toEqual([
212+
'GEONODE:SAVING_RESOURCE',
213+
'SHOW_NOTIFICATION',
214+
'GEONODE:SAVE_ERROR',
215+
'GEONODE:SAVE_SUCCESS' // loading ends
216+
]);
217+
} catch (e) {
218+
done(e);
219+
}
220+
done();
221+
},
222+
testState
223+
);
224+
});
225+
it('gnUpdateLayerSettings when update results in success', (done) => {
226+
const NUM_ACTIONS = 4;
227+
const testState = {
228+
gnresource: {
229+
id: 1,
230+
data: {
231+
layerSettings: {
232+
opacity: 0.9
233+
}
234+
}
235+
},
236+
layers: {
237+
flat: [{
238+
id: 'layer001',
239+
name: 'layer001',
240+
settings: {
241+
opacity: 0.8
242+
},
243+
pk: "1"
244+
}],
245+
settings: {
246+
node: 'layer001',
247+
opacity: 0.8
248+
}
249+
}
250+
};
251+
mockAxios.onPatch(new RegExp(`/api/v2/datasets/1`))
252+
.reply(() => [200, {
253+
dataset: {
254+
data: {
255+
opacity: 0.8
256+
}
257+
}
258+
}]);
259+
testEpic(gnUpdateLayerSettings,
260+
NUM_ACTIONS,
261+
applyLayerSettings(),
262+
(actions) => {
263+
try {
264+
expect(actions.length).toBe(4);
265+
expect(actions.map(({ type }) => type))
266+
.toEqual([
267+
'GEONODE:SAVING_RESOURCE',
268+
'GEONODE:UPDATE_RESOURCE_PROPERTIES',
269+
'SHOW_NOTIFICATION',
270+
'GEONODE:SAVE_SUCCESS'
271+
]);
272+
expect(actions[1].properties).toEqual({ layerSettings: { opacity: 0.8 } });
273+
} catch (e) {
274+
done(e);
275+
}
276+
done();
277+
},
278+
testState
279+
);
280+
});
199281
});

geonode_mapstore_client/client/js/epics/gnresource.js

+62-11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import axios from '@mapstore/framework/libs/ajax';
1111
import uuid from "uuid";
1212
import url from "url";
1313
import get from 'lodash/get';
14+
import pick from 'lodash/pick';
1415
import {
1516
getNewMapConfiguration,
1617
getNewGeoStoryConfig,
@@ -26,16 +27,18 @@ import {
2627
setResourceThumbnail,
2728
setLinkedResourcesByPk,
2829
removeLinkedResourcesByPk,
29-
getDatasetTimeSettingsByPk
30+
getDatasetTimeSettingsByPk,
31+
updateDataset
3032
} from '@js/api/geonode/v2';
3133
import { configureMap } from '@mapstore/framework/actions/config';
3234
import { mapSelector } from '@mapstore/framework/selectors/map';
3335
import { isMapInfoOpen } from '@mapstore/framework/selectors/mapInfo';
34-
import { getSelectedLayer } from '@mapstore/framework/selectors/layers';
36+
import { getSelectedLayer, layerSettingSelector, getLayerFromId } from '@mapstore/framework/selectors/layers';
3537
import { isLoggedIn } from '@mapstore/framework/selectors/security';
3638
import {
3739
browseData,
38-
selectNode
40+
selectNode,
41+
showSettings
3942
} from '@mapstore/framework/actions/layers';
4043
import {
4144
updateStatus,
@@ -57,7 +60,8 @@ import {
5760
updateResource,
5861
setResourcePathParameters,
5962
MANAGE_LINKED_RESOURCE,
60-
setMapViewerLinkedResource
63+
setMapViewerLinkedResource,
64+
UPDATE_DATASET_LAYER_SETTINGS
6165
} from '@js/actions/gnresource';
6266

6367
import {
@@ -95,7 +99,7 @@ import { styleServiceSelector } from '@mapstore/framework/selectors/styleeditor'
9599
import { updateStyleService } from '@mapstore/framework/api/StyleEditor';
96100
import { CLICK_ON_MAP, resizeMap, CHANGE_MAP_VIEW, zoomToExtent } from '@mapstore/framework/actions/map';
97101
import { purgeMapInfoResults, closeIdentify, NEW_MAPINFO_REQUEST } from '@mapstore/framework/actions/mapInfo';
98-
import { saveError } from '@js/actions/gnsave';
102+
import { saveError, saveSuccess, savingResource } from '@js/actions/gnsave';
99103
import {
100104
error as errorNotification,
101105
success as successNotification,
@@ -145,9 +149,10 @@ const resourceTypes = {
145149
.then((response) => {
146150
const [mapConfig, gnLayer, timeseries] = response;
147151
const newLayer = resourceToLayerConfig(gnLayer);
152+
const _gnLayer = {...gnLayer, layerSettings: gnLayer.data};
148153

149-
if (!newLayer?.extendedParams?.defaultStyle || page !== 'dataset_edit_style_viewer') {
150-
return [mapConfig, {...gnLayer, timeseries}, newLayer];
154+
if (!newLayer?.extendedParams?.defaultStyle || page !== 'dataset_edit_layer_settings') {
155+
return [mapConfig, {..._gnLayer, timeseries}, newLayer];
151156
}
152157

153158
return getStyleProperties({
@@ -156,7 +161,7 @@ const resourceTypes = {
156161
}).then((updatedStyle) => {
157162
return [
158163
mapConfig,
159-
{...gnLayer, timeseries},
164+
{..._gnLayer, timeseries},
160165
{
161166
...newLayer,
162167
availableStyles: [{
@@ -204,9 +209,9 @@ const resourceTypes = {
204209
browseData(newLayer)
205210
]
206211
: []),
207-
...(page === 'dataset_edit_style_viewer'
212+
...(page === 'dataset_edit_layer_settings'
208213
? [
209-
setControlProperty('visualStyleEditor', 'enabled', true),
214+
showSettings(newLayer.id, "layers", {opacity: newLayer.opacity ?? 1}),
210215
updateAdditionalLayer(newLayer.id, STYLE_OWNER_NAME, 'override', {}),
211216
updateStatus('edit'),
212217
resizeMap()
@@ -711,6 +716,51 @@ export const gnZoomToFitBounds = (action$) =>
711716
})
712717
);
713718

719+
export const gnUpdateLayerSettings = (action$, { getState = () => {} } = {}) =>
720+
action$.ofType(UPDATE_DATASET_LAYER_SETTINGS)
721+
.switchMap(() => {
722+
const state = getState();
723+
const {node, options} = layerSettingSelector(state) ?? {};
724+
const layer = getLayerFromId(state, node);
725+
const {layerSettings} = getResourceData(state) ?? {};
726+
const initialData = state?.gnresource?.initialResource?.data ?? {};
727+
const settings = {
728+
...initialData,
729+
...layerSettings,
730+
...options,
731+
...pick(layer, ['fields'])
732+
};
733+
const body = {...(settings.title && {title: settings.title}), data: settings};
734+
return Observable.defer(() =>
735+
updateDataset(layer.pk, body)
736+
)
737+
.switchMap((res) => {
738+
const {data, ...response} = res ?? {};
739+
const gnLayer = {...response, layerSettings: data};
740+
return Observable.of(
741+
updateResourceProperties(gnLayer),
742+
successNotification({
743+
title: "gnviewer.layerSettings.title",
744+
message: 'gnviewer.layerSettings.updateSuccess'
745+
})
746+
);
747+
})
748+
.let(
749+
wrapStartStop(
750+
savingResource(true),
751+
saveSuccess(),
752+
(error) => {
753+
return Observable.of(
754+
errorNotification({
755+
title: "gnviewer.layerSettings.title",
756+
message: 'gnviewer.layerSettings.updateError'
757+
}),
758+
saveError(error)
759+
);
760+
}
761+
)
762+
);
763+
});
714764
export default {
715765
gnViewerRequestNewResourceConfig,
716766
gnViewerRequestResourceConfig,
@@ -719,5 +769,6 @@ export default {
719769
closeOpenPanels,
720770
closeDatasetCatalogPanel,
721771
gnManageLinkedResource,
722-
gnZoomToFitBounds
772+
gnZoomToFitBounds,
773+
gnUpdateLayerSettings
723774
};

geonode_mapstore_client/client/js/plugins/Save.jsx

+31-7
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ import { saveDirectContent } from '@js/actions/gnsave';
2424
import {
2525
isNewResource,
2626
canEditResource,
27-
getResourceDirtyState
27+
getResourceDirtyState,
28+
getLayerSettingsDirtyState
2829
} from '@js/selectors/resource';
2930
import { getCurrentResourcePermissionsLoading } from '@js/selectors/resourceservice';
3031
import { withRouter } from 'react-router';
3132
import withPrompt from '@js/plugins/save/withPrompt';
33+
import { applyLayerSettings } from '@js/actions/gnresource';
3234

3335
function Save(props) {
3436
return props.saving ? (<div
@@ -54,7 +56,8 @@ function SaveButton({
5456
size,
5557
loading,
5658
className,
57-
dirtyState: dirtyStateProp
59+
dirtyState: dirtyStateProp,
60+
saveMsgId = "gnviewer.save"
5861
}) {
5962
return (
6063
<Button
@@ -64,7 +67,7 @@ function SaveButton({
6467
disabled={loading}
6568
className={className}
6669
>
67-
<Message msgId="save"/>{' '}{loading && <Spinner />}
70+
<Message msgId={saveMsgId}/>{' '}{loading && <Spinner />}
6871
</Button>
6972
);
7073
}
@@ -90,13 +93,34 @@ const ConnectedSaveButton = connect(
9093
}
9194
)((withRouter(withPrompt(SaveButton))));
9295

96+
const ConnectedApplyButton = connect(
97+
createSelector(
98+
state => state?.gnsave?.saving,
99+
state => getLayerSettingsDirtyState(state),
100+
(loading, dirtyState) => ({
101+
loading,
102+
dirtyState,
103+
saveMsgId: "gnviewer.apply",
104+
className: 'gn-apply-settings'
105+
})
106+
),
107+
{
108+
onClick: applyLayerSettings
109+
}
110+
)(SaveButton);
111+
93112
export default createPlugin('Save', {
94113
component: SavePlugin,
95114
containers: {
96-
ActionNavbar: {
97-
name: 'Save',
98-
Component: ConnectedSaveButton
99-
}
115+
ActionNavbar: [
116+
{
117+
name: 'Save',
118+
Component: ConnectedSaveButton
119+
}, {
120+
name: 'ApplyLayerSettings',
121+
Component: ConnectedApplyButton
122+
}
123+
]
100124
},
101125
epics: {
102126
...gnsaveEpics

0 commit comments

Comments
 (0)