Skip to content

Commit 8a70613

Browse files
authored
Fixes #2158: marker style editor for annotations (#2208)
* Fixes #2158: marker style editor for annotations * Fixes #2207: editing geometry issues * Fixed some codacy issues * Added more tests * Added more tests * Various fixes * fixed css issues
1 parent 8ebb66c commit 8a70613

26 files changed

Lines changed: 1569 additions & 100 deletions

web/client/actions/__tests__/annotations-test.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ const {
2020
UPDATE_ANNOTATION_GEOMETRY,
2121
VALIDATION_ERROR,
2222
REMOVE_ANNOTATION_GEOMETRY,
23+
TOGGLE_STYLE,
24+
SET_STYLE,
25+
RESTORE_STYLE,
2326
editAnnotation,
2427
removeAnnotation,
2528
confirmRemoveAnnotation,
@@ -29,7 +32,10 @@ const {
2932
toggleAdd,
3033
updateAnnotationGeometry,
3134
validationError,
32-
removeAnnotationGeometry
35+
removeAnnotationGeometry,
36+
toggleStyle,
37+
setStyle,
38+
restoreStyle
3339
} = require('../annotations');
3440

3541
describe('Test correctness of the annotations actions', () => {
@@ -82,18 +88,35 @@ describe('Test correctness of the annotations actions', () => {
8288
it('save annotation', () => {
8389
const result = saveAnnotation('1', {
8490
name: 'changed'
85-
}, {});
91+
}, {}, {});
8692
expect(result.type).toEqual(SAVE_ANNOTATION);
8793
expect(result.id).toEqual('1');
8894
expect(result.fields.name).toEqual('changed');
8995
expect(result.geometry).toExist();
96+
expect(result.style).toExist();
9097
});
9198

9299
it('toggle add', () => {
93100
const result = toggleAdd();
94101
expect(result.type).toEqual(TOGGLE_ADD);
95102
});
96103

104+
it('toggle style', () => {
105+
const result = toggleStyle();
106+
expect(result.type).toEqual(TOGGLE_STYLE);
107+
});
108+
109+
it('restore style', () => {
110+
const result = restoreStyle();
111+
expect(result.type).toEqual(RESTORE_STYLE);
112+
});
113+
114+
it('set style', () => {
115+
const result = setStyle({});
116+
expect(result.type).toEqual(SET_STYLE);
117+
expect(result.style).toExist();
118+
});
119+
97120
it('update annotation geometry', () => {
98121
const result = updateAnnotationGeometry({});
99122
expect(result.type).toEqual(UPDATE_ANNOTATION_GEOMETRY);

web/client/actions/annotations.js

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ const CANCEL_REMOVE_ANNOTATION = 'ANNOTATIONS:CANCEL_REMOVE';
1414
const CANCEL_EDIT_ANNOTATION = 'ANNOTATIONS:CANCEL_EDIT';
1515
const SAVE_ANNOTATION = 'ANNOTATIONS:SAVE';
1616
const TOGGLE_ADD = 'ANNOTATIONS:TOGGLE_ADD';
17+
const TOGGLE_STYLE = 'ANNOTATIONS:TOGGLE_STYLE';
18+
const SET_STYLE = 'ANNOTATIONS:SET_STYLE';
19+
const RESTORE_STYLE = 'ANNOTATIONS:RESTORE_STYLE';
1720
const UPDATE_ANNOTATION_GEOMETRY = 'ANNOTATIONS:UPDATE_GEOMETRY';
1821
const VALIDATION_ERROR = 'ANNOTATIONS:VALIDATION_ERROR';
1922

@@ -60,12 +63,13 @@ function cancelEditAnnotation() {
6063
};
6164
}
6265

63-
function saveAnnotation(id, fields, geometry) {
66+
function saveAnnotation(id, fields, geometry, style) {
6467
return {
6568
type: SAVE_ANNOTATION,
6669
id,
6770
fields,
68-
geometry
71+
geometry,
72+
style
6973
};
7074
}
7175

@@ -75,6 +79,25 @@ function toggleAdd() {
7579
};
7680
}
7781

82+
function toggleStyle() {
83+
return {
84+
type: TOGGLE_STYLE
85+
};
86+
}
87+
88+
function restoreStyle() {
89+
return {
90+
type: RESTORE_STYLE
91+
};
92+
}
93+
94+
function setStyle(style) {
95+
return {
96+
type: SET_STYLE,
97+
style
98+
};
99+
}
100+
78101
function updateAnnotationGeometry(geometry) {
79102
return {
80103
type: UPDATE_ANNOTATION_GEOMETRY,
@@ -100,6 +123,9 @@ module.exports = {
100123
UPDATE_ANNOTATION_GEOMETRY,
101124
VALIDATION_ERROR,
102125
REMOVE_ANNOTATION_GEOMETRY,
126+
TOGGLE_STYLE,
127+
SET_STYLE,
128+
RESTORE_STYLE,
103129
editAnnotation,
104130
removeAnnotation,
105131
confirmRemoveAnnotation,
@@ -109,5 +135,8 @@ module.exports = {
109135
toggleAdd,
110136
updateAnnotationGeometry,
111137
validationError,
112-
removeAnnotationGeometry
138+
removeAnnotationGeometry,
139+
toggleStyle,
140+
setStyle,
141+
restoreStyle
113142
};

web/client/components/map/leaflet/DrawSupport.jsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,9 @@ class DrawSupport extends React.Component {
165165
if (this.props.options && this.props.options.stopAfterDrawing) {
166166
this.props.onChangeDrawingStatus('stop', this.props.drawMethod, this.props.drawOwner);
167167
}
168-
const newGeoJsonFt = this.convertFeaturesToGeoJson(this.drawLayer, this.props);
168+
const newGeoJsonFt = this.convertFeaturesToGeoJson(evt.layer, this.props);
169169
this.props.onEndDrawing(geometry, this.props.drawOwner);
170-
this.props.onGeometryChanged(newGeoJsonFt.features, this.props.drawOwner, this.props.options && this.props.options.stopAfterDrawing ? "enterEditMode" : "");
170+
this.props.onGeometryChanged([newGeoJsonFt], this.props.drawOwner, this.props.options && this.props.options.stopAfterDrawing ? "enterEditMode" : "");
171171
};
172172

173173
onUpdateGeom = (features, props) => {
@@ -206,7 +206,9 @@ class DrawSupport extends React.Component {
206206

207207
addGeojsonLayer = ({features, projection, style}) => {
208208
this.clean();
209-
let geoJsonLayerGroup = L.geoJson(features, {style: style, pointToLayer: (f, latLng) => {
209+
let geoJsonLayerGroup = L.geoJson(features, {style: (f) => {
210+
return f.style || style;
211+
}, pointToLayer: (f, latLng) => {
210212
let center = CoordinatesUtils.reproject({x: latLng.lng, y: latLng.lat}, projection, "EPSG:4326");
211213
return VectorUtils.pointToLayer(L.latLng(center.y, center.x), f, style);
212214
}});
@@ -219,6 +221,10 @@ class DrawSupport extends React.Component {
219221
this.addGeojsonLayer({features: newProps.features, projection: newProps.options && newProps.options.featureProjection || "EPSG:4326", style: newProps.style});
220222
} else {
221223
this.drawLayer.clearLayers();
224+
this.drawLayer.options.pointToLayer = (f, latLng) => {
225+
let center = CoordinatesUtils.reproject({x: latLng.lng, y: latLng.lat}, newProps.options && newProps.options.featureProjection || "EPSG:4326", "EPSG:4326");
226+
return VectorUtils.pointToLayer(L.latLng(center.y, center.x), f, newProps.style);
227+
};
222228
this.drawLayer.addData(this.convertFeaturesPolygonToPoint(newProps.features, this.props.drawMethod));
223229
}
224230
};

web/client/components/map/openlayers/DrawSupport.jsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,12 @@ class DrawSupport extends React.Component {
132132

133133
addFeatures = ({features, drawMethod, options}) => {
134134
features.forEach((g) => {
135+
let geometry = g;
136+
if (geometry.geometry) {
137+
geometry = reprojectGeoJson(geometry, this.props.options.featureProjection, this.props.map.getView().getProjection().getCode()).geometry;
138+
}
135139
const feature = new ol.Feature({
136-
geometry: this.createOLGeometry(g)
140+
geometry: this.createOLGeometry(geometry)
137141
});
138142
this.drawSource.addFeature(feature);
139143
});
@@ -151,6 +155,9 @@ class DrawSupport extends React.Component {
151155
} else {
152156
this.drawSource.clear();
153157
this.addFeatures(newProps);
158+
if (newProps.style) {
159+
this.drawLayer.setStyle(this.toOlStyle(newProps.style));
160+
}
154161
}
155162
};
156163

@@ -243,10 +250,11 @@ class DrawSupport extends React.Component {
243250
this.props.onGeometryChanged([newFeature], this.props.drawOwner, this.props.options && this.props.options.stopAfterDrawing ? "enterEditMode" : "");
244251

245252
this.props.onEndDrawing(feature, this.props.drawOwner);
253+
const newFeatures = isSimpleGeomType(this.props.drawMethod) ? this.props.features.concat([feature]) : [feature];
246254
if (this.props.options.stopAfterDrawing) {
247-
this.props.onChangeDrawingStatus('stop', this.props.drawMethod, this.props.drawOwner, this.props.features.concat([feature]));
255+
this.props.onChangeDrawingStatus('stop', this.props.drawMethod, this.props.drawOwner, newFeatures);
248256
} else {
249-
this.props.onChangeDrawingStatus('replace', this.props.drawMethod, this.props.drawOwner, this.props.features.concat([feature]));
257+
this.props.onChangeDrawingStatus('replace', this.props.drawMethod, this.props.drawOwner, newFeatures, this.props.options);
250258
}
251259
if (this.selectInteraction) {
252260
// TODO update also the selected features
@@ -552,7 +560,7 @@ class DrawSupport extends React.Component {
552560
strokeColor = '#4a90e2';
553561
}
554562

555-
if (style.iconUrl || style.iconGlyph) {
563+
if (style && (style.iconUrl || style.iconGlyph)) {
556564
return VectorStyle.getMarkerStyle({
557565
style
558566
});

web/client/components/map/openlayers/Feature.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class Feature extends React.Component {
6161
this._feature = format.readFeatures({type: newProps.type, properties: newProps.properties, geometry: newProps.geometry, id: this.props.msId});
6262
this._feature.forEach((f) => f.getGeometry().transform(newProps.featuresCrs, this.props.crs || 'EPSG:3857'));
6363
this._feature.forEach((f) => {
64-
if ((newProps.style !== this.props.style) && (newProps.layerStyle !== newProps.style)) {
64+
if (newProps.layerStyle !== newProps.style) {
6565
f.setStyle(getStyle({style: newProps.style}));
6666
}
6767
});

web/client/components/map/openlayers/Map.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ class OpenlayersMap extends React.Component {
126126
let tLng = CoordinatesUtils.normalizeLng(coords[0]);
127127
let layerInfo;
128128
map.forEachFeatureAtPixel(event.pixel, (feature, layer) => {
129-
if (layer.get('handleClickOnLayer')) {
129+
if (layer && layer.get('handleClickOnLayer')) {
130130
layerInfo = layer.get('msId');
131131
}
132132
coords = ol.proj.toLonLat(feature.getGeometry().getFirstCoordinate(), this.props.projection);

web/client/components/map/openlayers/VectorStyle.js

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,15 @@
11
var markerIcon = require('./img/marker-icon.png');
22
var markerShadow = require('./img/marker-shadow.png');
3-
var extraMarker = require('./img/markers_default.png');
4-
var extraMarkerShadow = require('./img/markers_shadow.png');
53
var ol = require('openlayers');
64

75
const assign = require('object-assign');
86

9-
const csstree = require('css-tree');
10-
const css = require('raw-loader!./font-awesome.txt');
7+
const MarkerUtils = require('../../../utils/MarkerUtils');
8+
const markers = MarkerUtils.extraMarkers;
9+
const extraMarker = markers.icons[0];
10+
const extraMarkerShadow = markers.icons[1];
1111

12-
const getNodeOfType = (node, condition) => {
13-
if (condition(node)) {
14-
return node;
15-
}
16-
if (node.children) {
17-
return node.children.reduce((previous, current) => {
18-
const result = getNodeOfType(current, condition);
19-
return result || previous;
20-
}, null);
21-
}
22-
return null;
23-
};
24-
25-
const parsedCss = csstree.toPlainObject(csstree.parse(css));
26-
27-
const glyphs = parsedCss.children.reduce((previous, rule) => {
28-
if (rule.prelude) {
29-
const classSelector = getNodeOfType(rule.prelude, (node) => node.type === 'ClassSelector');
30-
const pseudoClassSelector = getNodeOfType(rule.prelude, (node) => node.type === 'PseudoClassSelector');
31-
if (classSelector && classSelector.name && classSelector.name.indexOf('fa-') === 0 && pseudoClassSelector && pseudoClassSelector.name === 'before') {
32-
const text = getNodeOfType(getNodeOfType(rule.block, (node) => node.type === 'Declaration' && node.property === 'content').value, (node) => node.type === 'String').value;
33-
/* eslint-disable */
34-
return assign(previous, {
35-
[classSelector.name.substring(3)]: eval("'\\u" + text.substring(2, text.length - 1) + "'")
36-
});
37-
/* eslint-enable */
38-
}
39-
}
40-
return previous;
41-
}, {});
42-
43-
44-
const markers = {
45-
size: [36, 46],
46-
colors: ['red', 'orange-dark', 'orange', 'yellow', 'blue-dark', 'blue', 'cyan', 'purple', 'violet', 'pink', 'green-dark', 'green',
47-
'green-light', 'black', 'white'],
48-
shapes: ['circle', 'square', 'star', 'penta']
49-
};
12+
const glyphs = MarkerUtils.getGlyphs('fontawesome');
5013

5114
const image = new ol.style.Circle({
5215
radius: 5,
@@ -170,7 +133,7 @@ function getMarkerStyle(options) {
170133
} else {
171134
markerStyle = [new ol.style.Style({
172135
image: new ol.style.Icon(({
173-
anchor: [markers.size[0] / 2, markers.size[1]],
136+
anchor: [12, 12],
174137
anchorXUnits: 'pixels',
175138
anchorYUnits: 'pixels',
176139
src: extraMarkerShadow
@@ -188,8 +151,7 @@ function getMarkerStyle(options) {
188151
text: glyphs[options.style.iconGlyph],
189152
font: '14px FontAwesome',
190153
offsetY: -markers.size[1] * 2 / 3,
191-
fill: new ol.style.Fill({color: '#FFFFFF'}),
192-
stroke: new ol.style.Stroke({color: '#FFFFFF', width: 2})
154+
fill: new ol.style.Fill({color: '#FFFFFF'})
193155
})
194156
})];
195157
}

0 commit comments

Comments
 (0)