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

geosolutions-it / MapStore2 / 19735587487

27 Nov 2025 09:59AM UTC coverage: 76.667% (-0.3%) from 76.929%
19735587487

Pull #11119

github

web-flow
Fix: #11712 Support for template format on vector layers to visualize embedded conent (#11720)
Pull Request #11119: Layer Selection Plugin on ArcGIS, WFS & WMS layers

32268 of 50209 branches covered (64.27%)

7 of 13 new or added lines in 2 files covered. (53.85%)

3017 existing lines in 248 files now uncovered.

40158 of 52380 relevant lines covered (76.67%)

37.8 hits per line

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

72.83
/web/client/components/data/query/GeometryDetails.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
import React from 'react';
9

10
import PropTypes from 'prop-types';
11
import { Row, Col } from 'react-bootstrap';
12
import SwitchPanel from '../../misc/switch/SwitchPanel';
13
import I18N from '../../I18N/I18N';
14
import { reprojectBbox, reproject, getUnits } from '../../../utils/CoordinatesUtils';
15
import IntlNumberFormControl from '../../I18N/IntlNumberFormControl';
16

17

18
class GeometryDetails extends React.Component {
19
    static propTypes = {
1✔
20
        useMapProjection: PropTypes.bool,
21
        geometry: PropTypes.object,
22
        type: PropTypes.string,
23
        onShowPanel: PropTypes.func,
24
        onChangeDrawingStatus: PropTypes.func,
25
        zoom: PropTypes.number,
26
        projection: PropTypes.string,
27
        enableGeodesic: PropTypes.bool
28
    };
29

30
    static defaultProps = {
1✔
31
        useMapProjection: true,
32
        geometry: null,
33
        type: null,
34
        onShowPanel: () => {},
35
        onChangeDrawingStatus: () => {}
36
    };
37

38
    componentDidMount() {
39

40
        const geometry = this.props.geometry;
4✔
41

42
        if (this.props.type === "BBOX") {
4✔
43

44
            this.extent = this.getBBOXDimensions(geometry);
1✔
45
            this.tempExtent = Object.assign({}, this.extent);
1✔
46

47
        } else if (this.props.type === "Circle") {
3!
48

49
            this.circle = this.getCircleDimensions(geometry);
3✔
50
            this.tempCircle = Object.assign({}, this.circle);
3✔
51

52
        }
53
    }
54

55
    onUpdateBBOX = (value, name, drawStatus = 'replace') => {
4✔
56
        if (drawStatus === 'replace') {
4!
57
            this.tempExtent[name] = !isNaN(parseFloat(value)) && parseFloat(value) || 0;
4!
58
        }
59
        let coordinates = [];
4✔
60
        for (let prop in this.tempExtent) {
4✔
61
            if (prop) {
16!
62
                coordinates.push(this.tempExtent[prop]);
16✔
63
            }
64
        }
65

66
        let bbox = reprojectBbox(coordinates, 'EPSG:4326', this.props.projection);
4✔
67

68
        let geometry = {
4✔
69
            type: this.props.geometry.type,
70
            coordinates: [[
71
                [bbox[0], bbox[1]],
72
                [bbox[0], bbox[3]],
73
                [bbox[2], bbox[3]],
74
                [bbox[2], bbox[1]],
75
                [bbox[0], bbox[1]]
76
            ]],
77
            projection: this.props.geometry.projection
78
        };
79

80
        this.props.onChangeDrawingStatus(drawStatus, undefined, "queryform", [geometry]);
4✔
81
    };
82

83
    onUpdateCircle = (value, name, drawStatus = 'replace') => {
4!
84
        if (drawStatus === 'replace') {
2!
UNCOV
85
            this.tempCircle[name] = parseFloat(value);
×
86
        }
87
        let center = !isNaN(parseFloat(this.tempCircle.x)) && !isNaN(parseFloat(this.tempCircle.y)) ?
2!
88
            reproject([this.tempCircle.x, this.tempCircle.y], 'EPSG:4326', this.props.projection) : [this.tempCircle.x, this.tempCircle.y];
89

90
        center = center.x === undefined ? {x: center[0], y: center[1]} : center;
2!
91
        const validateCenter = {x: !isNaN(center.x) ? center.x : 0, y: !isNaN(center.y) ? center.y : 0};
2!
92

93
        let geometry = {
2✔
94
            type: this.props.geometry.type,
95
            center: validateCenter,
96
            coordinates: [validateCenter.x, validateCenter.y],
97
            radius: !isNaN(this.tempCircle.radius) ? this.tempCircle.radius : 0,
2!
98
            projection: this.props.geometry.projection
99
        };
100

101

102
        this.props.onChangeDrawingStatus(drawStatus, undefined, "queryform", [geometry], {geodesic: this.props.enableGeodesic});
2✔
103
    };
104

105
    onModifyGeometry = () => {
4✔
106
        if (this.props.type === "BBOX") {
2!
UNCOV
107
            this.onUpdateBBOX(null, null, 'endDrawing');
×
108
        } else if (this.props.type === "Circle") {
2!
109
            this.onUpdateCircle(null, null, 'endDrawing');
2✔
110
        }
111
        this.props.onShowPanel(false);
2✔
112
    };
113

114
    onClosePanel = () => {
4✔
UNCOV
115
        this.resetGeom();
×
116
        this.props.onShowPanel(false);
×
117
    };
118
    getStep = (zoom = 1) => Math.min(1 / Math.pow(10, Math.ceil(Math.min(zoom, 21) / 3) - 2), 1);
13✔
119
    getStepCircle = (zoom, name) => {
4✔
120
        const step = this.getStep(zoom);
9✔
121
        return (name === 'radius' && !this.isWGS84() && step * 10000) || step;
9✔
122
    };
123
    getBBOXDimensions = (geometry) => {
4✔
124
        const extent =  reprojectBbox(geometry.extent, geometry.projection, 'EPSG:4326');
2✔
125

126
        return {
2✔
127
            // minx
128
            west: extent[0],
129
            // miny
130
            sud: extent[1],
131
            // maxx
132
            est: extent[2],
133
            // maxy
134
            north: extent[3]
135
        };
136
    };
137
    getCircleDimensions = (geometry) => {
4✔
138
        // Show the center coordinates in 4326
139
        const center = reproject(geometry.center, geometry.projection, 'EPSG:4326');
6✔
140
        const centerReproject = reproject(geometry.center, geometry.projection, this.props.projection);
6✔
141
        const radiusEnd = reproject([geometry.center[0] + geometry.radius, geometry.center[1]], geometry.projection, this.props.projection);
6✔
142
        const radius = Math.sqrt((radiusEnd.x - centerReproject.x) * (radiusEnd.x - centerReproject.x) +
6✔
143
            (radiusEnd.y - centerReproject.y) * (radiusEnd.y - centerReproject.y));
144

145
        return {
6✔
146
            x: center.x,
147
            y: center.y,
148
            radius: radius
149
        };
150
    };
151
    renderCoordinateField = (value, name) => {
4✔
152
        return (
4✔
153
            <div>
154
                <div className="detail-field-title">{name}</div>
155
                <IntlNumberFormControl
156
                    style={{minWidth: '105px', margin: 'auto'}}
157
                    type="number"
158
                    id={"queryform_bbox_" + name}
159
                    step={this.getStep(this.props.zoom)}
160
                    defaultValue={this.roundValue(value, 1000000)}
161
                    onChange={(val) => this.onUpdateBBOX(val, name)}/>
4✔
162
            </div>
163
        );
164
    };
165
    renderCircleField = (value, name) => {
4✔
166
        // radius should have 2 decimals if uom of projections is meter (EPSG:3857)
167
        // all other cases it must have at least 6 decimals because coords are converted to EPSG:4326
168
        return (
9✔
169
            <IntlNumberFormControl
170
                type="number"
171
                id={"queryform_circle_" + name}
172
                defaultValue={this.roundValue(value, name === 'radius' && !this.isWGS84() ? 100 : 1000000)}
21✔
173
                step={this.getStepCircle(this.props.zoom, name)}
UNCOV
174
                onChange={(val) => this.onUpdateCircle(val, name)}/>
×
175
        );
176
    };
177

178
    renderDetailsContent = () => {
4✔
179
        let detailsContent;
180
        let geometry = this.props.geometry;
4✔
181

182
        if (this.props.type === "BBOX") {
4✔
183

184
            const extent = this.getBBOXDimensions(geometry);
1✔
185

186
            detailsContent =
1✔
187
                (<div>
188
                    <div className="container-fluid">
189
                        <Row>
190
                            <Col xs={4}>
191
                                <span/>
192
                            </Col>
193
                            <Col xs={4}>
194
                                {this.renderCoordinateField(extent.north, "north")}
195
                            </Col>
196
                            <Col xs={4}>
197
                                <span/>
198
                            </Col>
199
                        </Row>
200
                        <Row>
201
                            <Col xs={4}>
202
                                {this.renderCoordinateField(extent.west, "west")}
203
                            </Col>
204
                            <Col xs={4}>
205
                                <span/>
206
                            </Col>
207
                            <Col xs={4}>
208
                                {this.renderCoordinateField(extent.est, "est")}
209
                            </Col>
210
                        </Row>
211
                        <Row>
212
                            <Col xs={4}>
213
                                <span/>
214
                            </Col>
215
                            <Col xs={4}>
216
                                {this.renderCoordinateField(extent.sud, "sud")}
217
                            </Col>
218
                            <Col xs={4}>
219
                                <span/>
220
                            </Col>
221
                        </Row>
222
                    </div>
223
                    <span>
224
                        <hr width="90%"/>
225
                        <div ><h5><I18N.Message msgId={"queryform.spatialfilter.details.details_bbox_label"}/></h5></div>
226
                    </span>
227
                </div>)
228
            ;
229
        } else if (this.props.type === "Circle") {
3!
230
            const circle = this.getCircleDimensions(geometry);
3✔
231
            const uom = getUnits(this.props.projection);
3✔
232
            detailsContent =
3✔
233
                (<div>
234
                    <div className="container-fluid">
235
                        <Row>
236
                            <Col xs={2}>
237
                                <span/>
238
                            </Col>
239
                            <Col xs={2}>
240
                                <span className="details-circle-attribute-name">{'x:'}</span>
241
                            </Col>
242
                            <Col xs={4}>
243
                                {this.renderCircleField(circle.x, "x")}
244
                            </Col>
245
                            <Col xs={4}>
246
                                <span/>
247
                            </Col>
248
                        </Row>
249
                        <Row>
250
                            <Col xs={2}>
251
                                <span/>
252
                            </Col>
253
                            <Col xs={2}>
254
                                <span className="details-circle-attribute-name">{'y:'}</span>
255
                            </Col>
256
                            <Col xs={4}>
257
                                {this.renderCircleField(circle.y, "y")}
258
                            </Col>
259
                            <Col xs={4}>
260
                                <span/>
261
                            </Col>
262
                        </Row>
263
                        <Row>
264
                            <Col xs={2}>
265
                                <span/>
266
                            </Col>
267
                            <Col xs={2}>
268
                                <span className="details-circle-attribute-name">
269
                                    <I18N.Message
270
                                        msgId="queryform.spatialfilter.details.radius"
271
                                        msgParams={{unit: uom === "degrees" ? "°" : uom }}/>{':'}
3✔
272
                                </span>
273
                            </Col>
274
                            <Col xs={4}>
275
                                {this.renderCircleField(circle.radius, "radius")}
276
                            </Col>
277
                            <Col xs={4}>
278
                                <span/>
279
                            </Col>
280
                        </Row>
281
                    </div>
282
                    <span>
283
                        <hr width="90%"/>
284
                        <div><h5><I18N.Message msgId={"queryform.spatialfilter.details.details_circle_label"}/></h5></div>
285
                    </span>
286
                </div>)
287
            ;
288
        }
289

290
        return detailsContent;
4✔
291
    };
292

293
    render() {
294
        return (
4✔
295
            <SwitchPanel buttons={[{
296
                key: 'confirm',
297
                glyph: 'ok',
298
                tooltipId: 'confirm',
299
                onClick: () => this.onModifyGeometry()
2✔
300
            }, {
301
                key: 'reset',
302
                tooltipId: 'queryform.reset',
303
                glyph: 'clear-filter',
UNCOV
304
                onClick: () => this.resetGeom()
×
305
            }, {
306
                key: 'close',
307
                glyph: '1-close',
UNCOV
308
                onClick: () => this.onClosePanel(false)
×
309
            }]} title={<I18N.Message msgId={"queryform.spatialfilter.details.details_header"}/>} locked expanded className="details-panel" bsStyle="primary">
310
                {this.renderDetailsContent()}
311
            </SwitchPanel>
312
        );
313
    }
314
    isWGS84 = () => this.props.projection === 'EPSG:4326';
6✔
315
    roundValue = (val, prec = 1000000) => Math.round(val * prec) / prec;
13!
316
    resetGeom = () => {
4✔
UNCOV
317
        if (this.props.type === "BBOX") {
×
318
            this.resetBBOX();
×
319
        } else if (this.props.type === "Circle") {
×
320
            this.resetCircle();
×
321
        }
322
    };
323
    resetBBOX = () => {
4✔
UNCOV
324
        for (let prop in this.extent) {
×
325
            if (prop) {
×
326
                let coordinateInput = document.getElementById("queryform_bbox_" + prop);
×
327
                coordinateInput.value = this.roundValue(this.extent[prop], 1000000);
×
328
                this.onUpdateBBOX(this.extent[prop], prop);
×
329
            }
330
        }
331
    };
332

333
    resetCircle = () => {
4✔
UNCOV
334
        let radiusInput = document.getElementById("queryform_circle_radius");
×
335
        radiusInput.value = this.roundValue(this.circle.radius, 100);
×
336
        this.onUpdateCircle(this.circle.radius, "radius");
×
337

UNCOV
338
        let coordinateXInput = document.getElementById("queryform_circle_x");
×
339
        coordinateXInput.value = this.roundValue(this.circle.x, !this.isWGS84() ? 100 : 1000000);
×
340
        this.onUpdateCircle(this.circle.x, "x");
×
341

UNCOV
342
        let coordinateYInput = document.getElementById("queryform_circle_y");
×
343
        coordinateYInput.value = this.roundValue(this.circle.y, !this.isWGS84() ? 100 : 1000000);
×
344
        this.onUpdateCircle(this.circle.y, "y");
×
345
    };
346
}
347

348
export default GeometryDetails;
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