From 691fe77b453d6b0323f190b7e2e0d719302c98e2 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Mon, 11 Jun 2018 13:55:54 -0400 Subject: [PATCH 1/2] Trigger an event when an annotation's coordinates change. For example, `layer.geoOn(geo.event.annotation.coordinates, function (evt) { console.log(evt.annotation.coordinates()); });`. Expose the annotation default edit handle style to make it possible to make global changes. --- src/annotation.js | 43 +++++++++++++++++++++++++++++++-------- src/event.js | 9 ++++++++ tests/cases/annotation.js | 16 ++++++++++++++- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/annotation.js b/src/annotation.js index a6196a22fc..055d9c8f56 100644 --- a/src/annotation.js +++ b/src/annotation.js @@ -21,6 +21,14 @@ var annotationState = { var annotationActionOwner = 'annotationAction'; +/** + * These styles are applied to edit handles, and can be overridden by + * individual annotations. + * + * @alias geo.annotation.defaultEditHandleStyle + * @type {object} + * @default + */ var defaultEditHandleStyle = { fill: true, fillColor: function (d) { @@ -278,6 +286,7 @@ var annotation = function (type, args) { * otherwise change it. This should be one of the * `geo.annotation.state` values. * @returns {this|string} The current state or this annotation. + * @fires geo.event.annotation.state */ this.state = function (arg) { if (arg === undefined) { @@ -401,6 +410,7 @@ var annotation = function (type, args) { * the option to this value. * @returns {object|this} If options are set, return the annotation, * otherwise return the requested option or the set of options. + * @fires geo.event.annotation.coordinates */ this.options = function (arg1, arg2) { if (arg1 === undefined) { @@ -409,7 +419,9 @@ var annotation = function (type, args) { if (typeof arg1 === 'string' && arg2 === undefined) { return m_options[arg1]; } + var coordinatesSet; if (arg2 === undefined) { + coordinatesSet = arg1[m_this._coordinateOption] !== undefined; m_options = $.extend(true, m_options, arg1); /* For style objects, re-extend them without recursion. This allows * setting colors without an opacity field, for instance. */ @@ -421,6 +433,7 @@ var annotation = function (type, args) { } }); } else { + coordinatesSet = arg1 === m_this._coordinateOption; m_options[arg1] = arg2; } if (m_options.coordinates) { @@ -444,6 +457,11 @@ var annotation = function (type, args) { m_this.description(description); } m_this.modified(); + if (coordinatesSet && m_this.layer()) { + m_this.layer().geoTrigger(geo_event.annotation.coordinates, { + annotation: this + }); + } return this; }; @@ -596,6 +614,8 @@ var annotation = function (type, args) { return []; }; + this._coordinateOption = 'vertices'; + /** * Get coordinates associated with this annotation. * @@ -1183,8 +1203,8 @@ var rectangleAnnotation = function (args) { }; /** - * Get coordinates associated with this annotation in the map gcs coordinate - * system. + * Get and optionally set coordinates associated with this annotation in the + * map gcs coordinate system. * * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates * to set. @@ -1199,6 +1219,8 @@ var rectangleAnnotation = function (args) { return m_this.options('corners'); }; + this._coordinateOption = 'corners'; + /** * Return the coordinates to be stored in a geojson geometry object. * @@ -1529,8 +1551,8 @@ var polygonAnnotation = function (args) { }; /** - * Get coordinates associated with this annotation in the map gcs coordinate - * system. + * Get and optionally set coordinates associated with this annotation in the + * map gcs coordinate system. * * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates * to set. @@ -1784,8 +1806,8 @@ var lineAnnotation = function (args) { }; /** - * Get coordinates associated with this annotation in the map gcs coordinate - * system. + * Get and optionally set coordinates associated with this annotation in the + * map gcs coordinate system. * * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates * to set. @@ -2172,8 +2194,8 @@ var pointAnnotation = function (args) { }; /** - * Get coordinates associated with this annotation in the map gcs coordinate - * system. + * Get and optionally set coordinates associated with this annotation in the + * map gcs coordinate system. * * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates * to set. @@ -2189,6 +2211,8 @@ var pointAnnotation = function (args) { return [m_this.options('position')]; }; + this._coordinateOption = 'position'; + /** * Handle a mouse click on this annotation. If the event is processed, * evt.handled should be set to `true` to prevent further processing. @@ -2263,5 +2287,6 @@ module.exports = { pointAnnotation: pointAnnotation, polygonAnnotation: polygonAnnotation, rectangleAnnotation: rectangleAnnotation, - _editHandleFeatureLevel: editHandleFeatureLevel + _editHandleFeatureLevel: editHandleFeatureLevel, + defaultEditHandleStyle: defaultEditHandleStyle }; diff --git a/src/event.js b/src/event.js index 84350ccafe..dd4aaf8764 100644 --- a/src/event.js +++ b/src/event.js @@ -472,6 +472,15 @@ geo_event.annotation.add_before = 'geo_annotation_add_before'; */ geo_event.annotation.update = 'geo_annotation_update'; +/** + * Triggered when an annotation's coordinates have been updated. + * + * @event geo.event.annotation.coordinates + * @type {object} + * @property {geo.annotation} annotation The annotation that was altered. + */ +geo_event.annotation.coordinates = 'geo_annotation_coordinates'; + /** * Triggered when an annotation has been removed. * diff --git a/tests/cases/annotation.js b/tests/cases/annotation.js index 0c0cb20d95..c8ad075b45 100644 --- a/tests/cases/annotation.js +++ b/tests/cases/annotation.js @@ -61,7 +61,7 @@ describe('geo.annotation', function () { } describe('geo.annotation.annotation', function () { - var map, layer, stateEvent = 0, lastStateEvent; + var map, layer, stateEvent = 0, lastStateEvent, coordinatesEvent = 0; it('create', function () { var ann = geo.annotation.annotation('test'); expect(ann instanceof geo.annotation.annotation); @@ -201,6 +201,20 @@ describe('geo.annotation', function () { ann.options('coordinates', testval); expect(ann.options().coordinates).toBe(undefined); expect(ann._coordinates()).toBe(testval); + coordinatesEvent = 0; + layer.geoOn(geo.event.annotation.coordinates, function (evt) { + coordinatesEvent += 1; + }); + ann._coordinates([]); + expect(coordinatesEvent).toBe(0); + ann.options('vertices', [{x: 1, y: 1}]); + expect(coordinatesEvent).toBe(1); + ann.options({vertices: [{x: 2, y: 1}]}); + expect(coordinatesEvent).toBe(2); + ann.options('other', 'test'); + expect(coordinatesEvent).toBe(2); + ann.options({other: 'test2'}); + expect(coordinatesEvent).toBe(2); }); it('style and editStyle', function () { var ann = geo.annotation.annotation('test', { From 0e0e49be28034ad47724cb6ebec3d4b5a8aa955d Mon Sep 17 00:00:00 2001 From: David Manthey Date: Tue, 12 Jun 2018 08:20:02 -0400 Subject: [PATCH 2/2] Add a few more annotation events. geo.event.annotation.select_edit_handle is triggered whenever an edit handle is selected or unselected (usually by just having the mouse hover over it). geo.event.annotation.edit_action is triggered on actions on an edit handle. This includes geo.event.actionup, geo.event.actionmove, and geo.event.actiondown. To perform some additional processing every time an annotation may have been changed via the edit handle, you could do something like `layer.geoOn(geo.event.annotation.edit_action, function (evt) { if (evt.action === geo.event.actionup) { } });`. This also fixes a minor issue where if you drag an edit handle and then then drag it again without the mouse leaving that handle, it would sometimes jump slightly. --- src/annotation.js | 10 +++++++++- src/annotationLayer.js | 19 +++++++++++++++++++ src/event.js | 26 ++++++++++++++++++++++++++ tests/cases/annotationLayer.js | 9 +++++++-- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/annotation.js b/src/annotation.js index 055d9c8f56..041ab30e20 100644 --- a/src/annotation.js +++ b/src/annotation.js @@ -378,6 +378,7 @@ var annotation = function (type, args) { * @param {object} handle The data for the edit handle. * @param {boolean} enable True to enable the handle, false to disable. * @returns {this} + * @fires geo.event.annotation.select_edit_handle */ this.selectEditHandle = function (handle, enable) { if (enable && m_this._editHandle && m_this._editHandle.handle && @@ -397,6 +398,13 @@ var annotation = function (type, args) { resizePosition: m_this._rotateHandlePosition( handle.style.resizeHandleOffset, handle.style.resizeHandleRotation) }; + if (m_this.layer()) { + m_this.layer().geoTrigger(geo_event.annotation.select_edit_handle, { + annotation: m_this, + handle: m_this._editHandle, + enable: enable + }); + } return m_this; }; @@ -459,7 +467,7 @@ var annotation = function (type, args) { m_this.modified(); if (coordinatesSet && m_this.layer()) { m_this.layer().geoTrigger(geo_event.annotation.coordinates, { - annotation: this + annotation: m_this }); } return this; diff --git a/src/annotationLayer.js b/src/annotationLayer.js index 87e29bcb8a..b51cf889f1 100644 --- a/src/annotationLayer.js +++ b/src/annotationLayer.js @@ -49,6 +49,10 @@ var textFeature = require('./textFeature'); * annotation to place it in edit mode. * @param {object} [args.defaultLabelStyle] Default styles for labels. * @returns {geo.annotationLayer} + * @fires geo.event.annotation.state + * @fires geo.event.annotation.coordinates + * @fires geo.event.annotation.edit_action + * @fires geo.event.annotation.select_edit_handle */ var annotationLayer = function (args) { 'use strict'; @@ -125,6 +129,7 @@ var annotationLayer = function (args) { * creates a rectangle. * * @param {geo.event} evt The selection event. + * @fires geo.event.annotation.edit_action */ this._processAction = function (evt) { var update; @@ -134,6 +139,20 @@ var annotationLayer = function (args) { switch (m_this.mode()) { case m_this.modes.edit: update = m_this.currentAnnotation.processEditAction(evt); + if (m_this.currentAnnotation && + m_this.currentAnnotation._editHandle && + m_this.currentAnnotation._editHandle.handle) { + m_this.geoTrigger(geo_event.annotation.edit_action, { + annotation: m_this.currentAnnotation, + handle: m_this.currentAnnotation._editHandle ? m_this.currentAnnotation._editHandle.handle : undefined, + action: evt.event + }); + if (evt.event === geo_event.actionup) { + m_this._selectEditHandle({ + data: m_this.currentAnnotation._editHandle.handle}, + m_this.currentAnnotation._editHandle.handle.selected); + } + } break; default: update = m_this.currentAnnotation.processAction(evt); diff --git a/src/event.js b/src/event.js index dd4aaf8764..ddce4b0125 100644 --- a/src/event.js +++ b/src/event.js @@ -481,6 +481,32 @@ geo_event.annotation.update = 'geo_annotation_update'; */ geo_event.annotation.coordinates = 'geo_annotation_coordinates'; +/** + * Triggered when an annotation's edit handle is selected or released. + * + * @event geo.event.annotation.select_edit_handle + * @type {object} + * @property {geo.annotation} annotation The annotation that has an edit handle + * selected or unselected. + * @property {object} handle Information on the edit handle. + * @property {boolean} enable Truthy if the handle was enabled, falsy if + * disabled. + */ +geo_event.annotation.select_edit_handle = 'geo_annotation_select_edit_handle'; + +/** + * Triggered when an action is performed on an annotation's edit handle. + * + * @event geo.event.annotation.edit_action + * @type {object} + * @property {geo.annotation} annotation The annotation that has an edit handle + * selected or unselected. + * @property {object} handle Information on the edit handle. + * @property {boolean} action The edit action, typically one of + * `geo.event.actiondown`, `geo.event.actionmove`, `geo.event.actionup`. + */ +geo_event.annotation.edit_action = 'geo_annotation_edit_action'; + /** * Triggered when an annotation has been removed. * diff --git a/tests/cases/annotationLayer.js b/tests/cases/annotationLayer.js index 52a83ec24e..5e25c1739e 100644 --- a/tests/cases/annotationLayer.js +++ b/tests/cases/annotationLayer.js @@ -332,7 +332,7 @@ describe('geo.annotationLayer', function () { }); }); describe('Private utility functions', function () { - var map, layer, point, rect, rect2; + var map, layer, point, rect, rect2, editActionEvent = 0; it('_update', function () { sinon.stub(console, 'warn', function () {}); /* Most of update is covered as a side effect of other code. This tests @@ -610,14 +610,19 @@ describe('geo.annotationLayer', function () { data: layer.features()[layer.features().length - 1].data()[0] }, true); expect(layer.annotations()[0].coordinates()[0].y).toBeCloseTo(14.77); + map.geoOn(geo.event.annotation.edit_action, function (evt) { + editActionEvent += 1; + }); layer._processAction({ mouse: {mapgcs: map.displayToGcs({x: 10, y: 33}, null)}, state: { actionRecord: {owner: geo.annotation.actionOwner}, origin: {mapgcs: map.displayToGcs({x: 10, y: 20}, null)} - } + }, + event: geo.event.actionup }); expect(layer.annotations()[0].coordinates()[0].y).toBeCloseTo(13.67); + expect(editActionEvent).toBe(1); }); it('_selectEditHandle', function () { layer.removeAllAnnotations();