• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geosolutions-it / MapStore2 / 12767533386

14 Jan 2025 12:22PM UTC coverage: 77.12% (+0.4%) from 76.716%
12767533386

Pull #10609

github

web-flow
Merge 372554eeb into 779416da2
Pull Request #10609: Normalize layers/groups management in TOC #10247

30300 of 47064 branches covered (64.38%)

6 of 6 new or added lines in 1 file covered. (100.0%)

522 existing lines in 35 files now uncovered.

37639 of 48806 relevant lines covered (77.12%)

34.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/web/client/plugins/Identify.jsx
1
/*
2
 * Copyright 2016, GeoSolutions Sas.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under the BSD-style license found in the
6
 * LICENSE file in the root directory of this source tree.
7
 */
8

9
import './identify/identify.css';
10

11
import { isUndefined } from 'lodash';
12
import assign from 'object-assign';
13
import React from 'react';
14
import { Glyphicon } from 'react-bootstrap';
15
import { connect } from 'react-redux';
16
import { compose, defaultProps } from 'recompose';
17
import { createSelector, createStructuredSelector } from 'reselect';
18

19
import { changeMousePointer, zoomToExtent } from '../actions/map';
20
import {
21
    changeFormat,
22
    changeMapInfoFormat,
23
    changePage,
24
    clearWarning,
25
    setShowInMapPopup,
26
    closeIdentify,
27
    editLayerFeatures,
28
    hideMapinfoMarker,
29
    hideMapinfoRevGeocode,
30
    purgeMapInfoResults,
31
    setMapTrigger,
32
    showMapinfoRevGeocode,
33
    toggleHighlightFeature,
34
    toggleMapInfoState,
35
    toggleShowCoordinateEditor,
36
    updateCenterToMarker,
37
    updateFeatureInfoClickPoint,
38
    checkIdentifyIsMounted,
39
    onInitPlugin
40
} from '../actions/mapInfo';
41
import { enableHideEmptyPopupOption } from '../actions/mapPopups';
42
import DefaultViewerComp from '../components/data/identify/DefaultViewer';
43
import { defaultViewerDefaultProps, defaultViewerHandlers } from '../components/data/identify/enhancers/defaultViewer';
44
import { identifyLifecycle } from '../components/data/identify/enhancers/identify';
45
import zoomToFeatureHandler from '../components/data/identify/enhancers/zoomToFeatureHandler';
46
import IdentifyContainer from '../components/data/identify/IdentifyContainer';
47
import loadingState from '../components/misc/enhancers/loadingState';
48
import FeatureInfoFormatSelectorComp from '../components/misc/FeatureInfoFormatSelector';
49
import FeatureInfoTriggerSelectorComp from '../components/misc/FeatureInfoTriggerSelector';
50
import epics from '../epics/identify';
51
import mapInfo from '../reducers/mapInfo';
52
import mapPopups from '../reducers/mapPopups';
53
import { isEditingAllowedSelector } from '../selectors/featuregrid';
54
import { layersSelector } from '../selectors/layers';
55
import { currentLocaleSelector } from '../selectors/locale';
56
import { isMouseMoveIdentifyActiveSelector, mapSelector } from '../selectors/map';
57
import {
58
    clickPointSelector,
59
    currentFeatureCrsSelector,
60
    currentFeatureSelector,
61
    generalInfoFormatSelector,
62
    indexSelector,
63
    isHighlightEnabledSelector,
64
    isLoadedResponseSelector,
65
    requestsSelector,
66
    responsesSelector,
67
    showEmptyMessageGFISelector,
68
    validResponsesSelector,
69
    hoverEnabledSelector,
70
    mapInfoEnabledSelector
71
} from '../selectors/mapInfo';
72
import { mapLayoutValuesSelector } from '../selectors/maplayout';
73
import { isCesium, mapTypeSelector } from '../selectors/maptype';
74
import ConfigUtils from '../utils/ConfigUtils';
75
import { getDefaultInfoFormatValue, getValidator } from '../utils/MapInfoUtils';
76
import getFeatureButtons from './identify/featureButtons';
77
import getToolButtons from './identify/toolButtons';
78
import Message from './locale/Message';
79

80
const selector = createStructuredSelector({
1✔
81
    enabled: (state) => mapInfoEnabledSelector(state) || state.controls && state.controls.info && state.controls.info.enabled || false,
2!
82
    responses: responsesSelector,
83
    validResponses: validResponsesSelector,
84
    requests: requestsSelector,
85
    format: generalInfoFormatSelector,
86
    map: mapSelector,
87
    layers: layersSelector,
88
    point: clickPointSelector,
89
    showModalReverse: (state) => state.mapInfo && state.mapInfo.showModalReverse,
2✔
90
    reverseGeocodeData: (state) => state.mapInfo && state.mapInfo.reverseGeocodeData,
2✔
91
    warning: (state) => state.mapInfo && state.mapInfo.warning,
2✔
92
    currentLocale: currentLocaleSelector,
93
    dockStyle: (state) => mapLayoutValuesSelector(state, { height: true, right: true }, true),
2✔
94
    formatCoord: (state) => state.mapInfo && state.mapInfo.formatCoord || ConfigUtils.getConfigProp('defaultCoordinateFormat'),
2✔
95
    showCoordinateEditor: (state) => state.mapInfo && state.mapInfo.showCoordinateEditor,
2✔
96
    showEmptyMessageGFI: state => showEmptyMessageGFISelector(state),
2✔
97
    isEditingAllowed: isEditingAllowedSelector,
98
    isCesium,
99
    floatingIdentifyEnabled: (state) => isMouseMoveIdentifyActiveSelector(state)
2✔
100
});
101
// result panel
102

103
/*
104
 * Enhancer to enable set index only if Component has not header in viewerOptions props
105
 */
106
const identifyIndex = compose(
1✔
107
    connect(
108
        createSelector(indexSelector, isLoadedResponseSelector, (state) => state.browser && state.browser.mobile,  (index, loaded, isMobile) => ({ index, loaded, isMobile })),
2!
109
        {
110
            setIndex: changePage
111
        }
112
    )
113
);
114
const DefaultViewer = compose(
1✔
115
    identifyIndex,
116
    defaultViewerDefaultProps,
117
    defaultViewerHandlers,
UNCOV
118
    loadingState(({ loaded }) => isUndefined(loaded))
×
119
)(DefaultViewerComp);
120

121

122
const identifyDefaultProps = defaultProps({
1✔
123
    formatCoord: "decimal",
124
    enabled: false,
125
    draggable: true,
126
    collapsible: false,
127
    format: getDefaultInfoFormatValue(),
128
    requests: [],
129
    responses: [],
130
    viewerOptions: {},
131
    viewer: DefaultViewer,
132
    purgeResults: () => { },
133
    hideMarker: () => { },
134
    clearWarning: () => { },
135
    changeMousePointer: () => { },
136
    showRevGeocode: () => { },
137
    checkIdentifyIsMounted: () => {},
138
    hideRevGeocode: () => { },
139
    containerProps: {
140
        continuous: false
141
    },
142
    enabledCoordEditorButton: true,
143
    showCoordinateEditor: false,
144
    showModalReverse: false,
145
    reverseGeocodeData: {},
146
    enableRevGeocode: true,
147
    wrapRevGeocode: false,
148
    style: {},
149
    point: {},
150
    layer: null,
151
    map: {},
152
    layers: [],
153
    panelClassName: "modal-dialog info-panel modal-content",
154
    headerClassName: "modal-header",
155
    bodyClassName: "modal-body info-wrap",
156
    dock: true,
157
    headerGlyph: "",
158
    closeGlyph: "1-close",
159
    className: "square-button",
160
    currentLocale: 'en-US',
161
    fullscreen: false,
162
    showTabs: true,
163
    showCoords: true,
164
    showLayerTitle: true,
165
    showMoreInfo: true,
166
    showEdit: false,
167
    position: 'right',
168
    size: 550,
169
    getToolButtons,
170
    getFeatureButtons,
171
    showFullscreen: false,
172
    validResponses: [],
173
    validator: getValidator, // TODO: move all validation from the components to the selectors
174
    zIndex: 1050
175
});
176

177
/**
178
 * This plugin allows get information about clicked point. It can be configured to have a mobile or a desktop flavor.
179
 *
180
 * You can configure some of the features of this plugin by setting up the initial mapInfo state, then you need to update the "initialState.defaultState", or by the plugin configuration
181
 * ```
182
 * "mapInfo": {
183
 *   "enabled": true, // enabled by default
184
 *   "disabledAlwaysOn": false, // if true, disable always on setup
185
 *   "configuration": {
186
 *     "showEmptyMessageGFI": false // allow or deny the visibility of message when you have no results from identify request
187
 *     "infoFormat": "text/plain" // default infoformat value, other values are "text/html" for text only or "application/json" for properties
188
 *   }
189
 * }
190
 * ```
191
 *
192
 * @class Identify
193
 * @memberof plugins
194
 * @static
195
 *
196
 * @prop showIn {string[]} List of the plugins where to show the plugin
197
 * @prop cfg.dock {bool} true shows dock panel, false shows modal
198
 * @prop cfg.draggable {boolean} draggable info window, when modal
199
 * @prop cfg.showHighlightFeatureButton {boolean} show the highlight feature button if the interrogation returned valid features (openlayers only)
200
 * @prop cfg.hidePopupIfNoResults {boolean} hide/show the identify popup in case of no results
201
 * @prop cfg.highlightEnabledFromTheStart {boolean} the highlight feature button will be activated by default if true
202
 * @prop cfg.viewerOptions.container {expression} the container of the viewer, expression from the context
203
 * @prop cfg.viewerOptions.header {expression} the header of the viewer, expression from the context{expression}
204
 * @prop cfg.disableCenterToMarker {bool} disable zoom to marker action
205
 * @prop cfg.showAllResponses {bool} if true it will include invalid/empty responses and it will always select the first request/layer
206
 * @prop cfg.zIndex {number} component z index order
207
 * @prop cfg.showInMapPopup {boolean} if true show the identify as popup
208
 * @prop cfg.maxItems {number} the number of features returned by this tool
209
 * @prop cfg.showMoreInfo {boolean} if true shows the more info icon which allow user to show/hide Geocode viewer as popup (true by default)
210
 * @prop cfg.showEdit {boolean} if true, and when the FeatureEditor plugin is present, shows and edit button to edit the current feature(s) clicked in the grid.
211
 * @prop cfg.enableInfoForSelectedLayers {boolean} if true, if some layer is selected in the TOC, the feature info is performed only on the selected ones. if false, the info is queried for all the layers, independently from selection. (default is true).
212
 * @prop cfg.disableCoordinatesRow {boolean} if true the coordinates row is disabled
213
 *
214
 * @example
215
 * {
216
 *   "name": "Identify",
217
 *   "showIn": ["Settings"],
218
 *   "cfg": {
219
 *       "draggable": false,
220
 *       "dock": true,
221
 *       "viewerOptions": {
222
 *          "container": "{context.ReactSwipe}",
223
 *          "header": "{context.SwipeHeader}"
224
 *       }
225
 *    }
226
 * }
227
 *
228
 */
229
const IdentifyPlugin = compose(
1✔
230
    connect(selector, {
231
        onInitPlugin,
232
        purgeResults: purgeMapInfoResults,
233
        closeIdentify,
234
        onSubmitClickPoint: updateFeatureInfoClickPoint,
235
        onToggleShowCoordinateEditor: toggleShowCoordinateEditor,
236
        onChangeFormat: changeFormat,
237
        changeMousePointer,
238
        clearWarning,
239
        hideMarker: hideMapinfoMarker,
240
        showRevGeocode: showMapinfoRevGeocode,
241
        hideRevGeocode: hideMapinfoRevGeocode,
242
        onEnableCenterToMarker: updateCenterToMarker.bind(null, 'enabled'),
243
        onEdit: editLayerFeatures,
244
        checkIdentifyIsMounted
245
    }, (stateProps, dispatchProps, ownProps) => ({
2✔
246
        ...ownProps,
247
        ...stateProps,
248
        ...dispatchProps,
249
        enabled: stateProps.enabled && (stateProps.isCesium || !ownProps.showInMapPopup) && !stateProps.floatingIdentifyEnabled
8✔
250
    })),
251
    // highlight support
252
    compose(
253
        connect(
254
            createStructuredSelector({
255
                highlight: isHighlightEnabledSelector,
256
                currentFeature: currentFeatureSelector,
257
                currentFeatureCrs: currentFeatureCrsSelector
258
            }), {
259
                toggleHighlightFeature,
260
                zoomToExtent
261
            }
262
        ),
263
        zoomToFeatureHandler
264
    ),
265
    // disable with not supported mapTypes. TODO: remove when reproject (leaflet) and features draw available (cesium)
266
    connect(createSelector(mapTypeSelector, mapType => ({mapType})), {}, ({mapType}, _, { showHighlightFeatureButton, ...props }) => ({...props, showHighlightFeatureButton: mapType === 'openlayers' && showHighlightFeatureButton}) ),
2✔
267
    identifyDefaultProps,
268
    identifyIndex,
269
    defaultViewerHandlers,
270
    connect(() => ({}), {
2✔
271
        setShowInMapPopup,
272
        enableHideEmptyPopupOption
273
    }),
274
    identifyLifecycle
275
)(IdentifyContainer);
276

277
// configuration UI
278
const FeatureInfoFormatSelector = connect((state) => ({
1✔
279
    infoFormat: generalInfoFormatSelector(state)
280
}), {
281
    onInfoFormatChange: changeMapInfoFormat
282
})(FeatureInfoFormatSelectorComp);
283

284
const FeatureInfoTriggerSelector = connect((state) => ({
1✔
285
    trigger: isMouseMoveIdentifyActiveSelector(state) ? 'hover' : 'click',
×
286
    hoverEnabled: hoverEnabledSelector(state)
287
}), {
288
    onSetMapTrigger: setMapTrigger,
289
    onPurgeMapInfoResults: purgeMapInfoResults,
290
    onHideMapinfoMarker: hideMapinfoMarker
291
})(FeatureInfoTriggerSelectorComp);
292

293
export default {
294
    IdentifyPlugin: assign(IdentifyPlugin, {
295
        Toolbar: {
296
            name: 'info',
297
            position: 6,
298
            tooltip: "info.tooltip",
299
            icon: <Glyphicon glyph="map-marker" />,
300
            help: <Message msgId="helptexts.infoButton" />,
301
            action: toggleMapInfoState,
UNCOV
302
            selector: (state) => ({
×
303
                bsStyle: state.mapInfo && state.mapInfo.enabled ? "success" : "primary",
×
304
                active: !!(state.mapInfo && state.mapInfo.enabled)
×
305
            })
306
        },
307
        Settings: {
308
            tool: [<FeatureInfoFormatSelector
309
                key="featureinfoformat"
310
                label={<Message msgId="infoFormatLbl" />
311
                } />, <FeatureInfoTriggerSelector
312
                key="featureinfotrigger" />],
313
            position: 3
314
        }
315
    }),
316
    reducers: { mapInfo, mapPopups },
317
    epics
318
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc