From 130c89d193ce29ae831fbf8e02ab77b9c21d181f Mon Sep 17 00:00:00 2001 From: David Manthey Date: Fri, 2 Sep 2016 08:15:45 -0400 Subject: [PATCH] Add maintenance functions including removeAll and coordinate output. Clean up a bunch of code and add comments. --- examples/annotations/example.json | 2 +- examples/annotations/main.js | 18 +- examples/annotations/thumb.jpg | Bin 62405 -> 0 bytes src/annotationLayer.js | 597 ++++++++++++++++++++++-------- src/event.js | 35 ++ 5 files changed, 503 insertions(+), 149 deletions(-) delete mode 100755 examples/annotations/thumb.jpg diff --git a/examples/annotations/example.json b/examples/annotations/example.json index c8ee61f208..11d603ae4b 100644 --- a/examples/annotations/example.json +++ b/examples/annotations/example.json @@ -4,6 +4,6 @@ "exampleCss": ["main.css"], "exampleJs": ["main.js"], "about": { - "text": "This example shows how to add annotations, such as marked rectangles, to a map." + "text": "This example shows how to add annotations, such as marked rectangles, to a map. Left click to add a polygon, right click to add a rectangle." } } diff --git a/examples/annotations/main.js b/examples/annotations/main.js index 71fedc5749..c2cb96e90f 100644 --- a/examples/annotations/main.js +++ b/examples/annotations/main.js @@ -6,6 +6,19 @@ var annotationDebug = {}; $(function () { 'use strict'; + var layer; + + function _mouseClickToStart(evt) { + if (evt.handled) { + return; + } + if (!layer.mode()) { + layer.mode(evt.buttonsDown.left ? 'polygon' : 'rectangle'); + } else { + layer.mode(null); + } + } + var query = utils.getQuery(); var map = geo.map({ node: '#map', @@ -23,10 +36,11 @@ $(function () { annotationDebug.satelliteLayer = map.createLayer('osm', {url: 'http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png', opacity: query.map === 'dual' ? 0.25 : 1}); } } - var layer = map.createLayer('annotation', { + layer = map.createLayer('annotation', { renderer: query.renderer ? (query.renderer === 'html' ? null : query.renderer) : undefined, - features: query.renderer ? undefined : ['polygon'] + features: query.renderer ? undefined : ['polygon', 'line'] }); + layer.geoOn(geo.event.mouseclick, _mouseClickToStart); map.draw(); diff --git a/examples/annotations/thumb.jpg b/examples/annotations/thumb.jpg deleted file mode 100755 index 7ed24adbc52cbac89663c700fc01df41cb7a0e52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62405 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 z�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 KyGriw%>My(q^AP_ diff --git a/src/annotationLayer.js b/src/annotationLayer.js index 29ea428f5d..b8d2361cf9 100644 --- a/src/annotationLayer.js +++ b/src/annotationLayer.js @@ -3,16 +3,30 @@ var featureLayer = require('./featureLayer'); var geo_action = require('./action'); var geo_event = require('./event'); var registry = require('./registry'); +var transform = require('./transform'); var $ = require('jquery'); var annotationId = 0; ///////////////////////////////////////////////////////////////////////////// /** - * Layer to handle direct interactions with different features. + * Layer to handle direct interactions with different features. Annotations + * (features) can be created by calling mode() or cancelled + * with mode(null). * * @class geo.annotationLayer * @extends geo.featureLayer + * @param {object?} options + * @param {number} [options.dblClickTime=300] The delay in milliseconds that + * is treated as a double-click when working with annotations. + * @param {number} [options.adjacentPointProximity=5] The minimum distance in + * display coordinates (pixels) between two adjacent points when creating a + * polygon. A value of 0 requires an exact match. + * @param {number} [options.finalPointProximity=10] The maximum distance in + * display coordinates (pixels) between the starting point and the mouse + * coordinates to signal closing a polygon. A value of 0 requires an exact + * match. A negative value disables closing a polygon by clicking on the + * start point. * @returns {geo.annotationLayer} */ ///////////////////////////////////////////////////////////////////////////// @@ -40,154 +54,169 @@ var annotationLayer = function (args) { m_options = $.extend(true, {}, { dblClickTime: 300, - actions: [{ - action: 'annotationmenu', // this will redirect to the appropriate menu - input: 'right' - }], - generalMenuOptions: { // null on a menu entry prevents it from showing that item - default: 'rectangle', - metakeys: '', - togeojson: true, - fromgeojson: true, - deleteall: true, - showall: true, - hideall: true - }, - generalMenuText: { // override for different locales - default: 'Make %s', - metakeys: '', - togeojson: 'Copy to Clipboard', - fromgeojson: 'Copy from Clipboard', - deleteall: 'Delete Annotations', - showall: 'Show All', - hideall: 'Hide All' - }, - annotationMenuOptions: { - name: true, - delete: true, - rename: true, - style: true, - move: true, // move to top, up, down, to bottom - hide: true - }, - annotationMenuText: { - name: 'Name: %s', - delete: 'Delete', - rename: 'Rename', - style: 'Options Dialog', - movetotop: 'Move to Top', - moveup: 'Move Up', - movedown: 'Move Down', - movetobottom: 'Move to Bottom' - } + adjacentPointProximity: 5, // in pixels, 0 is exact + finalPointProximity: 10 // in pixels, 0 is exact }, args); - this._processSelection = function (event) { + m_actions = { + rectangle: { + action: geo_action.annotation_rectangle, + owner: 'annotationLayer', + input: 'left', + modifiers: {shift: false, ctrl: false}, + selectionRectangle: true + } + }; + + /** + * Process a selection event. If we are in rectangle-creation mode, this + * creates a rectangle. + * + * @param {geo.event} evt the selection event. + */ + this._processSelection = function (evt) { if (m_this.mode() === 'rectangle') { m_this.mode(null); - if (event.state.action === geo_action.annotation_rectangle) { + if (evt.state.action === geo_action.annotation_rectangle) { var map = m_this.map(); var params = { corners: [ /* Keep in map gcs, not interface gcs to avoid wrapping issues */ - map.displayToGcs({x: event.lowerLeft.x, y: event.lowerLeft.y}, null), - map.displayToGcs({x: event.lowerLeft.x, y: event.upperRight.y}, null), - map.displayToGcs({x: event.upperRight.x, y: event.upperRight.y}, null), - map.displayToGcs({x: event.upperRight.x, y: event.lowerLeft.y}, null) - ] + map.displayToGcs({x: evt.lowerLeft.x, y: evt.lowerLeft.y}, null), + map.displayToGcs({x: evt.lowerLeft.x, y: evt.upperRight.y}, null), + map.displayToGcs({x: evt.upperRight.x, y: evt.upperRight.y}, null), + map.displayToGcs({x: evt.upperRight.x, y: evt.lowerLeft.y}, null) + ], + layer: this }; this.addAnnotation(rectangleAnnotation(params)); } } }; + /** + * Handle mouse movement. If there is a current annotation, the movement + * event is sent to it. + * + * @param {geo.event} evt the mouse move event. + */ this._handleMouseMove = function (evt) { - if (!m_this.mode() || !m_this.currentAnnotation) { - return; - } - switch (m_this.mode()) { - case 'polygon': - var vertices = m_this.currentAnnotation.options().vertices; - if (vertices.length) { - vertices[vertices.length - 1] = evt.mapgcs; - } + if (this.mode() && this.currentAnnotation) { + var update = this.currentAnnotation.mouseMove(evt); + if (update) { m_this.modified(); m_this._update(); m_this.draw(); - break; + } } }; + /** + * Handle mouse clicks. If there is a current annotation, the click event is + * sent to it. + * + * @param {geo.event} evt the mouse click event. + */ this._handleMouseClick = function (evt) { - switch (m_this.mode()) { - case 'polygon': - var end = !!evt.buttonsDown.right, skip; - if (!evt.buttonsDown.left && !evt.buttonsDown.right) { + if (this.mode() && this.currentAnnotation) { + var update = this.currentAnnotation.mouseClick(evt); + switch (update) { + case 'remove': + m_this.removeAnnotation(m_this.currentAnnotation, false); break; - } - var vertices = m_this.currentAnnotation.options().vertices; - if (evt.buttonsDown.left) { - if (vertices.length) { - if (vertices.length >= 2 && - vertices[vertices.length - 2].x === evt.mapgcs.x && - vertices[vertices.length - 2].y === evt.mapgcs.y) { - skip = true; - if (m_this.currentAnnotation.lastClick && - evt.time - m_this.currentAnnotation.lastClick < m_options.dblClickTime) { - end = true; - } - } else if (vertices.length >= 2 && vertices[0].x === evt.mapgcs.x && - vertices[0].y === evt.mapgcs.y) { - end = true; - } else { - vertices[vertices.length - 1] = evt.mapgcs; - } - } else { - vertices.push(evt.mapgcs); - } - if (!end && !skip) { - vertices.push(evt.mapgcs); - } - m_this.currentAnnotation.lastClick = evt.time; - } else { - if (vertices.length > 1) { - vertices.pop(); - } - } - if (end) { - if (vertices.length < 3) { - m_this.removeAnnotation(m_this.currentAnnotation, false); - } else { - m_this.currentAnnotation.state('done'); - } + case 'done': m_this.mode(null); - } + break; + } + if (update) { m_this.modified(); m_this._update(); m_this.draw(); - evt.handled = true; - return; + } } }; - this._mouseClickToStart = function (evt) { - if (evt.handled) { - return; + /** + * Set or get options. + * + * @param {string|object} arg1 if undefined, return the options object. If + * a string, either set or return the option of that name. If an object, + * update the options with the object's values. + * @param {object} arg2 if arg1 is a string and this is defined, set the + * option to this value. + * @returns {object|this} if options are set, return the layer, otherwise + * return the requested option or the set of options. + */ + this.options = function (arg1, arg2) { + if (arg1 === undefined) { + return m_options; } - if (!m_this.mode()) { - m_this.mode(evt.buttonsDown.left ? 'polygon' : 'rectangle'); + if (typeof arg1 === 'string' && arg2 === undefined) { + return m_options[arg1]; + } + if (arg2 === undefined) { + m_options = $.extend(m_options, arg1); } else { - m_this.mode(null); + m_options[arg1] = arg2; } + this.modified(); + return this; }; + /** + * Calculate the display distance for two coordinate in the current map. + * + * @param {object} coord1 the first coordinates. + * @param {string|geo.transform} [gcs1] undefined to use the interface gcs, + * null to use the map gcs, 'display' if the coordinates are already in + * display coordinates, or any other transform. + * @param {object} coord2 the second coordinates. + * @param {string|geo.transform} [gcs2] undefined to use the interface gcs, + * null to use the map gcs, 'display' if the coordinates are already in + * display coordinates, or any other transform. + * @returns {number} the Euclidian distance between the two coordinates. + */ + this.displayDistance = function (coord1, gcs1, coord2, gcs2) { + var map = this.map(); + if (gcs1 !== 'display') { + gcs1 = (gcs1 === null ? map.gcs() : ( + gcs1 === undefined ? map.ingcs() : gcs1)); + coord1 = map.gcsToDisplay(coord1, gcs1); + } + if (gcs2 !== 'display') { + gcs2 = (gcs2 === null ? map.gcs() : ( + gcs2 === undefined ? map.ingcs() : gcs2)); + coord2 = map.gcsToDisplay(coord2, gcs2); + } + var dist = Math.sqrt(Math.pow(coord1.x - coord2.x, 2) + + Math.pow(coord1.y - coord2.y, 2)); + return dist; + }; + + /** + * Add an annotation to the layer. The annotation could be in any state. + * + * @param {object} annotation the annotation to add. + */ this.addAnnotation = function (annotation) { + m_this.geoTrigger(geo_event.annotation.add, { + annotation: annotation + }); m_annotations.push(annotation); this.modified(); this._update(); this.draw(); + return this; }; + /** + * Remove an annotation from the layer. + * + * @param {object} annotation the annotation to remove. + * @param {boolean} update if false, don't update the layer after removing + * the annotation. + * @returns {boolean} true if an annotation was removed. + */ this.removeAnnotation = function (annotation, update) { var pos = $.inArray(annotation, m_annotations); if (pos >= 0) { @@ -197,10 +226,45 @@ var annotationLayer = function (args) { this._update(); this.draw(); } + m_this.geoTrigger(geo_event.annotation.remove, { + annotation: annotation + }); } return pos >= 0; }; + /** + * Remove all annotations from the layer. + * + * @returns {number} the number of annotations that were removed. + */ + this.removeAllAnnotations = function () { + var removed = 0; + $.each(this.annotations, function (idx, annotation) { + if (this.removeAnnotation(annotation, false)) { + removed += 1; + } + }); + return removed; + }; + + /** + * Get the list of annotations on the layer. + * + * @returns {array} An array of annotations. + */ + this.annotations = function () { + return m_annotations.slice(); + }; + + /** + * Get or set the current mode. The mode is either null for nothing being + * created, or the name of the type of annotation that is being created. + * + * @param {string|null} arg the new mode or undefined to get the current + * mode. + * @returns {string|null|this} The current mode or the layer. + */ this.mode = function (arg) { if (arg === undefined) { return m_mode; @@ -208,10 +272,20 @@ var annotationLayer = function (args) { if (arg !== m_mode) { m_mode = arg; m_this.map().node().css('cursor', m_mode ? 'crosshair' : ''); - this.currentAnnotation = null; + if (this.currentAnnotation) { + switch (this.currentAnnotation.state()) { + case 'create': + this.removeAnnotation(this.currentAnnotation); + break; + } + this.currentAnnotation = null; + } switch (m_mode) { case 'polygon': - this.currentAnnotation = polygonAnnotation({state: 'create'}); + this.currentAnnotation = polygonAnnotation({ + state: 'create', + layer: this + }); this.addAnnotation(m_this.currentAnnotation); break; case 'rectangle': @@ -221,7 +295,9 @@ var annotationLayer = function (args) { if (m_mode !== 'rectangle') { m_this.map().interactor().removeAction(m_actions.rectangle); } + m_this.geoTrigger(geo_event.annotation.mode, {mode: m_mode}); } + return this; }; /////////////////////////////////////////////////////////////////////////// @@ -231,6 +307,12 @@ var annotationLayer = function (args) { /////////////////////////////////////////////////////////////////////////// this._update = function (request) { if (m_this.getMTime() > m_buildTime.getMTime()) { + /* Interally, we have a set of feature levels (to provide z-index + * support), each of which can have data from multiple annotations. We + * clear the data on each of these features, then build it up from each + * annotation. Eventually, it may be necessary to optimize this and + * only update the features that are changed. + */ $.each(m_features, function (idx, featureLevel) { $.each(featureLevel, function (type, feature) { feature.data = []; @@ -243,6 +325,7 @@ var annotationLayer = function (args) { m_features[idx] = {}; } $.each(featureLevel, function (type, featureSpec) { + /* Create features as needed */ if (!m_features[idx][type]) { try { var feature = m_this.createFeature(type, { @@ -259,6 +342,12 @@ var annotationLayer = function (args) { } return; } + /* Since each annotation can have separate styles, the styles are + * combined together with a meta-style function. Any style that + * could be used should be in this list. Color styles may be + * restricted to {r, g, b} objects for efficiency, but this + * hasn't been tested. + */ var style = {}; $.each(['fill', 'fillColor', 'fillOpacity', 'line', 'polygon', 'position', 'stroke', 'strokeColor', 'strokeOpacity', @@ -289,10 +378,12 @@ var annotationLayer = function (args) { data: [] }; } + /* Collect the data for each feature */ m_features[idx][type].data.push(featureSpec.data || featureSpec); }); }); }); + /* Update the data for each feature */ $.each(m_features, function (idx, featureLevel) { $.each(featureLevel, function (type, feature) { feature.feature.data(feature.data); @@ -320,9 +411,6 @@ var annotationLayer = function (args) { m_this.geoOn(geo_event.mouseclick, m_this._handleMouseClick); m_this.geoOn(geo_event.mousemove, m_this._handleMouseMove); - m_this.geoOn(geo_event.mouseclick, m_this._mouseClickToStart); - //DWM:: capture mouse actions on the layer and on child features. - console.log(m_options); //DWM:: return m_this; }; @@ -334,19 +422,11 @@ var annotationLayer = function (args) { this._exit = function () { /// Call super class exit s_exit.call(m_this); + m_annotations = []; + m_features = []; return m_this; }; - m_actions = { - rectangle: { - action: geo_action.annotation_rectangle, - owner: 'annotationLayer', - input: 'left', - modifiers: {shift: false, ctrl: false}, - selectionRectangle: true - } - }; - return m_this; }; @@ -357,6 +437,17 @@ module.exports = annotationLayer; ///////////////////////////////////////////////////////////////////////////// /** * Base annotation class + * + * @class geo.annotation + * @param {string} type the type of annotation. + * @param {object?} options Inidividual annotations have additional options. + * @param {string} [options.name] A name for the annotation. This defaults to + * the type with a unique ID suffixed to it. + * @param {geo.annotationLayer} [options.layer] a reference to the controlling + * layer. This is used for coordinate transforms. + * @param {string} [options.state] initial annotation state. One of 'create', + * 'done', or 'edit'. + * @returns {geo.annotation} */ ///////////////////////////////////////////////////////////////////////////// var annotation = function (type, args) { @@ -369,13 +460,26 @@ var annotation = function (type, args) { m_id = annotationId, m_name = args.name || (args.type + ' ' + annotationId), m_type = type, + m_layer = args.layer, m_state = args.state || 'done'; /* create, done, edit */ annotationId += 1; + /** + * Get a unique annotation id. + + * @returns {number} the annotation id. + */ this.id = function () { return m_id; }; + /** + * Get or set the name of this annotation. + * + * @param {string|undefined} arg if undefined, return the name, otherwise + * change it. + * @returns {this|string} the current name or this annotation. + */ this.name = function (arg) { if (arg === undefined) { return m_name; @@ -384,6 +488,28 @@ var annotation = function (type, args) { return this; }; + /** + * Get or set the annotation layer associated with this annotation. + * + * @param {geo.annotationLayer|undefined} arg if undefined, return the layer, + * otherwise change it. + * @returns {this|geo.annotationLayer} the current layer or this annotation. + */ + this.layer = function (arg) { + if (arg === undefined) { + return m_layer; + } + m_layer = arg; + return this; + }; + + /** + * Get or set the state of this annotation. + * + * @param {string|undefined} arg if undefined, return the state, otherwise + * change it. + * @returns {this|string} the current state or this annotation. + */ this.state = function (arg) { if (arg === undefined) { return m_state; @@ -392,33 +518,109 @@ var annotation = function (type, args) { return this; }; + /** + * Get the options for this annotation. + * + * @returns {object} the current options. + */ this.options = function () { return m_options; }; + /** + * Get the type of this annotation. + * + * @returns {string} the annotation type. + */ this.type = function () { return m_type; }; - // features //DWM:: + /** + * Get a list of renderable features for this annotation. The list index is + * functionally a z-index for the feature. Each entry is a dictionary with + * the key as the feature name (such as line, quad, or polygon), and the + * value a dictionary of values to pass to the feature constructor, such as + * style and coordinates. + * + * @returns {array} an array of features. + */ + this.features = function () { + return []; + }; + + /** + * Handle a mouse click on this annotation. If the event is processed, + * evt.handled should be set to true to prevent further processing. + * + * @param {geo.event} evt the mouse click 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.mouseClick = function (evt) { + }; - this.dragControls = function () { - /* return a list of drag control structures. This is the four corners, the - * four edges, the central region, and a rotation handle. The drag - * controls should always use the adjustment styling, and could also - * specify the appropriate cursor to show over them. */ - //DWM:: + /** + * Handle a mouse move on this annotation. + * + * @param {geo.event} evt the mouse move event. + * @returns {boolean|string} true to update the annotation, falsy to not + * update anything. + */ + this.mouseMove = function (evt) { }; + /** + * Get coordinates associated with this annotation in the map gcs coordinate + * system. + * + * @returns {array} an array of coordinates. + */ + this._coordinates = function () { + return []; + }; + + /** + * Get coordinates associated with this annotation. + * + * @param {string|geo.transform} [gcs] undefined to use the interface gcs, + * null to use the map gcs, or any other transform. + * @returns {array} an array of coordinates. + */ + this.coordinates = function (gcs) { + var coord = this._coordinates(); + var map = this.layer().map(); + gcs = (gcs === null ? map.gcs() : ( + gcs === undefined ? map.ingcs() : gcs)); + if (gcs !== map.gcs()) { + coord = transform.transformCoordinates(map.gcs(), gcs, coord); + } + return coord; + }; + + /** + * TODO: return the annotation as a geojson object + */ this.geojson = function () { - // return the annotation as a geojson object - //DWM:: + return 'not implemented'; }; }; ///////////////////////////////////////////////////////////////////////////// /** * Rectangle annotation class + * + * Rectangles are always rendered as polygons. This could be changed -- if no + * stroke is specified, the quad feature would be sufficient and work on more + * renderers. + * + * Must specify: + * corners: a list of four corners {x: x, y: y} in map gcs coordinates. + * May specify: + * style. + * fill, fillColor, fillOpacity, stroke, strokeWidth, strokeColor, + * strokeOpacity */ ///////////////////////////////////////////////////////////////////////////// var rectangleAnnotation = function (args) { @@ -426,12 +628,6 @@ var rectangleAnnotation = function (args) { if (!(this instanceof rectangleAnnotation)) { return new rectangleAnnotation(args); } - /* Must specify: - * corners: a list of four corners {x: x, y: y} in map gcs coordinates. - * May specify: - * fill, fillColor, fillOpacity, stroke, strokeWidth, strokeColor, - * strokeOpacity - */ args = $.extend(true, {}, { style: { fill: true, @@ -447,6 +643,11 @@ var rectangleAnnotation = function (args) { }, args || {}); annotation.call(this, 'rectangle', args); + /** + * Get a list of renderable features for this annotation. + * + * @returns {array} an array of features. + */ this.features = function () { var opt = this.options(); return [{ @@ -456,12 +657,35 @@ var rectangleAnnotation = function (args) { } }]; }; + + /** + * Get coordinates associated with this annotation in the map gcs coordinate + * system. + * + * @returns {array} an array of coordinates. + */ + this._coordinates = function () { + return this.options().corners; + }; }; inherit(rectangleAnnotation, annotation); ///////////////////////////////////////////////////////////////////////////// /** * Polygon annotation class + * + * When complete, polygons are rendered as polygons. During creation they are + * rendered as lines and polygons. + * + * Must specify: + * vertices: a list of vertices {x: x, y: y} in map gcs coordinates. + * May specify: + * style. + * fill, fillColor, fillOpacity, stroke, strokeWidth, strokeColor, + * strokeOpacity + * editstyle. + * fill, fillColor, fillOpacity, stroke, strokeWidth, strokeColor, + * strokeOpacity */ ///////////////////////////////////////////////////////////////////////////// var polygonAnnotation = function (args) { @@ -472,12 +696,6 @@ var polygonAnnotation = function (args) { var m_this = this; - /* Must specify: - * vertices: a list of vertices {x: x, y: y} in map gcs coordinates. - * May specify: - * fill, fillColor, fillOpacity, stroke, strokeWidth, strokeColor, - * strokeOpacity - */ args = $.extend(true, {}, { vertices: [], style: { @@ -515,6 +733,13 @@ var polygonAnnotation = function (args) { }, args || {}); annotation.call(this, 'polygon', args); + /** + * Get a list of renderable features for this annotation. When the polygon + * is done, this is just a single polygon. During creation this can be a + * polygon and line at z-levels 1 and 2. + * + * @returns {array} an array of features. + */ this.features = function () { var opt = this.options(); var state = this.state(); @@ -523,7 +748,7 @@ var polygonAnnotation = function (args) { case 'create': features = []; if (opt.vertices && opt.vertices.length >= 3) { - features[0] = { + features[1] = { polygon: { polygon: opt.vertices, style: opt.editstyle @@ -531,7 +756,7 @@ var polygonAnnotation = function (args) { }; } if (opt.vertices && opt.vertices.length >= 2) { - features[1] = { + features[2] = { line: { line: opt.vertices, style: opt.editstyle @@ -550,6 +775,86 @@ var polygonAnnotation = function (args) { } return features; }; + + /** + * Get coordinates associated with this annotation in the map gcs coordinate + * system. + * + * @returns {array} an array of coordinates. + */ + this._coordinates = function () { + return this.options().vertices; + }; + + /** + * Handle a mouse move on this annotation. + * + * @param {geo.event} evt the mouse move event. + * @returns {boolean|string} true to update the annotation, falsy to not + * update anything. + */ + this.mouseMove = function (evt) { + var vertices = this.options().vertices; + if (vertices.length) { + vertices[vertices.length - 1] = evt.mapgcs; + return true; + } + }; + + /** + * Handle a mouse click on this annotation. If the event is processed, + * evt.handled should be set to true to prevent further processing. + * + * @param {geo.event} evt the mouse click 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.mouseClick = function (evt) { + if (this.state() !== 'create') { + return; + } + var end = !!evt.buttonsDown.right, skip; + if (!evt.buttonsDown.left && !evt.buttonsDown.right) { + return; + } + var layer = this.layer(); + evt.handled = true; + var vertices = this.options().vertices; + if (evt.buttonsDown.left) { + if (vertices.length) { + if (vertices.length >= 2 && layer.displayDistance( + vertices[vertices.length - 2], null, evt.map, 'display') <= + layer.options('adjacentPointProximity')) { + skip = true; + if (this.lastClick && + evt.time - this.lastClick < layer.options('dblClickTime')) { + end = true; + } + } else if (vertices.length >= 2 && layer.displayDistance( + vertices[0], null, evt.map, 'display') <= + layer.options('finalPointProximity')) { + end = true; + } else { + vertices[vertices.length - 1] = evt.mapgcs; + } + } else { + vertices.push(evt.mapgcs); + } + if (!end && !skip) { + vertices.push(evt.mapgcs); + } + this.lastClick = evt.time; + } + if (end) { + if (vertices.length < 3) { + return 'remove'; + } + vertices.pop(); + this.state('done'); + return 'done'; + } + return (end || !skip); + }; }; inherit(polygonAnnotation, annotation); - diff --git a/src/event.js b/src/event.js index 85e49ea156..60e89d369c 100644 --- a/src/event.js +++ b/src/event.js @@ -428,4 +428,39 @@ geo_event.camera.projection = 'geo_camera_projection'; ////////////////////////////////////////////////////////////////////////////// geo_event.camera.viewport = 'geo_camera_viewport'; +//////////////////////////////////////////////////////////////////////////// +/** + * These events are triggered by the annotation layer. + * @namespace geo.event.annotation + */ +//////////////////////////////////////////////////////////////////////////// +geo_event.annotation = {}; + +////////////////////////////////////////////////////////////////////////////// +/** + * Triggered when an annotation is about to be added. + * + * @property {geo.annotation} annotation The annotation that will be added. + */ +////////////////////////////////////////////////////////////////////////////// +geo_event.annotation.add = 'geo_annotation_add'; + +////////////////////////////////////////////////////////////////////////////// +/** + * Triggered when an annotation has been removed. + * + * @property {geo.annotation} annotation The annotation that was removed. + */ +////////////////////////////////////////////////////////////////////////////// +geo_event.annotation.remove = 'geo_annotation_remove'; + +////////////////////////////////////////////////////////////////////////////// +/** + * Triggered when the annotation mode is changed. + * + * @property {string|null} mode the new annotation mode. + */ +////////////////////////////////////////////////////////////////////////////// +geo_event.annotation.mode = 'geo_annotation_mode'; + module.exports = geo_event;