Skip to content

Commit

Permalink
Fix #9999 Measures distance and area now supports all operations as g…
Browse files Browse the repository at this point in the history
…eodesics features (#10010)
  • Loading branch information
MV88 committed Mar 7, 2024
1 parent 278a75a commit ff421f5
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 31 deletions.
5 changes: 5 additions & 0 deletions web/client/components/map/openlayers/MeasurementSupport.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,11 @@ export default class MeasurementSupport extends React.Component {
clonedNewFeature = set("geometry.coordinates", newCoords, clonedNewFeature);
} else if (!this.props.measurement.disableLabels && this.props.measurement.areaMeasureEnabled) {
// the one before the last is a dummy
let oldCoords = clonedNewFeature.geometry.coordinates;
let newCoords = transformLineToArcs(oldCoords[0]);
clonedNewFeature = set("geometry.coordinates", [newCoords], clonedNewFeature);

// edit geom for drawing geodesic lines
this.textLabels.splice(this.segmentOverlays.length - 2, 1);
this.props.map.removeOverlay(this.segmentOverlays[this.segmentOverlays.length - 2]);
this.segmentOverlayElements[this.segmentOverlays.length - 2].parentNode.removeChild(
Expand Down
39 changes: 36 additions & 3 deletions web/client/plugins/Annotations/utils/AnnotationsUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@
*/

import uuidv1 from 'uuid/v1';
import { slice, head, last, get, isNaN, isEqual, isNumber } from 'lodash';
import { slice, omit, head, last, get, isNaN, isEqual, isNumber } from 'lodash';
import turfBbox from '@turf/bbox';
import { measureIcons } from '../../../utils/MeasureUtils';
import {
MeasureTypes,
measureIcons
} from '../../../utils/MeasureUtils';
import {
getMeasureType
} from '../../../utils/MeasurementUtils';

import {
transformLineToArcs
} from '../../../utils/CoordinatesUtils';

// legacy style
export const STYLE_CIRCLE = {
Expand Down Expand Up @@ -405,6 +415,9 @@ export const updateAnnotationsLayer = (layer = {}) => {
}
return [];
};

export const isGeodesicMeasure = (measureType) => [MeasureTypes.LENGTH, MeasureTypes.AREA].includes(measureType);

/**
* This function takes an annotations layer and converts it to valid GeoJSON Feature Collection usable for export
* @param {array} annotations annotations layers
Expand All @@ -415,10 +428,21 @@ export const annotationsToGeoJSON = (annotations) => {
return [
...acc,
...(annotation.features || []).map((feature) => {
const measureType = getMeasureType(feature);
const isGeodesic = isGeodesicMeasure(measureType);
let newGeom = feature.geometry;
if (isGeodesic) {
newGeom = {
type: feature.geometry.type,
coordinates: measureType === MeasureTypes.LENGTH ? transformLineToArcs(feature.geometry.coordinates) : feature.geometry.coordinates.map(transformLineToArcs)
};
}
return {
...feature,
geometry: newGeom,
properties: {
...feature.properties,
...(isGeodesic ? {originalGeom: feature.geometry} : {}),
annotationLayerId: annotation.id,
annotationLayerTitle: annotation.title,
annotationLayerDescription: annotation.description
Expand Down Expand Up @@ -452,7 +476,16 @@ export const geoJSONToAnnotations = (json) => {
const layers = (json?.annotations || [])
.map((annotation) => {
const features = (json?.features || [])
.filter((feature) => feature?.properties?.annotationLayerId === annotation.id);
.filter((feature) => feature?.properties?.annotationLayerId === annotation.id)
.map(feature => {
const measureType = getMeasureType(feature);
const isGeodesic = isGeodesicMeasure(measureType);
return isGeodesic && feature.properties.originalGeom ? {
...feature,
geometry: feature.properties.originalGeom,
properties: omit(feature.properties, "originalGeom")
} : feature;
});
const {
annotationLayerTitle: title,
annotationLayerDescription: description
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import expect from 'expect';
import {
isAnnotationLayer,
isAnnotation,
isGeodesicMeasure,
createAnnotationId,
validateCoords,
coordToArray,
Expand All @@ -24,8 +25,16 @@ import {
applyDefaultCoordinates,
getFeatureIcon
} from '../AnnotationsUtils';
import { annotationsTest, annotationsTestResult, annotationsTestFromGeoJson } from './resources';

import { MeasureTypes } from '../../../../utils/MeasureUtils';

describe('AnnotationsUtils', () => {
it('isGeodesicMeasure', () => {
expect(isGeodesicMeasure(MeasureTypes.LENGTH)).toBe(true);
expect(isGeodesicMeasure(MeasureTypes.AREA)).toBe(true);
expect(isGeodesicMeasure(MeasureTypes.BEARING)).toBe(false);
});
it('isAnnotationLayer', () => {
expect(isAnnotationLayer({ id: 'annotations:01', rowViewer: 'annotations', features: [], type: 'vector' })).toBe(true);
expect(isAnnotationLayer({ id: '01', features: [], type: 'vector' })).toBe(false);
Expand Down Expand Up @@ -498,6 +507,9 @@ describe('AnnotationsUtils', () => {
]
});
});
it('annotationsToGeoJSON with length and area measures', () => {
expect(annotationsToGeoJSON(annotationsTest)).toEqual(annotationsTestResult);
});
it('geoJSONToAnnotations', () => {
const geoJSON = {
type: 'FeatureCollection',
Expand Down Expand Up @@ -544,6 +556,9 @@ describe('AnnotationsUtils', () => {
{ id: 'annotations:2', visibility: true, rowViewer: 'annotations', title: 'Annotation02', description: '<p>description</p>', type: 'vector', style: { format: 'geostyler', body: { name: '', rules: [{ name: '', filter: ['==', 'id', 'feature-02'], symbolizers: [{ kind: 'Icon' }] }] } }, features: [{ type: 'Feature', id: 'feature-02', geometry: { type: 'Point', coordinates: [0, 0] }, properties: { id: 'feature-02', annotationType: 'Point' } }] }
]);
});
it('geoJSONToAnnotations with geodesic measures in input', () => {
expect(geoJSONToAnnotations(annotationsTestResult)).toEqual(annotationsTestFromGeoJson);
});
it('importJSONToAnnotations invalid', () => {
expect(importJSONToAnnotations({})).toEqual([]);
});
Expand Down
5 changes: 5 additions & 0 deletions web/client/plugins/Annotations/utils/__tests__/resources.js

Large diffs are not rendered by default.

23 changes: 20 additions & 3 deletions web/client/utils/MeasurementUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export const getGeomTypeSelected = (features = []) =>{
}));
};

const getMeasureType = (feature) => {
export const getMeasureType = (feature) => {
if (feature?.properties?.measureType) {
return feature.properties.measureType;
}
Expand Down Expand Up @@ -257,7 +257,7 @@ const convertMeasureToFeatureCollection = (geometricFeatures, textLabels = [], u
properties: {
...properties,
label: infoLabelText,
geodesic: measureType === MeasureTypes.LENGTH,
geodesic: measureType === MeasureTypes.LENGTH || measureType === MeasureTypes.AREA,
...parseProperties(values, uom),
type: [MeasureTypes.POINT_COORDINATES].includes(measureType)
? 'position'
Expand Down Expand Up @@ -422,6 +422,9 @@ export const convertMeasuresToAnnotation = (geometricFeatures, textLabels, uom,
{
symbolizerId: uuidv1(),
kind: 'Fill',
msGeometry: {
name: 'lineToArc'
},
color: '#ffffff',
fillOpacity: 0.5,
outlineColor: '#33A8FF',
Expand Down Expand Up @@ -543,7 +546,21 @@ export const convertMeasuresToGeoJSON = (geometricFeatures, textLabels = [], uom

return {
type: 'FeatureCollection',
features,
msType: MEASURE_TYPE,
features: features.map(ft => {
const measureType = getMeasureType(ft);
return measureType === MeasureTypes.LENGTH || measureType === MeasureTypes.AREA ? {
...ft,
geometry: {
...ft.geometry,
coordinates: measureType === MeasureTypes.LENGTH ? transformLineToArcs(ft.geometry.coordinates) : ft.geometry.coordinates.map(transformLineToArcs)
},
properties: {
...ft.properties,
...(measureType === MeasureTypes.LENGTH || measureType === MeasureTypes.AREA ? { originalGeom: ft.geometry} : {})
}
} : ft;
}),
style: {
metadata: { editorType: 'visual' },
format: 'geostyler',
Expand Down
90 changes: 88 additions & 2 deletions web/client/utils/__tests__/MeasurementUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
*/

import expect from 'expect';
import find from 'lodash/find';
import {
convertMeasuresToAnnotation,
getGeomTypeSelected,
convertMeasuresToGeoJSON
convertMeasuresToGeoJSON,
getMeasureType
} from '../MeasurementUtils';
import { MeasureTypes, defaultUnitOfMeasure } from '../MeasureUtils';

Expand Down Expand Up @@ -57,6 +59,49 @@ describe('MeasurementUtils', () => {
]
}
}];
it('getMeasureType', () => {
const tests = [
{
source: {},
expectedValue: null
},
{
source: {
properties: {
measureType: "measureType"
}
},
expectedValue: "measureType"
},
{
source: {
properties: {
values: [{type: "bearing"}]
}
},
expectedValue: MeasureTypes.BEARING
},
{
source: {
properties: {
values: [{type: "length"}]
}
},
expectedValue: MeasureTypes.LENGTH
},
{
source: {
properties: {
values: [{type: "length"}, {type: "area"}]
}
},
expectedValue: MeasureTypes.AREA
}

];
tests.forEach(t => expect(getMeasureType(t.source)).toEqual(t.expectedValue));

});
it('convertMeasuresToAnnotation with LineString', () => {
const layer = convertMeasuresToAnnotation(features, [], testUom, 'id');
expect(layer).toBeTruthy();
Expand Down Expand Up @@ -86,6 +131,45 @@ describe('MeasurementUtils', () => {
'Mark', 'Mark', 'Line', 'Text', 'Text'
]);
});
it('convertMeasuresToAnnotation with Polygon', () => {
const fts = [{"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[[10.042417613994017, 53.9617045912569], [21.29241761399402, 57.23667168428492], [22.522886363994022, 53.17884325605559], [10.042417613994017, 53.9617045912569]]], "textLabels": [{"text": "793.737,01 m", "position": [15.667417613994019, 55.633387330220785]}, {"text": "457.894,01 m", "position": [21.90765198899402, 55.25951976945266]}, {"text": "827.598,36 m", "position": [16.28265198899402, 53.57208559899865]}]}, "properties": {"values": [{"value": 175074750490.87378, "formattedValue": "175.074.750.490,87 m²", "position": [18.148330256039472, 55.25951976945266], "type": "area"}, {"value": 2079229.382, "formattedValue": "2.079.229,38 m", "position": [10.042417613994017, 53.9617045912569], "uom": {"length": {"unit": "m", "label": "m", "value": "m"}, "area": {"unit": "sqm", "label": "m²", "value": "sqm"}, "bearing": {"unit": "deg", "label": "°", "value": "deg"}, "POLYLINE_DISTANCE_3D": {"unit": "m", "label": "m", "value": "m"}, "AREA_3D": {"unit": "sqm", "label": "m²", "value": "sqm"}, "POINT_COORDINATES": {"unit": "m", "label": "m", "value": "m"}, "HEIGHT_FROM_TERRAIN": {"unit": "m", "label": "m", "value": "m"}, "SLOPE": {"unit": "deg", "label": "°", "value": "deg"}, "ANGLE_3D": {"unit": "deg", "label": "°", "value": "deg"}}, "type": "length"}]}}];
const layer = convertMeasuresToAnnotation(fts, [{"text": "793.737,01 m", "position": [15.667417613994019, 55.633387330220785], "type": "Polygon", "textId": 0}, {"text": "457.894,01 m", "position": [21.90765198899402, 55.25951976945266], "type": "Polygon", "textId": 0}, {"text": "827.598,36 m", "position": [16.28265198899402, 53.57208559899865], "type": "Polygon", "textId": 0}], testUom, 'id');
expect(layer).toBeTruthy();
expect(layer.id).toBe('annotations:id');
expect(layer.type).toBe('vector');
expect(layer.title).toBe('Measure');
expect(layer.rowViewer).toBe('annotations');
expect(layer.features.length).toBe(4);
expect(layer.features[0].type).toBe('Feature');
expect(layer.features[0].geometry).toBeTruthy();
expect(layer.features[0].geometry.type).toBe('Polygon');
expect(layer.features[0].geometry.coordinates).toEqual([ [ [ 10.042417613994017, 53.9617045912569 ], [ 21.29241761399402, 57.23667168428492 ], [ 22.522886363994022, 53.17884325605559 ], [ 10.042417613994017, 53.9617045912569 ] ] ]);
const { id, ...properties } = layer.features[0].properties;
expect(properties).toEqual({
label: '175.074.750.490,87 m²',
geodesic: true,
length: 2079229.382,
lengthUom: 'm',
lengthTargetUom: 'm',
area: 175074750490.87378,
areaUom: 'sqm',
areaTargetUom: 'sqm',
type: 'measurement',
measureType: 'area',
annotationType: 'Polygon',
name: 'area' }
);
expect(layer.style.body.rules.length).toBe(5);
expect(layer.style.body.rules.map(({ symbolizers }) => symbolizers[0].kind)).toEqual([ 'Mark', 'Mark', 'Fill', 'Text', 'Text' ]);
layer.style.body.rules.forEach(rule => {
const fill = find(rule.symbolizers, symb => symb.kind === "Fill");
if (fill) {
expect(fill.msGeometry).toEqual({
name: 'lineToArc'
});
}
});
});

it('getGeomTypeSelected', ()=>{
const geomTypeSelected = getGeomTypeSelected(features);
Expand Down Expand Up @@ -607,6 +691,7 @@ describe('MeasurementUtils', () => {
label: '1,953.32 m',
geodesic: true,
length: 1953.316,
originalGeom: { type: 'LineString', coordinates: [ [ 9.194719791412354, 45.47215472687304 ], [ 9.1780686378479, 45.45903193872102 ] ] },
lengthUom: 'm',
lengthTargetUom: 'm',
type: 'measurement',
Expand Down Expand Up @@ -794,7 +879,8 @@ describe('MeasurementUtils', () => {
const { id, ...measurementProperties } = measureFeature.properties;
expect(measurementProperties).toEqual({
label: '1,644,850.69 m²',
geodesic: false,
geodesic: true,
originalGeom: { type: 'Polygon', coordinates: [ [ [ 9.193453788757324, 45.479723019275006 ], [ 9.164185523986816, 45.46196677235737 ], [ 9.20529842376709, 45.47394532919275 ], [ 9.193453788757324, 45.479723019275006 ] ] ] },
length: 7614.5289999999995,
lengthUom: 'm',
lengthTargetUom: 'm',
Expand Down
10 changes: 7 additions & 3 deletions web/client/utils/styleparser/CesiumStyleParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -849,8 +849,10 @@ const symbolizerToPrimitives = {
}] : [])
];
},
Fill: ({ parsedSymbolizer, globalOpacity }) => {
Fill: ({ parsedSymbolizer, feature, globalOpacity }) => {
const isExtruded = !parsedSymbolizer.msClampToGround && !!parsedSymbolizer.msExtrudedHeight;
const geometryFunction = getGeometryFunction(parsedSymbolizer);
const additionalOptions = geometryFunction ? geometryFunction(feature) : {};
return [
{
type: 'polygon',
Expand All @@ -870,7 +872,8 @@ const symbolizerToPrimitives = {
Cesium.ClassificationType.BOTH} ),
arcType: parsedSymbolizer.msClampToGround
? Cesium.ArcType.GEODESIC
: undefined
: undefined,
...additionalOptions
}
}
},
Expand Down Expand Up @@ -902,7 +905,8 @@ const symbolizerToPrimitives = {
Cesium.ClassificationType.BOTH} ),
arcType: parsedSymbolizer.msClampToGround
? Cesium.ArcType.GEODESIC
: Cesium.ArcType.NONE
: Cesium.ArcType.NONE,
...additionalOptions
}
}
}
Expand Down
18 changes: 17 additions & 1 deletion web/client/utils/styleparser/GeometryFunctionsUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const geometryFunctionsLibrary = {
};
},
lineToArc: () => {
return { arcType: Cesium.ArcType.GEODESIC };
return { arcType: Cesium.ArcType.GEODESIC, perPositionHeight: undefined };
},
startPoint: (feature) => {
const { positions } = getPositions(feature);
Expand All @@ -125,13 +125,15 @@ export const geometryFunctionsLibrary = {
* @param {object} options
* @param {class} options.Point ol/geom/Point class
* @param {class} options.LineString ol/geom/LineString class
* @param {class} options.Polygon ol/geom/Polygon class
* @param {class} options.GeoJSON ol/format/GeoJSON class
* @param {function} options.getCenter from ol/extent
* @returns {function} geometry function utils for OpenLayers
*/
openlayers: ({
Point,
LineString,
Polygon,
GeoJSON,
getCenter
}) => {
Expand Down Expand Up @@ -176,6 +178,17 @@ export const geometryFunctionsLibrary = {
return [point.x, point .y];
}));
}
if (type === 'Polygon') {
let coordinates = feature.getGeometry().getCoordinates()[0]; // not managing holes
coordinates = transformLineToArcs(coordinates.map(c => {
const point = reproject(c, mapProjection, 'EPSG:4326');
return [point.x, point .y];
}));
return new Polygon([coordinates.map(c => {
const point = reproject(c, 'EPSG:4326', mapProjection);
return [point.x, point .y];
})]);
}
return feature.getGeometry();
},
startPoint: () => (feature) => {
Expand Down Expand Up @@ -232,6 +245,9 @@ export const geometryFunctionsLibrary = {
if (feature.geometry.type === 'LineString') {
return transformLineToArcs(feature.geometry.coordinates);
}
if (feature.geometry.type === 'Polygon') {
return feature.geometry.coordinates.map(transformLineToArcs);
}
return null;
},
startPoint: (feature) => {
Expand Down

0 comments on commit ff421f5

Please sign in to comment.