From 73fa35bfc56830cfc89ac2088b210a0efca63dea Mon Sep 17 00:00:00 2001 From: David Manthey Date: Mon, 9 May 2022 13:10:37 -0400 Subject: [PATCH 1/2] feat: Add controls to combine and change annotations. --- CHANGELOG.md | 1 + src/annotation/annotation.js | 6 ++ src/annotation/polygonAnnotation.js | 3 +- src/annotation/rectangleAnnotation.js | 3 +- src/annotationLayer.js | 88 ++++++++++++++++++++++++- src/css/cursor-crosshair-difference.svg | 4 ++ src/css/cursor-crosshair-intersect.svg | 4 ++ src/css/cursor-crosshair-union.svg | 4 ++ src/css/cursor-crosshair-xor.svg | 4 ++ src/event.js | 14 ++++ src/main.styl | 8 +++ src/util/polyops.js | 11 +++- tests/cases/annotationLayer.js | 41 ++++++++++++ tests/cases/polyops.js | 6 ++ 14 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 src/css/cursor-crosshair-difference.svg create mode 100644 src/css/cursor-crosshair-intersect.svg create mode 100644 src/css/cursor-crosshair-union.svg create mode 100644 src/css/cursor-crosshair-xor.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index cdaeab9257..cd29256d4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Polygon operations ([#1198](../../pull/1198)) +- Use meta keys to modify annotations ([#1209](../../pull/1209)) ## Version 1.8.10 diff --git a/src/annotation/annotation.js b/src/annotation/annotation.js index cba55d88d0..dd93396f0f 100644 --- a/src/annotation/annotation.js +++ b/src/annotation/annotation.js @@ -74,6 +74,12 @@ var editHandleFeatureLevel = 3; * @property {boolean|string[]} [showLabel=true] `true` to show the annotation * label on annotations in done or edit states. Alternately, a list of * states in which to show the label. Falsy to not show the label. + * @property {boolean} [allowBooleanOperations] This defaults to `true` for + * annotations that have area and `false` for those without area (e.g., + * false for lines and points). If it is truthy, then, when the annotation + * is being created, it checks the metakeys on the first click that defines + * a coordinate to determine what boolean polygon operation should be + * performaned on the completion of the annotation. */ /** diff --git a/src/annotation/polygonAnnotation.js b/src/annotation/polygonAnnotation.js index b2485e323c..f69ccd4877 100644 --- a/src/annotation/polygonAnnotation.js +++ b/src/annotation/polygonAnnotation.js @@ -79,7 +79,8 @@ var polygonAnnotation = function (args) { }, stroke: false, strokeColor: {r: 0, g: 0, b: 1} - } + }, + allowBooleanOperations: true }, args || {}); args.vertices = args.vertices || args.coordinates || []; delete args.coordinates; diff --git a/src/annotation/rectangleAnnotation.js b/src/annotation/rectangleAnnotation.js index 024bcfa3ff..36e796c74b 100644 --- a/src/annotation/rectangleAnnotation.js +++ b/src/annotation/rectangleAnnotation.js @@ -69,7 +69,8 @@ var rectangleAnnotation = function (args, annotationName) { fillColor: {r: 0.3, g: 0.3, b: 0.3}, fillOpacity: 0.25, strokeColor: {r: 0, g: 0, b: 1} - } + }, + allowBooleanOperations: true }, args || {}); args.corners = args.corners || args.coordinates || []; delete args.coordinates; diff --git a/src/annotationLayer.js b/src/annotationLayer.js index 533c6f0b38..95e6231bd9 100644 --- a/src/annotationLayer.js +++ b/src/annotationLayer.js @@ -181,6 +181,44 @@ var annotationLayer = function (arg) { m_this._updateFromEvent(update); }; + /** + * Check if there is a current boolean operation. + * + * @returns {string?} Either undefined for no current boolean operation or + * the name of the operation. + */ + this.currentBooleanOperation = function () { + let op; + if (m_this._currentBooleanClass) { + op = m_this._currentBooleanClass.split('-')[1]; + } + return op; + }; + + /** + * Check if the map is currently in a mode where we are adding an annotation + * with a boolean operation. If so, remove the current annotation from the + * layer, then apply it via the boolean operation. + */ + this._handleBooleanOperation = function () { + const op = m_this.currentBooleanOperation(); + if (!op || !m_this.currentAnnotation || !m_this.currentAnnotation.toPolygonList) { + return; + } + const newAnnot = m_this.currentAnnotation; + m_this.removeAnnotation(m_this.currentAnnotation, false); + if (m_this.annotations().length || (op !== 'difference' && op !== 'intersect')) { + const evt = { + annotation: newAnnot, + operation: op + }; + m_this.geoTrigger(geo_event.annotation.boolean, evt); + if (evt.cancel !== false) { + util.polyops[op](m_this, newAnnot.toPolygonList(), {correspond: {}, keepAnnotations: 'exact', style: m_this}); + } + } + }; + /** * Handle updating the current annotation based on an update state. * @@ -196,6 +234,7 @@ var annotationLayer = function (arg) { m_this.mode(null); break; case 'done': + m_this._handleBooleanOperation(); m_this.mode(null); break; } @@ -205,6 +244,27 @@ var annotationLayer = function (arg) { } }; + /** + * Check the state of the modifier keys and apply them if appropriate. + * + * @param {geo.event} evt The mouse move or click event. + */ + this._handleMouseMoveModifiers = function (evt) { + if (m_this.mode() !== m_this.modes.edit && m_this.currentAnnotation.options('allowBooleanOperations') && m_this.currentAnnotation._coordinates().length < 2) { + if (evt.modifiers) { + const mod = (evt.modifiers.shift ? 's' : '') + (evt.modifiers.ctrl ? 'c' : '') + (evt.modifiers.meta || evt.modifiers.alt ? 'a' : ''); + if (m_this._currentBooleanClass === m_this._booleanClasses[mod]) { + return; + } + m_this._currentBooleanClass = m_this._booleanClasses[mod]; + const mapNode = m_this.map().node(); + Object.values(m_this._booleanClasses).forEach((c) => { + mapNode.toggleClass(c, m_this._booleanClasses[mod] === c); + }); + } + } + }; + /** * Handle mouse movement. If there is a current annotation, the movement * event is sent to it. @@ -213,6 +273,7 @@ var annotationLayer = function (arg) { */ this._handleMouseMove = function (evt) { if (m_this.mode() && m_this.currentAnnotation) { + m_this._handleMouseMoveModifiers(evt); var update = m_this.currentAnnotation.mouseMove(evt); if (update) { m_this.modified(); @@ -316,6 +377,7 @@ var annotationLayer = function (arg) { }); retrigger = true; } else if (m_this.mode() && m_this.currentAnnotation) { + m_this._handleMouseMoveModifiers(evt); update = m_this.currentAnnotation.mouseClick(evt); m_this._updateFromEvent(update); retrigger = !m_this.mode(); @@ -518,6 +580,15 @@ var annotationLayer = function (arg) { edit: 'edit' }; + /* Keys are short-hand for preferred event modifiers. Values are classes to + * apply to the map node. */ + this._booleanClasses = { + s: 'annotation-union', + sc: 'annotation-intersect', + c: 'annotation-difference', + sa: 'annotation-xor' + }; + /** * Get or set the current mode. * @@ -537,11 +608,15 @@ var annotationLayer = function (arg) { if (arg === undefined) { return m_mode; } - if (arg !== m_mode || (arg === m_this.modes.edit && editAnnotation !== m_this.editAnnotation)) { + if (arg !== m_mode || (arg === m_this.modes.edit && editAnnotation !== m_this.editAnnotation) || (arg !== m_this.modes.edit && arg && !m_this.currentAnnotation)) { var createAnnotation, actions, mapNode = m_this.map().node(), oldMode = m_mode; m_mode = arg; mapNode.toggleClass('annotation-input', !!(m_mode && m_mode !== m_this.modes.edit)); + if (!m_mode || m_mode === m_this.modes.edit) { + Object.values(m_this._booleanClasses).forEach((c) => mapNode.toggleClass(c, false)); + m_this._currentBooleanClass = undefined; + } if (!m_keyHandler) { m_keyHandler = Mousetrap(mapNode[0]); } @@ -1199,6 +1274,13 @@ var annotationLayer = function (arg) { exact = opts.correspond.exact1; annot = m_this.annotations(); } + if (keep !== 'all' && keep !== 'none') { + annot.forEach((oldAnnot, idx) => { + if (opts.annotationIndices.indexOf(idx) < 0) { + keepIds[oldAnnot.id()] = true; + } + }); + } const polyAnnot = []; poly.forEach((p, idx) => { p = p.map((h) => h.map((pt) => ({x: pt[0], y: pt[1]}))); @@ -1218,7 +1300,9 @@ var annotationLayer = function (arg) { reusedIds[orig.id()] = true; } ['name', 'description', 'label'].forEach((k) => { - result[k] = orig[k](); + if (orig[k](undefined, true)) { + result[k] = orig[k](undefined, true); + } }); Object.entries(orig.options()).forEach(([key, value]) => { if (['showLabel', 'style'].indexOf(key) >= 0 || key.endsWith('Style')) { diff --git a/src/css/cursor-crosshair-difference.svg b/src/css/cursor-crosshair-difference.svg new file mode 100644 index 0000000000..7f878e207d --- /dev/null +++ b/src/css/cursor-crosshair-difference.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/css/cursor-crosshair-intersect.svg b/src/css/cursor-crosshair-intersect.svg new file mode 100644 index 0000000000..7fd92e3180 --- /dev/null +++ b/src/css/cursor-crosshair-intersect.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/css/cursor-crosshair-union.svg b/src/css/cursor-crosshair-union.svg new file mode 100644 index 0000000000..811ded9186 --- /dev/null +++ b/src/css/cursor-crosshair-union.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/css/cursor-crosshair-xor.svg b/src/css/cursor-crosshair-xor.svg new file mode 100644 index 0000000000..e13e9e28ac --- /dev/null +++ b/src/css/cursor-crosshair-xor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/event.js b/src/event.js index 3fd19a3723..50a49286e4 100644 --- a/src/event.js +++ b/src/event.js @@ -692,4 +692,18 @@ geo_event.annotation.state = 'geo_annotation_state'; */ geo_event.annotation.mode = 'geo_annotation_mode'; +/** + * Triggered when an annotation can be combined via a boolean operation (union, + * intersect, difference, xor). + * + * @event geo.event.annotation.boolean + * @type {geo.event.base} + * @property {geo.annotation} annotation The annotation that is being operated + * on. + * @property {string} operation The operation being performed. + * @property {boolean} [cancel] If the handle sets this to false, don't apply + * the operation to the annotation layer. + */ +geo_event.annotation.boolean = 'geo_annotation_boolean'; + module.exports = geo_event; diff --git a/src/main.styl b/src/main.styl index 928f12df33..13ca13c989 100644 --- a/src/main.styl +++ b/src/main.styl @@ -49,6 +49,14 @@ &.annotation-input cursor crosshair + &.annotation-intersect + cursor embedurl("./css/cursor-crosshair-intersect.svg") 12 12,crosshair + &.annotation-difference + cursor embedurl("./css/cursor-crosshair-difference.svg") 12 12,crosshair + &.annotation-union + cursor embedurl("./css/cursor-crosshair-union.svg") 12 12,crosshair + &.annotation-xor + cursor embedurl("./css/cursor-crosshair-xor.svg") 12 12,crosshair &.highlight-focus &:after diff --git a/src/util/polyops.js b/src/util/polyops.js index f0f3d5c95f..8db224564a 100644 --- a/src/util/polyops.js +++ b/src/util/polyops.js @@ -39,7 +39,7 @@ var geo_map = require('../map'); * applied. If not specified , this is taken from the map second if * available. * @property {geo.map} [map] Used for ``ingcs`` and ``gcs`` if needed. - * @property {string} [innerOperation="xor"] one of union, intersect, xor. + * @property {string} [innerOperation="union"] one of union, intersect, xor. * Used to combine individual polygons in each of ``poly1`` and ``poly2`` * before the main operation is carried out. * @property {object} [correspond] If present, information about the @@ -181,6 +181,11 @@ function toPolygonList(poly, mode, opts) { if (poly.toPolygonList) { mode.style = poly; poly = poly.toPolygonList(opts); + if (!poly.length) { + mode.min = mode.max = [0, 0]; + mode.epsilon = 1e-10; + return poly; + } } else { mode.style = ''; if (poly.outer) { @@ -401,8 +406,8 @@ function generalOperationProcess(op, poly1, poly2, opts) { } let seglist1 = poly1.map(p => PolyBool.segments({regions: p})); let seglist2 = poly2.map(p => PolyBool.segments({regions: p})); - seglist1 = polygonOperationSeglist(opts.innerOperation || 'xor', mode1.epsilon, seglist1); - seglist2 = polygonOperationSeglist(opts.innerOperation || 'xor', mode2.epsilon, seglist2); + seglist1 = polygonOperationSeglist(opts.innerOperation || 'union', mode1.epsilon, seglist1); + seglist2 = polygonOperationSeglist(opts.innerOperation || 'union', mode2.epsilon, seglist2); let seglist = seglist1; if (seglist1[0] && seglist2[0]) { /* We need to do the main operation with the same inversion flags */ diff --git a/tests/cases/annotationLayer.js b/tests/cases/annotationLayer.js index d868ab901f..c8ff39af4a 100644 --- a/tests/cases/annotationLayer.js +++ b/tests/cases/annotationLayer.js @@ -514,6 +514,47 @@ describe('geo.annotationLayer', function () { expect(layer.mode()).toBe(null); layer.options('clickToEdit', false); }); + it('_handleMouseClick with modifier', function () { + layer.removeAllAnnotations(); + layer.mode('polygon'); + expect(layer.annotations().length).toBe(1); + expect(layer.annotations()[0].options('vertices').length).toBe(0); + var time = Date.now(); + layer._handleMouseClick({ + buttonsDown: {left: true}, + modifiers: {shift: true}, + time: time, + map: {x: 10, y: 20}, + mapgcs: map.displayToGcs({x: 10, y: 20}, null) + }); + layer._handleMouseClick({ + buttonsDown: {left: true}, + time: time, + map: {x: 30, y: 20}, + mapgcs: map.displayToGcs({x: 30, y: 20}, null) + }); + layer._handleMouseClick({ + buttonsDown: {left: true}, + time: time + 1000, + map: {x: 30, y: 20}, + mapgcs: map.displayToGcs({x: 30, y: 20}, null) + }); + layer._handleMouseClick({ + buttonsDown: {left: true}, + time: time + 1000, + map: {x: 20, y: 50}, + mapgcs: map.displayToGcs({x: 20, y: 50}, null) + }); + layer._handleMouseClick({ + buttonsDown: {left: true}, + time: time + 1000, + map: {x: 20, y: 50}, + mapgcs: map.displayToGcs({x: 20, y: 50}, null) + }); + expect(layer.annotations()[0].options('vertices').length).toBe(3); + expect(layer.annotations()[0].state()).toBe(geo.annotation.state.done); + layer.removeAllAnnotations(); + }); it('_handleMouseOn', function () { var rect = geo.annotation.rectangleAnnotation({ layer: layer, diff --git a/tests/cases/polyops.js b/tests/cases/polyops.js index 8d78541563..284f7f98e2 100644 --- a/tests/cases/polyops.js +++ b/tests/cases/polyops.js @@ -68,6 +68,12 @@ describe('geo.util.polyops', function () { }); }); + describe('toPolygonList', function () { + it('empty list', function () { + expect(geo.util.polyops.toPolygonList([])).toEqual([]); + }); + }); + var opTests = [{ a: [[0, 0], [10, 0], [10, 10], [0, 10]], b: [[5, 0], [15, 0], [15, 5], [5, 5]], From a84a92119092ae4c4fa3cab57e186c972e216e1a Mon Sep 17 00:00:00 2001 From: David Manthey Date: Tue, 24 May 2022 15:54:25 -0400 Subject: [PATCH 2/2] perf: Explicitly have annotation defaults. --- src/annotation/annotation.js | 15 ++++++- src/annotation/circleAnnotation.js | 9 ++++- src/annotation/ellipseAnnotation.js | 8 +++- src/annotation/lineAnnotation.js | 49 +++++++++++++--------- src/annotation/pointAnnotation.js | 51 ++++++++++++----------- src/annotation/polygonAnnotation.js | 58 ++++++++++++++++----------- src/annotation/rectangleAnnotation.js | 53 +++++++++++++----------- src/annotation/squareAnnotation.js | 9 ++++- src/annotationLayer.js | 7 +++- tests/cases/annotationLayer.js | 8 ++-- 10 files changed, 166 insertions(+), 101 deletions(-) diff --git a/src/annotation/annotation.js b/src/annotation/annotation.js index dd93396f0f..8ec7c763d3 100644 --- a/src/annotation/annotation.js +++ b/src/annotation/annotation.js @@ -100,7 +100,7 @@ var annotation = function (type, args) { } var m_this = this, - m_options = $.extend({}, {showLabel: true}, args || {}), + m_options = $.extend(true, {}, this.constructor.defaults, args || {}), m_id = m_options.annotationId; delete m_options.annotationId; if (m_id === undefined || (m_options.layer && m_options.layer.annotationById(m_id))) { @@ -803,10 +803,14 @@ var annotation = function (type, args) { key = styles[i]; value = util.ensureFunction(objStyle[key])(); if (value !== undefined) { + let defvalue = ((m_this.constructor.defaults || {}).style || {})[key]; if (key.toLowerCase().match(/color$/)) { value = util.convertColorToHex(value, 'needed'); + defvalue = defvalue !== undefined ? util.convertColorToHex(defvalue, 'needed') : defvalue; + } + if (value !== defvalue) { + obj.properties[key] = value; } - obj.properties[key] = value; } } for (i = 0; i < textFeature.usedStyles.length; i += 1) { @@ -1414,6 +1418,13 @@ function constrainAspectRatio(ratio) { return constraintFunction; } +/** + * This object contains the default options to initialize the class. + */ +annotation.defaults = { + showLabel: true +}; + module.exports = { state: annotationState, actionOwner: annotationActionOwner, diff --git a/src/annotation/circleAnnotation.js b/src/annotation/circleAnnotation.js index 8f3f0f5603..ad7956623c 100644 --- a/src/annotation/circleAnnotation.js +++ b/src/annotation/circleAnnotation.js @@ -19,13 +19,20 @@ const ellipseAnnotation = require('./ellipseAnnotation'); */ var circleAnnotation = function (args, annotationName) { 'use strict'; - args = $.extend({}, args, {constraint: 1}); if (!(this instanceof circleAnnotation)) { return new circleAnnotation(args, annotationName); } + args = $.extend(true, {}, this.constructor.defaults, args, {constraint: 1}); ellipseAnnotation.call(this, args, annotationName || 'circle'); }; inherit(circleAnnotation, ellipseAnnotation); + +/** + * This object contains the default options to initialize the class. + */ +circleAnnotation.defaults = $.extend({}, ellipseAnnotation.defaults, { +}); + var circleRequiredFeatures = {}; circleRequiredFeatures[markerFeature.capabilities.feature] = true; registerAnnotation('circle', circleAnnotation, circleRequiredFeatures); diff --git a/src/annotation/ellipseAnnotation.js b/src/annotation/ellipseAnnotation.js index e3efa8c2ba..425097911b 100644 --- a/src/annotation/ellipseAnnotation.js +++ b/src/annotation/ellipseAnnotation.js @@ -23,7 +23,7 @@ var ellipseAnnotation = function (args, annotationName) { if (!(this instanceof ellipseAnnotation)) { return new ellipseAnnotation(args, annotationName); } - + args = $.extend(true, {}, this.constructor.defaults, args); rectangleAnnotation.call(this, args, annotationName || 'ellipse'); var m_this = this; @@ -117,6 +117,12 @@ var ellipseAnnotation = function (args, annotationName) { }; inherit(ellipseAnnotation, rectangleAnnotation); +/** + * This object contains the default options to initialize the class. + */ +ellipseAnnotation.defaults = $.extend({}, rectangleAnnotation.defaults, { +}); + var ellipseRequiredFeatures = {}; ellipseRequiredFeatures[markerFeature.capabilities.feature] = true; registerAnnotation('ellipse', ellipseAnnotation, ellipseRequiredFeatures); diff --git a/src/annotation/lineAnnotation.js b/src/annotation/lineAnnotation.js index 665be33e43..d0a6eeec09 100644 --- a/src/annotation/lineAnnotation.js +++ b/src/annotation/lineAnnotation.js @@ -37,8 +37,7 @@ var lineAnnotation = function (args) { if (!(this instanceof lineAnnotation)) { return new lineAnnotation(args); } - - args = $.extend(true, {}, { + args = $.extend(true, {}, this.constructor.defaults, { style: { line: function (d) { /* Return an array that has the same number of items as we have @@ -48,16 +47,7 @@ var lineAnnotation = function (args) { }, position: function (d, i) { return m_this.options('vertices')[i]; - }, - strokeColor: {r: 0, g: 0, b: 0}, - strokeOpacity: 1, - strokeWidth: 3, - closed: false, - lineCap: 'butt', - lineJoin: 'miter' - }, - highlightStyle: { - strokeWidth: 5 + } }, createStyle: { line: function (d) { @@ -68,15 +58,9 @@ var lineAnnotation = function (args) { }, position: function (d, i) { return m_this.options('vertices')[i]; - }, - strokeColor: {r: 0, g: 0, b: 1}, - strokeOpacity: 1, - strokeWidth: 3, - closed: false, - lineCap: 'butt', - lineJoin: 'miter' + } } - }, args || {}); + }, args); args.vertices = args.vertices || args.coordinates || []; delete args.coordinates; annotation.call(this, 'line', args); @@ -337,6 +321,31 @@ var lineAnnotation = function (args) { }; inherit(lineAnnotation, annotation); +/** + * This object contains the default options to initialize the class. + */ +lineAnnotation.defaults = $.extend({}, annotation.defaults, { + style: { + strokeColor: {r: 0, g: 0, b: 0}, + strokeOpacity: 1, + strokeWidth: 3, + closed: false, + lineCap: 'butt', + lineJoin: 'miter' + }, + highlightStyle: { + strokeWidth: 5 + }, + createStyle: { + strokeColor: {r: 0, g: 0, b: 1}, + strokeOpacity: 1, + strokeWidth: 3, + closed: false, + lineCap: 'butt', + lineJoin: 'miter' + } +}); + var lineRequiredFeatures = {}; lineRequiredFeatures[lineFeature.capabilities.basic] = [annotationState.create]; registerAnnotation('line', lineAnnotation, lineRequiredFeatures); diff --git a/src/annotation/pointAnnotation.js b/src/annotation/pointAnnotation.js index 2dadfe3279..f51d93d20c 100644 --- a/src/annotation/pointAnnotation.js +++ b/src/annotation/pointAnnotation.js @@ -40,29 +40,7 @@ var pointAnnotation = function (args) { return new pointAnnotation(args); } - args = $.extend(true, {}, { - style: { - fill: true, - fillColor: {r: 0, g: 1, b: 0}, - fillOpacity: 0.25, - radius: 10, - scaled: false, - stroke: true, - strokeColor: {r: 0, g: 0, b: 0}, - strokeOpacity: 1, - strokeWidth: 3 - }, - createStyle: { - fillColor: {r: 0.3, g: 0.3, b: 0.3}, - fillOpacity: 0.25, - strokeColor: {r: 0, g: 0, b: 1} - }, - highlightStyle: { - fillColor: {r: 0, g: 1, b: 1}, - fillOpacity: 0.5, - strokeWidth: 5 - } - }, args || {}); + args = $.extend(true, {}, this.constructor.defaults, args); args.position = args.position || (args.coordinates ? args.coordinates[0] : undefined); delete args.coordinates; annotation.call(this, 'point', args); @@ -201,6 +179,33 @@ var pointAnnotation = function (args) { }; inherit(pointAnnotation, annotation); +/** + * This object contains the default options to initialize the class. + */ +pointAnnotation.defaults = $.extend({}, annotation.defaults, { + style: { + fill: true, + fillColor: {r: 0, g: 1, b: 0}, + fillOpacity: 0.25, + radius: 10, + scaled: false, + stroke: true, + strokeColor: {r: 0, g: 0, b: 0}, + strokeOpacity: 1, + strokeWidth: 3 + }, + createStyle: { + fillColor: {r: 0.3, g: 0.3, b: 0.3}, + fillOpacity: 0.25, + strokeColor: {r: 0, g: 0, b: 1} + }, + highlightStyle: { + fillColor: {r: 0, g: 1, b: 1}, + fillOpacity: 0.5, + strokeWidth: 5 + } +}); + var pointRequiredFeatures = {}; pointRequiredFeatures[pointFeature.capabilities.feature] = true; registerAnnotation('point', pointAnnotation, pointRequiredFeatures); diff --git a/src/annotation/polygonAnnotation.js b/src/annotation/polygonAnnotation.js index f69ccd4877..dde0234a43 100644 --- a/src/annotation/polygonAnnotation.js +++ b/src/annotation/polygonAnnotation.js @@ -43,27 +43,11 @@ var polygonAnnotation = function (args) { return new polygonAnnotation(args); } - args = $.extend(true, {}, { + args = $.extend(true, {}, this.constructor.defaults, { style: { - fill: true, - fillColor: {r: 0, g: 1, b: 0}, - fillOpacity: 0.25, - polygon: function (d) { return d.polygon; }, - stroke: true, - strokeColor: {r: 0, g: 0, b: 0}, - strokeOpacity: 1, - strokeWidth: 3, - uniformPolygon: true - }, - highlightStyle: { - fillColor: {r: 0, g: 1, b: 1}, - fillOpacity: 0.5, - strokeWidth: 5 + polygon: function (d) { return d.polygon; } }, createStyle: { - closed: false, - fillColor: {r: 0.3, g: 0.3, b: 0.3}, - fillOpacity: 0.25, line: function (d) { const coord = m_this._coordinates(); /* Return an array that has the same number of items as we have @@ -76,12 +60,9 @@ var polygonAnnotation = function (args) { return d.x; } return m_this.options('vertices')[i]; - }, - stroke: false, - strokeColor: {r: 0, g: 0, b: 1} - }, - allowBooleanOperations: true - }, args || {}); + } + } + }, args); args.vertices = args.vertices || args.coordinates || []; delete args.coordinates; annotation.call(this, 'polygon', args); @@ -325,6 +306,35 @@ var polygonAnnotation = function (args) { }; inherit(polygonAnnotation, annotation); +/** + * This object contains the default options to initialize the class. + */ +polygonAnnotation.defaults = $.extend({}, annotation.defaults, { + style: { + fill: true, + fillColor: {r: 0, g: 1, b: 0}, + fillOpacity: 0.25, + stroke: true, + strokeColor: {r: 0, g: 0, b: 0}, + strokeOpacity: 1, + strokeWidth: 3, + uniformPolygon: true + }, + highlightStyle: { + fillColor: {r: 0, g: 1, b: 1}, + fillOpacity: 0.5, + strokeWidth: 5 + }, + createStyle: { + closed: false, + fillColor: {r: 0.3, g: 0.3, b: 0.3}, + fillOpacity: 0.25, + stroke: false, + strokeColor: {r: 0, g: 0, b: 1} + }, + allowBooleanOperations: true +}); + var polygonRequiredFeatures = {}; polygonRequiredFeatures[polygonFeature.capabilities.feature] = true; polygonRequiredFeatures[lineFeature.capabilities.basic] = [annotationState.create]; diff --git a/src/annotation/rectangleAnnotation.js b/src/annotation/rectangleAnnotation.js index 36e796c74b..a6f89238db 100644 --- a/src/annotation/rectangleAnnotation.js +++ b/src/annotation/rectangleAnnotation.js @@ -48,30 +48,7 @@ var rectangleAnnotation = function (args, annotationName) { return new rectangleAnnotation(args, annotationName); } - args = $.extend(true, {}, { - style: { - fill: true, - fillColor: {r: 0, g: 1, b: 0}, - fillOpacity: 0.25, - polygon: function (d) { return d.polygon; }, - stroke: true, - strokeColor: {r: 0, g: 0, b: 0}, - strokeOpacity: 1, - strokeWidth: 3, - uniformPolygon: true - }, - highlightStyle: { - fillColor: {r: 0, g: 1, b: 1}, - fillOpacity: 0.5, - strokeWidth: 5 - }, - createStyle: { - fillColor: {r: 0.3, g: 0.3, b: 0.3}, - fillOpacity: 0.25, - strokeColor: {r: 0, g: 0, b: 1} - }, - allowBooleanOperations: true - }, args || {}); + args = $.extend(true, {}, this.constructor.defaults, args); args.corners = args.corners || args.coordinates || []; delete args.coordinates; annotation.call(this, annotationName || 'rectangle', args); @@ -409,6 +386,34 @@ var rectangleAnnotation = function (args, annotationName) { }; inherit(rectangleAnnotation, annotation); +/** + * This object contains the default options to initialize the class. + */ +rectangleAnnotation.defaults = $.extend({}, annotation.defaults, { + style: { + fill: true, + fillColor: {r: 0, g: 1, b: 0}, + fillOpacity: 0.25, + polygon: function (d) { return d.polygon; }, + stroke: true, + strokeColor: {r: 0, g: 0, b: 0}, + strokeOpacity: 1, + strokeWidth: 3, + uniformPolygon: true + }, + highlightStyle: { + fillColor: {r: 0, g: 1, b: 1}, + fillOpacity: 0.5, + strokeWidth: 5 + }, + createStyle: { + fillColor: {r: 0.3, g: 0.3, b: 0.3}, + fillOpacity: 0.25, + strokeColor: {r: 0, g: 0, b: 1} + }, + allowBooleanOperations: true +}); + var rectangleRequiredFeatures = {}; rectangleRequiredFeatures[polygonFeature.capabilities.feature] = true; registerAnnotation('rectangle', rectangleAnnotation, rectangleRequiredFeatures); diff --git a/src/annotation/squareAnnotation.js b/src/annotation/squareAnnotation.js index 820c2071e8..cddb7cf409 100644 --- a/src/annotation/squareAnnotation.js +++ b/src/annotation/squareAnnotation.js @@ -19,13 +19,20 @@ const rectangleAnnotation = require('./rectangleAnnotation'); */ var squareAnnotation = function (args, annotationName) { 'use strict'; - args = $.extend({}, args, {constraint: 1}); if (!(this instanceof squareAnnotation)) { return new squareAnnotation(args, annotationName); } + args = $.extend(true, {}, this.constructor.defaults, args, {constraint: 1}); rectangleAnnotation.call(this, args, annotationName || 'square'); }; inherit(squareAnnotation, rectangleAnnotation); + +/** + * This object contains the default options to initialize the class. + */ +squareAnnotation.defaults = $.extend({}, rectangleAnnotation.defaults, { +}); + var squareRequiredFeatures = {}; squareRequiredFeatures[polygonFeature.capabilities.feature] = true; registerAnnotation('square', squareAnnotation, squareRequiredFeatures); diff --git a/src/annotationLayer.js b/src/annotationLayer.js index 95e6231bd9..03d993c1a3 100644 --- a/src/annotationLayer.js +++ b/src/annotationLayer.js @@ -693,7 +693,12 @@ var annotationLayer = function (arg) { */ this.geojson = function (geojson, clear, gcs, includeCrs) { if (geojson !== undefined) { - var reader = registry.createFileReader('geojsonReader', {layer: m_this}); + var reader = registry.createFileReader('geojsonReader', { + layer: m_this, + lineStyle: require('./annotation/lineAnnotation').defaults.style, + pointStyle: require('./annotation/pointAnnotation').defaults.style, + polygonStyle: require('./annotation/polygonAnnotation').defaults.style + }); if (!reader.canRead(geojson)) { return; } diff --git a/tests/cases/annotationLayer.js b/tests/cases/annotationLayer.js index c8ff39af4a..254bde1c3d 100644 --- a/tests/cases/annotationLayer.js +++ b/tests/cases/annotationLayer.js @@ -828,10 +828,10 @@ describe('geo.annotationLayer', function () { }; layer.geojson(badattr, true); var attr = layer.geojson().features[0].properties; - expect(attr.radius).toBeGreaterThan(0); - expect(attr.fillOpacity).toBeGreaterThan(0); - expect(attr.fillColor).toBe('#00ff00'); - expect(attr.scaled).toBe(false); + expect(attr.radius).toBe(undefined); + expect(attr.fillOpacity).toBe(undefined); + expect(attr.fillColor).toBe(undefined); + expect(attr.scaled).toBe(undefined); var goodattr = { type: 'Feature', geometry: {