Allow continuous drawing of polygon annotations.
Before, only clicking for discrete vertices was supported.  This adds
the same capabilities as for lines, using the same layer settings to
control if it is allowed and the smoothness.
MODIFY
CHANGELOG.md
CHANGELOG.md
## Unreleased
+### Features
+- Polygon annotations can be drawn in the same continuous smooth manner as line annotations.
+
### Changes
- Rename the d3 renderer to svg. d3 still works as an alias (#965)
- Rename the vgl renderer to webgl. vgl still works as an alias (#965)

MODIFY
src/annotation.js
src/annotation.js
@@ -1074,6 +1074,89 @@ var annotation = function (type, args) {
};
};
+/* Functions used by multiple annotations */
+
+/**
+ * Return actions needed for the specified state of this annotation.
+ *
+ * @param {object} m_this The current annotation instance.
+ * @param {function} s_actions The parent actions method.
+ * @param {string} [state] The state to return actions for. Defaults to
+ * the current state.
+ * @param {string} name The name of this annotation.
+ * @param {Array} originalArgs arguments to original call
+ * @returns {geo.actionRecord[]} A list of actions.
+ */
+function continuousVerticesActions(m_this, s_actions, state, name, originalArgs) {
+ if (!state) {
+ state = m_this.state();
+ }
+ switch (state) {
+ case annotationState.create:
+ return [{
+ action: geo_action['annotation_' + name],
+ name: name + ' create',
+ owner: annotationActionOwner,
+ input: 'left',
+ modifiers: {shift: false, ctrl: false}
+ }, {
+ action: geo_action['annotation_' + name],
+ name: name + ' create',
+ owner: annotationActionOwner,
+ input: 'pan'
+ }];
+ default:
+ return s_actions.apply(m_this, originalArgs);
+ }
+}
+
+/**
+ * Process actions to allow drawing continuous vertices for an annotation.
+ *
+ * @param {object} m_this The current annotation instance.
+ * @param {geo.event} evt The action event.
+ * @param {string} name The name of this annotation.
+ * @returns {boolean|string} `true` to update the annotation, `'done'` if the
+ * annotation was completed (changed from create to done state),
+ * `'remove'` if the annotation should be removed, falsy to not update
+ * anything.
+ */
+function continuousVerticesProcessAction(m_this, evt, name) {
+ var layer = m_this.layer();
+ if (m_this.state() !== annotationState.create || !layer ||
+ evt.state.action !== geo_action['annotation_' + name]) {
+ return;
+ }
+ var cpp = layer.options('continuousPointProximity');
+ var cpc = layer.options('continuousPointColinearity');
+ if (cpp || cpp === 0) {
+ var vertices = m_this.options('vertices');
+ if (!vertices.length) {
+ vertices.push(evt.mouse.mapgcs);
+ vertices.push(evt.mouse.mapgcs);
+ return true;
+ }
+ var dist = layer.displayDistance(vertices[vertices.length - 2], null, evt.mouse.map, 'display');
+ if (dist && dist > cpp) {
+ // combine nearly colinear points
+ if (vertices.length >= (m_this._lastClickVertexCount || 1) + 3) {
+ var d01 = layer.displayDistance(vertices[vertices.length - 3], null, vertices[vertices.length - 2], null),
+ d12 = dist,
+ d02 = layer.displayDistance(vertices[vertices.length - 3], null, evt.mouse.map, 'display');
+ if (d01 && d02) {
+ var costheta = (d02 * d02 - d01 * d01 - d12 * d12) / (2 * d01 * d12);
+ if (costheta > Math.cos(cpc)) {
+ vertices.pop();
+ }
+ }
+ }
+ vertices[vertices.length - 1] = evt.mouse.mapgcs;
+ vertices.push(evt.mouse.mapgcs);
+ return true;
+ }
+ }
+}
+
/**
* Rectangle annotation specification. Extends {@link geo.annotation.spec}.
*
@@ -1530,7 +1613,8 @@ var polygonAnnotation = function (args) {
delete args.coordinates;
annotation.call(this, 'polygon', args);
- var m_this = this;
+ var m_this = this,
+ s_actions = this.actions;
/**
* Get a list of renderable features for this annotation. When the polygon
@@ -1660,6 +1744,7 @@ var polygonAnnotation = function (args) {
vertices.push(evt.mapgcs);
}
m_this._lastClick = evt.time;
+ m_this._lastClickVertexCount = vertices.length;
}
if (end) {
if (vertices.length < 4) {
@@ -1672,6 +1757,30 @@ var polygonAnnotation = function (args) {
return !skip;
};
+ /**
+ * Return actions needed for the specified state of this annotation.
+ *
+ * @param {string} [state] The state to return actions for. Defaults to
+ * the current state.
+ * @returns {geo.actionRecord[]} A list of actions.
+ */
+ this.actions = function (state) {
+ return continuousVerticesActions(m_this, s_actions, state, 'polygon', arguments);
+ };
+
+ /**
+ * Process any actions for this annotation.
+ *
+ * @param {geo.event} evt The action event.
+ * @returns {boolean|string} `true` to update the annotation, `'done'` if the
+ * annotation was completed (changed from create to done state),
+ * `'remove'` if the annotation should be removed, falsy to not update
+ * anything.
+ */
+ this.processAction = function (evt) {
+ return continuousVerticesProcessAction(m_this, evt, 'polygon');
+ };
+
/**
* Return the coordinates to be stored in a geojson geometry object.
*
@@ -1934,26 +2043,7 @@ var lineAnnotation = function (args) {
* @returns {geo.actionRecord[]} A list of actions.
*/
this.actions = function (state) {
- if (!state) {
- state = m_this.state();
- }
- switch (state) {
- case annotationState.create:
- return [{
- action: geo_action.annotation_line,
- name: 'line create',
- owner: annotationActionOwner,
- input: 'left',
- modifiers: {shift: false, ctrl: false}
- }, {
- action: geo_action.annotation_line,
- name: 'line create',
- owner: annotationActionOwner,
- input: 'pan'
- }];
- default:
- return s_actions.apply(m_this, arguments);
- }
+ return continuousVerticesActions(m_this, s_actions, state, 'line', arguments);
};
/**
@@ -1966,39 +2056,7 @@ var lineAnnotation = function (args) {
* anything.
*/
this.processAction = function (evt) {
- var layer = m_this.layer();
- if (m_this.state() !== annotationState.create || !layer ||
- evt.state.action !== geo_action.annotation_line) {
- return;
- }
- var cpp = layer.options('continuousPointProximity');
- var cpc = layer.options('continuousPointColinearity');
- if (cpp || cpp === 0) {
- var vertices = m_this.options('vertices');
- if (!vertices.length) {
- vertices.push(evt.mouse.mapgcs);
- vertices.push(evt.mouse.mapgcs);
- return true;
- }
- var dist = layer.displayDistance(vertices[vertices.length - 2], null, evt.mouse.map, 'display');
- if (dist && dist > cpp) {
- // combine nearly colinear points
- if (vertices.length >= (m_this._lastClickVertexCount || 1) + 3) {
- var d01 = layer.displayDistance(vertices[vertices.length - 3], null, vertices[vertices.length - 2], null),
- d12 = dist,
- d02 = layer.displayDistance(vertices[vertices.length - 3], null, evt.mouse.map, 'display');
- if (d01 && d02) {
- var costheta = (d02 * d02 - d01 * d01 - d12 * d12) / (2 * d01 * d12);
- if (costheta > Math.cos(cpc)) {
- vertices.pop();
- }
- }
- }
- vertices[vertices.length - 1] = evt.mouse.mapgcs;
- vertices.push(evt.mouse.mapgcs);
- return true;
- }
- }
+ return continuousVerticesProcessAction(m_this, evt, 'line');
};
/**

MODIFY
tests/cases/annotationLayer.js
tests/cases/annotationLayer.js
@@ -68,7 +68,7 @@ describe('geo.annotationLayer', function () {
expect(layer.mode()).toBe('polygon');
expect(layer.annotations().length).toBe(1);
expect(layer.annotations()[0].id()).not.toBe(id);
- expect(map.interactor().hasAction(undefined, undefined, geo.annotation.actionOwner)).toBeNull();
+ expect(map.interactor().hasAction(undefined, undefined, geo.annotation.actionOwner)).not.toBeNull();
expect(layer.mode('rectangle')).toBe(layer);
expect(layer.mode()).toBe('rectangle');
expect(map.interactor().hasAction(undefined, undefined, geo.annotation.actionOwner)).not.toBeNull();

Allow continuous drawing of polygon annotations.

Before, only clicking for discrete vertices was supported.  This adds
the same capabilities as for lines, using the same layer settings to
control if it is allowed and the smoothness.