Source: util/editor/ShapeEditor.js

/*
 * Copyright 2003-2006, 2009, 2017, United States Government, as represented by the Administrator of the
 * National Aeronautics and Space Administration. All rights reserved.
 *
 * The NASAWorldWind/WebWorldWind platform is licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @exports ShapeEditor
 */
define([
        '../../shapes/Annotation',
        '../../shapes/AnnotationAttributes',
        '../../error/ArgumentError',
        '../Color',
        '../Font',
        '../Insets',
        '../../geom/Location',
        '../Logger',
        '../../shapes/Placemark',
        '../../shapes/PlacemarkAttributes',
        './PlacemarkEditorFragment',
        '../../geom/Position',
        '../../layer/RenderableLayer',
        '../../shapes/ShapeAttributes',
        './ShapeEditorConstants',
        './SurfaceEllipseEditorFragment',
        './SurfaceCircleEditorFragment',
        '../../shapes/SurfacePolygon',
        '../../shapes/SurfacePolyline',
        './SurfacePolygonEditorFragment',
        './SurfacePolylineEditorFragment',
        './SurfaceRectangleEditorFragment',
        './SurfaceSectorEditorFragment',
        '../../geom/Vec2',
        '../../geom/Vec3'
    ],
    function (Annotation,
              AnnotationAttributes,
              ArgumentError,
              Color,
              Font,
              Insets,
              Location,
              Logger,
              Placemark,
              PlacemarkAttributes,
              PlacemarkEditorFragment,
              Position,
              RenderableLayer,
              ShapeAttributes,
              ShapeEditorConstants,
              SurfaceEllipseEditorFragment,
              SurfaceCircleEditorFragment,
              SurfacePolygon,
              SurfacePolyline,
              SurfacePolygonEditorFragment,
              SurfacePolylineEditorFragment,
              SurfaceRectangleEditorFragment,
              SurfaceSectorEditorFragment,
              Vec2,
              Vec3) {
        "use strict";

        /**
         * Constructs a new shape editor attached to the specified World Window.
         * @alias ShapeEditor
         * @classdesc Provides a controller for editing shapes. Depending on the type of shape, the following actions
         * are available:
         * <ul>
         *     <li>Edit the location and size of its vertexes using control points;</li>
         *     <li>Rotate the shape using a handle;</li>
         *     <li>Drag the shape on the surface of the globe.</li>
         * </ul>
         * <p>
         * To start editing a shape, pass it to the {@link ShapeEditor#edit} method. To end the edition, call the
         * {@link ShapeEditor#stop} method.
         * <p>
         * Dragging the body of the shape moves the whole shape. Dragging a control point performs the action associated
         * with that control point. The editor provides vertex insertion and removal for SurfacePolygon and
         * SurfacePolyline. Shift-clicking when the cursor is over the shape inserts a control point near the position
         * of the cursor. Ctrl-clicking when the cursor is over a control point removes that particular control point.
         * <p>
         * This editor currently supports all surface shapes except SurfaceImage.
         * @param {WorldWindow} worldWindow The World Window to associate this shape editor controller with.
         * @throws {ArgumentError} If the specified World Window is <code>null</code> or <code>undefined</code>.
         * @constructor
         */
        var ShapeEditor = function (worldWindow) {
            if (!worldWindow) {
                throw new ArgumentError(Logger.logMessage(Logger.LEVEL_SEVERE, "ShapeEditor", "constructor",
                    "missingWorldWindow"));
            }

            // Documented in defineProperties below.
            this._worldWindow = worldWindow;

            // Documented in defineProperties below.
            this._shape = null;

            // Internal use only.
            // Flags indicating whether the specific action is allowed or not.
            this._allowMove = true;
            this._allowReshape = true;
            this._allowRotate = true;
            this._allowManageControlPoint = true;

            // Internal use only
            // List of highlighted control points - on mouse over
            this._highlightedItems = [];

            // Documented in defineProperties below (blue dot image).
            this._moveControlPointAttributes = new PlacemarkAttributes(null);
            this._moveControlPointAttributes.imageSource = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAG1klEQVR42uVbaWxUVRSelsoishPDGpeIKVERNRHEhEQkRCL6QyLBBcpWisi+KhDCLsawiEshRECKFAIYisgmIIRChbAVK0sboAulC22n20w77917juddcjrXcaB7p/P4ki+vue++eT3fu+s55zrqG4jYhNiPON4UsKKgFPbkl8DF0nLIERIkURS7oSCvBJJziuCk0wUxhoDpiNiLGOoIRiBiM+Lg4jLY6vZAEdYQRW4oT8mGBBLHEqRDMBj+an4p7Cs3oaxqBiKWm1gl0G/K87fg9IHLclBjNPx5arr70Q8AEC+mAq46KHHoOoF9Fpn41HQTm48x0DHCwJCRBraNMrDnXBPfWmHixz8KXLhHYHwyoAT0i+QsyE7KgAhEDAm04d2omccICYAahETcfU7iB98KbD/BMrRm7PCZgR+RIDGnJeaV4P/wdwaknLohXw6E4SElZTDZEGCihpwixKV7JXabaloG1CmbjjZw/CaBaXmAOjwmAHWLA8TWDWX84zQ47UINJWWIM34R2Gx0JYbUkRBRJER6PqCO1HtQeviKfK2+je9B01cKath+RmKXKeqLNySV2FNiBJYZWIFCN8hDiXJ0fRk/hJqbm1+WXYg4cKUyPKB8ZYGJN3MAGVICHr8qo+t0gDQFDDIlSH7J+dvg7eeBp5pF4i5I1HE0SR6uqy//On15j97kW4y1Xty4GErT6dydAvX56NAVubm2xvck44v5B1cflDRvWy9stFSDsY4T1+T8mhrf3RCQhfeh5uLQRm48L6yij0lkmALwchoMr67xofTlT/OPHEwEfGyU9YLgYNgoQ/3PDNqAGfE3ZJfqCBDFDyemAz4RGTzGM1uPN/BKuleE08nyelWN72pKKL3ffBB7z1ejfVDyadpzuD1YgTPJMqrSJS4xjh9YslcGrfHMZXESGU6X2qW2f5gAQ7ly0h3ws7QNzq6QW4yoden1D/z6EuAiVxzwlWr6tuDELUIfED20j2jhT4A+qKD27zzf24FqBrt+F5BBq8bV/gT4mSt8Ei2sB23FYd8LZKTnQaHV4nXjO0oADxLuFPCcbyuq5bvLg/qAOEAXYA7fWB6nRn47UnmpGJdSYbsuQDzfGPyNaVcBVNdmkGcpl40PI7r5RrsJ9jSet82GwAqQc6ezJUAvLrh2F6yKtuahK4AMcpzMtAQYywWbTkrbCzBvl9C3ytssAdZzQeRPwvYCjFjvFeDCbUiwBPiLC1780rS7ACr4wiBfYoYlQMXyt+NEw/YCPDfLKwAFaV2WAOe44MnP7S8ALYj0xZDHEiCBCzpPtirZn3klFXEEYQlwigXoygEOmzOzAHgdoAQ4wQJ0n/poCMDRJLcHTEuAoywAuZAeqTEguxAKLQEOc0GP2bYXQHVzBmWf3LIE2M0FFM+3vQBvLvEKcDnt/kJoGhesPWT/pfDsWIGMszflDkuAlzRXmO0F2KsFUeOTZSS7wnM4n6dtlH2NDxmpPMQ62rNPIJZL3l1lz4GQcwkYmU7I0T1C4/jG1/vtOw58d0TqG6GdugDP8I2rmUBRYHvO/04XVsAQ0M/XLX6Lbw7/wXbToco/ZNx1QgG7xXUBFnKFG1mAYTZyjVOLtmY43SO81l9gpBUxlyuN2WifVhCxQeh5hZIunR4UH5zmdRvbIzjacpxhBXv00X/9w6LDzS3bufKkrcHfChb9KnU3eBld2laWIzDam/oa3E6SNxabaArUd38LHJWAAyVJ3vUyBDAtrnaJ1hQG143PpEuzqqbJhBML+eHYBCs9LrhG/d8ve40vM8AsNyC8upli7xC5A1k5/EGz3l93RPqePBlV01zBGahh2jZBL2jUxqstvQ6nC7bW6lwAcTNq2HBcUv5A4zR+jY/xGfmwjy5N6uIA1AnUcOwf0E6CBJ6tIg01ThF0d9eBujp1xiJs9jm3g30XB37r/MIXppb/w9M37Ki98f67wzSiRA1bTknsNKnhDW8SoU6O/CftxZQAzlJY5Gt8XQsxSAIUoobiMsRZsQKbNtDS+e2Vpkrh1ZFbDE6XB/o21LmhHsREou+gow5NPTvTrJe5vf8yE3+7BOiL1Huqv7dyNCD4SGwEMR394PhVwE+jBbappY+x1zzT8lLpqzq9r183BQx0BBC8gZohAQrQP6xBSp03mErriH5LTLW/aDdBeWnY+6S6T/gcE99fI1R32vinVOm6/pDlhDRa3Lzn69QItBBtiMuJ97Ca8JiIEiqrAwYFNf8wBQwJvOGVd43+xDXEVKwFDAEGJTFYobsPiS0dQQaeOnsTRxGXErcTz0qAPCGhiL5qgasc8tweuEN/pwipzh8vIw4jhhPDHPWMfwG/nw93W8wmlQAAAABJRU5ErkJggg==";
            this._moveControlPointAttributes.imageScale = 0.15;

            // Documented in defineProperties below (grey dot image).
            this._shadowControlPointAttributes = new PlacemarkAttributes(null);
            this._shadowControlPointAttributes.imageSource = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAACx1BMVEUAAAD////////////////////////6+vr////////////y8vL////////////////////////9/f309PT////////////////////////9/f3////////////////09PT////////y8vL////////9/f3+/v7////////8/Pz+/v7y8vL+/v7////////////+/v7////8/Pz////////////39/f39/f////8/Pz////y8vL////////////09PT////19fX////8/Pz////7+/v+/v7////z8/P////09PT7+/v////8/Pz////////////+/v7x8fH////29vbz8/P39/f6+vr////y8vL////////8/Pz8/Pz09PT29vb////////19fX7+/v5+fn8/Pz////8/Pz////6+vr8/Pz8/Pz+/v7y8vL4+Pj5+fnz8/P7+/v////6+vr////////7+/v////////29vb+/v729vb39/f4+Pj////////////////8/Pz+/v7////////////19fX7+/v7+/v39/f9/f34+Pj////////6+vr////////5+fn9/f37+/v+/v74+Pj6+vr9/f3////5+fn////////9/f3+/v76+vr8/Pz+/v77+/v6+vr7+/v+/v7////9/f3+/v7+/v75+fn+/v77+/v+/v7////8/Pz9/f38/Pz8/Pz+/v78/Pz+/v79/f3+/v78/Pz9/f3+/v78/Pz9/f3+/v7////+/v7T09PU1NTV1dXW1tbX19fY2NjZ2dna2trb29vc3Nzd3d3e3t7f39/g4ODh4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7///+ggSSQAAAAwHRSTlMAAQIDBAcICQoLDQ4PEBITFBgZGhscHSElJigoKS0uLzAyMzY3ODo6PkJERkZGSElMTE1TVVZYX19gYGFhY2RlZmdnbXBxcXJzdnt8foCDhIWGjo+Ql5iYmJ+foaKjpKamp6ioqausrbGysrS0tra2ubm5u7zAwcPExcXHx8fHyMnKzs7O0dTY2Nrb297e4OHh4uPk5efo6enp6urr7Ozu7u7v8PDw8PHx8vT09fb29/f4+fn6+vv7/Pz8/f39/f6yTS27AAADH0lEQVR4AZ3X93dUVRTF8f0SgoqKXVEUe++99yj2XhTB3nsRe++KvXfF3kUQLBpy3nspMYEkJCEJkBJkhplkvn+EZEgwQzLl3c/vZ6/7w1n7rqNsinc+5uzrnni37M9Pn79nyokTihRJyd6TvybDLw+fsJ4Kte2NMxjUl2LQ3/fvrkJsfhNpsfb6fyp9s7C6rrH1X9LePthTHptMKQdSXfWhZQgbOnoBXthauXjH/wYk2yptBH5TAii/ZW1lteY1QF+Lb2nZIr7bTlmMmwZ0VlgOfnMK/jpcI9r3R0jOszxqlkPqfE/D7VEGyyotr7AbuF3DbD8LOgMrxELgYq1my2+h3QrUApyiDJu+BR1WsMWQOkhDFD0APb4VzO+BXzfS/46DWGARBDF4Vqts/D2pGoukqg+O1QDvemi1iNrg57FaaX+I+xZRkIQLlOY9CXUWWRPMXEP9doCYRefH4Rz1uwQazUEDfO5JWn8WCd8cBH2wq6SToc2cdMFlku6D+eakEd6XRv0EoTkJU7ChJkDcHPXASToSlpijhXCpLoIF5qgRHtSjUGOO6uB1PQWhOaqCH/Q4VJijAGbqEdwDrJffdS9Umqskf2gquAekmKO7ococBfCF7sQ9oBJe1LVQb45q4SGVwiJz1AJXaiuImaMuOFree+6rmISx0hXOhVID70g6CtrNySK4WtJmro0S9MJOWuElaHBsg088rXAaxH2LLgbnqd+YD2CB0wPKNlBaKQ5fS5CAC7XS6Feh2SJqhRnrasARkKywSGpTcKoGjXoGlgUWQZiAN0u0yvivoNMi6IY5W2iIveZCa6Qd5FBlmAg0R5mfrEzeJGCxX/D8DcVaTclUYGloeQWdwK1FGqZkErC81vKojgNXFWkEXulcoCP3wdHUB+WnF2lke36Z++SxeTHgsx2V1binARJtVTaSum6A28Yoh+JDXgNgaWNomWraEwDP7aY8Rk+cTlq8o7m2IgzM/Or5LUviALyyn6f81jnrQ4ZIMWD2Xft4KkzxLue+TKbZdxywlqLwtjnsjMsf+/ib6R+9Me3mMw8cP0pZ/AewF57BEV8rHgAAAABJRU5ErkJggg==";
            this._shadowControlPointAttributes.imageScale = 0.15;

            // Documented in defineProperties below (yellow dot image).
            this._resizeControlPointAttributes = new PlacemarkAttributes(null);
            this._resizeControlPointAttributes.imageSource = "data:image/png;base64,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";
            this._resizeControlPointAttributes.imageScale = 0.15;

            // Documented in defineProperties below (green dot image).
            this._rotateControlPointAttributes = new PlacemarkAttributes(null);
            this._rotateControlPointAttributes.imageColor = WorldWind.Color.GREEN;
            this._rotateControlPointAttributes.imageSource = "data:image/png;base64,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";
            this._rotateControlPointAttributes.imageScale = 0.15;

            // Documented in defineProperties below.
            this._annotationAttributes = new AnnotationAttributes(null);
            this._annotationAttributes.altitudeMode = WorldWind.CLAMP_TO_GROUND;
            this._annotationAttributes.cornerRadius = 5;
            this._annotationAttributes.backgroundColor = new Color(0.67, 0.67, 0.67, 0.8);
            this._annotationAttributes.leaderGapHeight = 0;
            this._annotationAttributes.drawLeader = false;
            this._annotationAttributes.scale = 1;
            this._annotationAttributes.textAttributes.color = Color.BLACK;
            this._annotationAttributes.textAttributes.font = new Font(10);
            this._annotationAttributes.insets = new Insets(5, 5, 5, 5);

            // Internal use only.
            // The annotation that displays hints during the actions on the shape.
            this.annotation = new WorldWind.Annotation(new WorldWind.Position(0, 0, 0), this._annotationAttributes);

            //Internal use only. Intentionally not documented.
            this.editorFragments = [
                new PlacemarkEditorFragment(),
                new SurfaceCircleEditorFragment(),
                new SurfaceEllipseEditorFragment(),
                new SurfacePolygonEditorFragment(),
                new SurfacePolylineEditorFragment(),
                new SurfaceRectangleEditorFragment(),
                new SurfaceSectorEditorFragment()
            ];

            // Internal use only.
            // The layer that holds the control points created by the editor fragment.
            this.controlPointsLayer = new RenderableLayer("Shape Editor Control Points");

            // Internal use only.
            // The layer that holds the shadow control points created by the editor fragment.
            this.shadowControlPointsLayer = new RenderableLayer("Shape Editor Shadow Control Points");

            // Internal use only.
            // The layers that holds the additional accessories created by the editor fragment.
            this.accessoriesLayer = new RenderableLayer("Shape Editor Accessories");
            this.accessoriesLayer.pickEnabled = false;

            // Internal use only.
            // The layer that holds the above-mentioned annotation.
            this.annotationLayer = new RenderableLayer("Shape Editor Annotation");
            this.annotationLayer.pickEnabled = false;
            this.annotationLayer.enabled = false;
            this.annotationLayer.addRenderable(this.annotation);

            // Internal use only.
            // The layer that holds the shadow of the shape during the actions.
            this.shadowShapeLayer = new RenderableLayer("Shape Editor Shadow Shape");
            this.shadowShapeLayer.pickEnabled = false;

            // Internal use only.
            // The layer that holds the initial shapes created at shape creation by the create method.
            this.creatorShapeLayer = new RenderableLayer("Shape Creator Initial Shape");
            this.creatorShapeLayer.pickEnabled = false;

            // Internal use only.
            // The editor fragment selected for the shape being edited or null.
            this.activeEditorFragment = null;

            // Internal use only.
            // The type of action being conducted or null.
            this.actionType = null;

            // Internal use only.
            // The control point that triggered the current action or null.
            this.actionControlPoint = null;

            // Internal use only.
            // The lat/lon/alt position that is currently involved with the action or null.
            this.actionControlPosition = null;

            // Internal use only.
            // Flag indicating whether the action should trigger the secondary behavior in the editor fragment.
            this.actionSecondaryBehavior = false;

            // Internal use only.
            // The current client X position for the action.
            this.actionCurrentX = null;

            // Internal use only.
            // The current client Y position for the action.
            this.actionCurrentY = null;

            // Internal use only.
            // The original highlight attributes of the shape in order to restore them after the action.
            this.originalHighlightAttributes = new ShapeAttributes(null);
            this.originalPlacemarkHighlightAttributes = new PlacemarkAttributes(null);

            // Internal use only.
            // counters used to detect double click (time measured in ms)
            this._clicked0X = null;
            this._clicked0Y = null;
            this._clicked1X = null;
            this._clicked1Y = null;
            this._click0Time = 0;
            this._click1Time = 0;
            this._dbclickTimeout = 0;
            this._clickDelay = 200;

            // Internal use only.
            // Used for shape creation
            this.creatorEnabled = false;
            this.creatorShapeProperties = null;
            this.promisedShape = null;

            this._worldWindow.worldWindowController.addGestureListener(this);
        };

        Object.defineProperties(ShapeEditor.prototype, {
            /**
             * The World Window associated with this shape editor.
             * @memberof ShapeEditor.prototype
             * @type {WorldWindow}
             * @readonly
             */
            worldWindow: {
                get: function () {
                    return this._worldWindow;
                }
            },

            /**
             * The shape currently being edited.
             * @memberof ShapeEditor.prototype
             * @type {Object}
             * @readonly
             */
            shape: {
                get: function () {
                    return this._shape;
                }
            },

            /**
             * Attributes used for the control points that move the boundaries of the shape.
             * @memberof ShapeEditor.prototype
             * @type {PlacemarkAttributes}
             */
            moveControlPointAttributes: {
                get: function () {
                    return this._moveControlPointAttributes;
                },
                set: function (value) {
                    this._moveControlPointAttributes = value;
                }
            },

            /**
             * Attributes used for the shadow control points used to mask the middle of a segment.
             * @memberof ShapeEditor.prototype
             * @type {PlacemarkAttributes}
             */
            shadowControlPointAttributes: {
                get: function () {
                    return this._shadowControlPointAttributes;
                },
                set: function (value) {
                    this._shadowControlPointAttributes = value;
                }
            },

            /**
             * Attributes used for the control points that resize the shape.
             * @memberof ShapeEditor.prototype
             * @type {PlacemarkAttributes}
             */
            resizeControlPointAttributes: {
                get: function () {
                    return this._resizeControlPointAttributes;
                },
                set: function (value) {
                    this._resizeControlPointAttributes = value;
                }
            },

            /**
             * Attributes used for the control points that rotate the shape.
             * @memberof ShapeEditor.prototype
             * @type {PlacemarkAttributes}
             */
            rotateControlPointAttributes: {
                get: function () {
                    return this._rotateControlPointAttributes;
                },
                set: function (value) {
                    this._rotateControlPointAttributes = value;
                }
            },

            /**
             * Attributes used for the annotation that displays hints during the actions on the shape.
             * @memberof ShapeEditor.prototype
             * @type {AnnotationAttributes}
             */
            annotationAttributes: {
                get: function () {
                    return this._annotationAttributes;
                },
                set: function (value) {
                    this._annotationAttributes = value;
                    this.annotation.attributes = value;
                }
            }
        });

        /**
         * Creates the specified shape. Currently, only surface shapes are supported.
         * @param {SurfaceShape} shape The shape to edit.
         * @param {{}} properties Configuration properties for the shape:
         * <ul>
         *     <li>TODO: describe properties fro each shape</li>
         *     <li>attributes: {ShapeAttributes} attributes of the shape.</li>
         * <ul>
         * @return {Promise} <code>shape</code> if the creator can create the specified shape; otherwise
         * <code>null</code>.
         */
        ShapeEditor.prototype.create = function (shape, properties) {
            var res, rej;

            this.stop();
            this.setCreatorEnabled(true);

            for (var i = 0, len = this.editorFragments.length; i < len; i++) {
                var editorFragment = this.editorFragments[i];
                if (editorFragment.canHandle(shape)) {
                    this.activeEditorFragment = editorFragment;
                    this.creatorShapeProperties = properties;
                }
            }

            if (this.activeEditorFragment != null) {
                var promise = new Promise(function (resolve, reject) {
                    res = resolve;
                    rej = reject;
                });

                promise.resolve = res;
                promise.reject = rej;

                this.promisedShape = promise;

                return promise;
            } else {
                return null;
            }
        };

        /**
         * Identifies whether the shape editor create mode is armed.
         * @return true if armed, false if not armed.
         */
        ShapeEditor.prototype.isCreatorEnabled = function() {
            return this.creatorEnabled;
        };

        /**
         * Arms and disarms the shape editor create mode. When armed, editor monitors user input and builds the
         * shape in response to user actions. When disarmed, the shape editor ignores all user input for creation of a
         * new shape.
         *
         * @param armed true to arm the shape editor create mode, false to disarm it.
         */
        ShapeEditor.prototype.setCreatorEnabled = function(creatorEnabled) {
            if (this.creatorEnabled != creatorEnabled) {
                this.creatorEnabled = creatorEnabled;
            }
        };

        /**
         * Edits the specified shape. Currently, only surface shapes are supported.
         * @param {SurfaceShape} shape The shape to edit.
         * @param {{}} config Configuration properties for the ShapeEditor:
         * <ul>
         *     <li>move: {Boolean} move true to enable move action on shape, false to disable move action on shape.</li>
         *     <li>reshape: {Boolean} reshape true to enable reshape action on shape, false to disable reshape action on shape.</li>
         *     <li>rotate: {Boolean} rotate true to enable rotate action on shape, false to disable rotate action on shape.</li>
         *     <li>manageControlPoint: {Boolean} manageControlPoint true to enable the action to manage the control points of the shape, false to disable it.</li>
         * <ul>
         * @return {Boolean} <code>true</code> if the editor could start the edition of the specified shape; otherwise
         * <code>false</code>.
         */
        ShapeEditor.prototype.edit = function (shape, config) {
            this.stop();

            this._allowMove = Object.prototype.hasOwnProperty.call(config, 'move') ? config.move : this._allowMove;
            this._allowReshape = Object.prototype.hasOwnProperty.call(config, 'reshape') ? config.reshape : this._allowReshape;
            this._allowRotate = Object.prototype.hasOwnProperty.call(config, 'rotate') ?  config.rotate : this._allowRotate;
            this._allowManageControlPoint = Object.prototype.hasOwnProperty.call(config, 'manageControlPoint') ?  config.manageControlPoint : this._allowManageControlPoint;

            if (!this._allowReshape) {
                this._allowManageControlPoint = false;
            }

            // Look for a fragment that can handle the specified shape
            for (var i = 0, len = this.editorFragments.length; i < len; i++) {
                var editorFragment = this.editorFragments[i];
                if (editorFragment.canHandle(shape)) {
                    this.activeEditorFragment = editorFragment;
                }
            }

            // If we have a fragment for this shape, accept the shape and start the edition
            if (this.activeEditorFragment != null) {
                this._shape = shape;
                this._shape.highlighted = true;
                this.initializeControlElements();
                return true;
            }

            return false;
        };

        /**
         * Stops the current edition activity if any.
         * @return {SurfaceShape} The shape being edited if any; otherwise <code>null</code>.
         */
        ShapeEditor.prototype.stop = function () {
            this.removeControlElements();

            this.activeEditorFragment = null;

            this._allowMove = true;
            this._allowReshape = true;
            this._allowRotate = true;
            this._allowManageControlPoint = true;

            var currentShape = this._shape;
            this._shape = null;

            if (currentShape !== null) {
                currentShape.highlighted = false;
            }

            return currentShape;
        };

        // Internal use only.
        // Called by {@link ShapeEditor#edit} to initialize the control elements used for editing.
        ShapeEditor.prototype.initializeControlElements = function () {
            var moveControlAttributes = this._moveControlPointAttributes;
            var resizeControlAttributes = this._resizeControlPointAttributes;
            var rotateControlAttributes = this._rotateControlPointAttributes;
            var shadowControlAttributes = this._shadowControlPointAttributes;

            if (!this._allowMove) {
                moveControlAttributes = null;
            }

            if (!this._allowReshape) {
                resizeControlAttributes = null;
            }

            if (!this._allowRotate) {
                rotateControlAttributes = null;
            }

            if (!this._allowManageControlPoint) {
                shadowControlAttributes = null;
            }

            if (this._worldWindow.indexOfLayer(this.shadowShapeLayer) == -1) {
                this._worldWindow.insertLayer(0, this.shadowShapeLayer);
            }

            if (this._worldWindow.indexOfLayer(this.creatorShapeLayer) == -1) {
                this._worldWindow.addLayer(this.creatorShapeLayer);
            }

            if (this._worldWindow.indexOfLayer(this.controlPointsLayer) == -1) {
                this._worldWindow.addLayer(this.controlPointsLayer);
            }

            if (this._worldWindow.indexOfLayer(this.shadowControlPointsLayer) == -1) {
                this._worldWindow.addLayer(this.shadowControlPointsLayer);
            }


            if (this._worldWindow.indexOfLayer(this.accessoriesLayer) == -1) {
                this._worldWindow.addLayer(this.accessoriesLayer);
            }

            if (this._worldWindow.indexOfLayer(this.annotationLayer) == -1) {
                this._worldWindow.addLayer(this.annotationLayer);
            }

            if (this.isCreatorEnabled() &&
                (this.activeEditorFragment instanceof SurfacePolylineEditorFragment ||
                    this.activeEditorFragment instanceof SurfacePolygonEditorFragment)) {

                this.activeEditorFragment.initializeCreationControlElements(
                    this._shape,
                    this.controlPointsLayer.renderables,
                    moveControlAttributes
                );
            } else {
                this.activeEditorFragment.initializeControlElements(
                    this._shape,
                    this.controlPointsLayer.renderables,
                    this.shadowControlPointsLayer.renderables,
                    this.accessoriesLayer.renderables,
                    resizeControlAttributes,
                    rotateControlAttributes,
                    moveControlAttributes,
                    shadowControlAttributes
                );
            }

            this.updateControlElements();
        };

        // Internal use only.
        // Called by {@link ShapeEditor#stop} to remove the control elements used for editing.
        ShapeEditor.prototype.removeControlElements = function () {
            this._worldWindow.removeLayer(this.controlPointsLayer);
            this.controlPointsLayer.removeAllRenderables();

            this._worldWindow.removeLayer(this.creatorShapeLayer);
            this.creatorShapeLayer.removeAllRenderables();

            this._worldWindow.removeLayer(this.shadowControlPointsLayer);
            this.shadowControlPointsLayer.removeAllRenderables();

            this._worldWindow.removeLayer(this.accessoriesLayer);
            this.accessoriesLayer.removeAllRenderables();

            this._worldWindow.removeLayer(this.shadowShapeLayer);
            this.shadowShapeLayer.removeAllRenderables();

            this._worldWindow.removeLayer(this.annotationLayer);
        };

        // Internal use only.
        // Updates the position of the control elements.
        ShapeEditor.prototype.updateControlElements = function () {
            if (this.isCreatorEnabled() &&
                (this.activeEditorFragment instanceof SurfacePolylineEditorFragment ||
                    this.activeEditorFragment instanceof SurfacePolygonEditorFragment)) {
                this.activeEditorFragment.updateCreationControlElements(
                    this._shape,
                    this._worldWindow.globe,
                    this.controlPointsLayer.renderables
                );
            } else {
                this.activeEditorFragment.updateControlElements(
                    this._shape,
                    this._worldWindow.globe,
                    this.controlPointsLayer.renderables,
                    this.shadowControlPointsLayer.renderables,
                    this.accessoriesLayer.renderables
                );
            }
        };

        // Internal use only.
        // Dispatches the events relevant to the shape editor.
        ShapeEditor.prototype.onGestureEvent = function (event) {
            if(this._shape === null && !this.isCreatorEnabled()) {
                return;
            }

            // TODO Add support for touch devices

            if (event.type === "pointerup" || event.type === "mouseup") {
                this.handleMouseUp(event);
            } else if (event.type === "pointerdown" || event.type === "mousedown") {
                this.handleMouseDown(event);
            } else if (event.type === "pointermove" || event.type === "mousemove") {
                this.handleMouseMove(event);
            }
        };

        // Internal use only.
        // Triggers an action if the shape below the mouse is the shape being edited or a control point.
        ShapeEditor.prototype.handleMouseDown = function (event) {
            var x = event.clientX,
                y = event.clientY;

            this.actionCurrentX = x;
            this.actionCurrentY = y;

            var mousePoint = this._worldWindow.canvasCoordinates(x, y);
            var tmpOutlineWidth = 0;

            if (this._shape !== null && !this.isCreatorEnabled()) {
                tmpOutlineWidth = this._shape.highlightAttributes.outlineWidth;
                this._shape.highlightAttributes.outlineWidth = 5;
            }

            var pickList = this._worldWindow.pick(mousePoint);

            if (tmpOutlineWidth !== 0) {
                this._shape.highlightAttributes.outlineWidth = tmpOutlineWidth;
            }

            var terrainObject = pickList.terrainObject();

            if (this._click0Time && !this._click1Time) {
                this._clicked1X = x;
                this._clicked1Y = y;
                this._click1Time = Date.now() - this._click0Time;
            } else {
                this._clicked0X = x;
                this._clicked0Y = y;
                this._click0Time = Date.now();
                this._click1Time = 0;
                clearTimeout(this._dbclickTimeout);
                this._dbclickTimeout = setTimeout(function () {
                        this._click0Time = 0;
                    }, this._clickDelay
                );
            }

            for (var p = 0, len = pickList.objects.length; p < len; p++) {
                var object = pickList.objects[p];

                if (!object.isTerrain && !this.isCreatorEnabled()) {
                    var userObject = object.userObject;

                    if (userObject === this._shape) {
                        this.beginAction(terrainObject.position, this._allowManageControlPoint);
                        event.preventDefault();
                        break;

                    } else if (this.controlPointsLayer.renderables.indexOf(userObject) !== -1) {
                        this.beginAction(terrainObject.position, this._allowManageControlPoint, userObject);
                        event.preventDefault();
                        break;
                    } else if (this.shadowControlPointsLayer.renderables.indexOf(userObject) !== -1) {
                        this.beginAction(terrainObject.position, this._allowManageControlPoint, userObject);

                        if (this.actionType == 'shadow' && this._allowManageControlPoint) {
                            this.activeEditorFragment.convertShadowControlPoint(
                                this._shape,
                                this._worldWindow.globe,
                                userObject.userProperties.index,
                                terrainObject.position
                            );
                            this.updateControlElements();
                        }

                        event.preventDefault();
                        break;
                    }
                } else if (this.isCreatorEnabled() && this.activeEditorFragment !== null && this._shape === null) {
                    // set default shape attributes and highlight attributes
                    var attributes = new ShapeAttributes(null);
                    attributes.outlineColor = Color.BLACK;
                    attributes.interiorColor = new Color(0.8, 0.9, 0.9, 1.0);
                    attributes.outlineWidth = 5;

                    var highlightAttributes = new WorldWind.ShapeAttributes(attributes);
                    highlightAttributes.outlineColor = WorldWind.Color.RED;
                    highlightAttributes.outlineWidth = 5;

                    if (this.activeEditorFragment.isRegularShape()) {
                        if (this.activeEditorFragment instanceof PlacemarkEditorFragment) {
                            this.creatorShapeProperties.position = terrainObject.position;
                        } else {
                            this.creatorShapeProperties.center = terrainObject.position;
                        }

                        this.creatorShapeProperties.radius = 3;
                        this.creatorShapeProperties._boundaries = [
                            {
                                latitude: terrainObject.position.latitude - 0.5,
                                longitude: terrainObject.position.longitude - 0.5
                            },
                            {
                                latitude: terrainObject.position.latitude + 0.5,
                                longitude: terrainObject.position.longitude - 0.5
                            },
                            {
                                latitude: terrainObject.position.latitude + 0.5,
                                longitude: terrainObject.position.longitude + 0.5
                            }
                        ];

                        this._shape = this.activeEditorFragment.createShadowShape(this.creatorShapeProperties);

                        if (this.activeEditorFragment instanceof PlacemarkEditorFragment) {
                            var placemarkAttributes = new PlacemarkAttributes(null);
                            placemarkAttributes.imageSource = "data:image/png;base64,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";
                            placemarkAttributes.imageScale = 1;
                            placemarkAttributes.imageColor = Color.WHITE;
                            placemarkAttributes.drawLeaderLine = true;
                            placemarkAttributes.leaderLineAttributes.outlineColor = Color.RED;

                            var highlightPlacemarkAttributes = new PlacemarkAttributes(null);
                            highlightPlacemarkAttributes.imageSource = "data:image/png;base64,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";
                            highlightPlacemarkAttributes.imageScale = 1;
                            highlightPlacemarkAttributes.imageColor = Color.RED;

                            this._shape.attributes =  placemarkAttributes;
                            this._shape.highlightAttributes = highlightPlacemarkAttributes;
                        } else {

                            this._shape.attributes =  attributes;
                            this._shape.highlightAttributes = highlightAttributes;
                        }
                    } else {
                        // Polyline and Polygon are not regular shapes
                        if (this.creatorShapeProperties.boundaries == null) {
                            this.creatorShapeProperties.boundaries = [];
                        }

                        if (this.creatorShapeProperties.boundaries.length < 2) {
                            this.creatorShapeProperties.boundaries.push(new Location(
                                terrainObject.position.latitude,
                                terrainObject.position.longitude
                            ));

                            this.creatorShapeProperties.boundaries.push(new Location(
                                terrainObject.position.latitude,
                                terrainObject.position.longitude
                            ));

                            // return a polyline as shape
                            this._shape = this.editorFragments[4].createShadowShape(this.creatorShapeProperties);
                            this._shape.attributes =  attributes;
                            this._shape.highlightAttributes = highlightAttributes;
                        }
                    }

                    this._shape.highlighted = true;
                    this.initializeControlElements();
                    this.beginAction(terrainObject.position, this._allowManageControlPoint, this.controlPointsLayer.renderables[0]);

                    event.preventDefault();
                }
            }
        };

        // Internal use only.
        // Updates the current action if any.
        ShapeEditor.prototype.handleMouseMove = function (event) {
            var redrawRequired = this._highlightedItems.length > 0; // must redraw if we de-highlight previous shapes

            var mousePoint = this._worldWindow.canvasCoordinates(event.clientX, event.clientY);

            if (this._click0Time && !this._click1Time) {
                this._clicked1X = event.clientX;
                this._clicked1Y = event.clientY;
            }

            if (!(this._clicked0X === this._clicked1X
                    && this._clicked0Y === this._clicked1Y)) {
                clearTimeout(this._dbclickTimeout);
                this._click0Time = 0;
                this._click1Time = 0;
            }

            // De-highlight any previously highlighted shapes.
            for (var h = 0; h < this._highlightedItems.length; h++) {
                this._highlightedItems[h].highlighted = false;
            }
            this._highlightedItems = [];

            // Perform the pick. Must first convert from window coordinates to canvas coordinates, which are
            // relative to the upper left corner of the canvas rather than the upper left corner of the page.
            var pickList = this._worldWindow.pick(this._worldWindow.canvasCoordinates(event.clientX, event.clientY));
            if (pickList.objects.length > 0) {
                redrawRequired = true;
            }

            // Highlight the items picked by simply setting their highlight flag to true.
            if (pickList.objects.length > 0) {
                for (var p = 0; p < pickList.objects.length; p++) {
                    if (!pickList.objects[p].isTerrain && pickList.objects[p].userObject.userProperties.purpose) {
                        pickList.objects[p].userObject.highlighted = true;

                        // Keep track of highlighted items in order to de-highlight them later.
                        this._highlightedItems.push(pickList.objects[p].userObject);
                    }
                }
            }

            // Update the window if we changed anything.
            if (redrawRequired) {
                this._worldWindow.redraw(); // redraw to make the highlighting changes take effect on the screen
            }

            if (this.actionType && this._shape !== null) {

                var terrainObject = this._worldWindow.pickTerrain(mousePoint).terrainObject();

                if (terrainObject) {
                    if (this.actionType === ShapeEditorConstants.DRAG) {
                        if (this._allowMove) {
                            this.drag(event.clientX, event.clientY);
                        } else {
                            Logger.logMessage(Logger.LEVEL_INFO, "ShapeEditor", "handleMouseMove",
                                "Disabled action for selected shape.");
                        }
                    } else {
                        if (this._allowReshape || this._allowRotate) {
                            this.actionSecondaryBehavior = false;
                            this.actionControlPoint.highlighted = true;
                            this.reshape(terrainObject.position);
                        } else {
                            Logger.logMessage(Logger.LEVEL_INFO, "ShapeEditor", "handleMouseMove",
                                "Disabled action for selected shape.");
                        }
                    }

                    event.preventDefault();
                }
            }
        };

        // Internal use only.
        // Terminates the current action if any; otherwise handles other click responses.
        ShapeEditor.prototype.handleMouseUp = function (event) {
            var mousePoint = this._worldWindow.canvasCoordinates(event.clientX, event.clientY);
            var terrainObject = this._worldWindow.pickTerrain(mousePoint).terrainObject();

            if (this.isCreatorEnabled() && this.activeEditorFragment !== null && this._shape !== null) {
                if (this.activeEditorFragment.isRegularShape()) {
                    this.setCreatorEnabled(false);
                    this.promisedShape.resolve(this._shape);
                } else {
                    // add new vertex to shape
                    this.activeEditorFragment.createNewVertex(this._shape, this._worldWindow.globe, terrainObject.position);

                    if (this.activeEditorFragment instanceof SurfacePolylineEditorFragment) {
                        this.updateControlElements();
                        var latestIndex = this.controlPointsLayer.renderables.length - 1;
                        this.actionControlPoint = this.controlPointsLayer.renderables[latestIndex];
                    }

                    if (this.activeEditorFragment instanceof SurfacePolygonEditorFragment &&
                        this._shape instanceof SurfacePolyline &&
                        this._shape.boundaries.length === 3) {

                        var attributes = new ShapeAttributes(null);
                        attributes.outlineColor = Color.BLACK;
                        attributes.interiorColor = new Color(0.8, 0.9, 0.9, 1.0);
                        attributes.outlineWidth = 5;

                        var highlightAttributes = new ShapeAttributes(attributes);
                        highlightAttributes.outlineColor = Color.RED;
                        highlightAttributes.outlineWidth = 5;

                        this._shape = this.activeEditorFragment.createShadowShape(this._shape);
                        this._shape.attributes =  attributes;
                        this._shape.highlightAttributes = highlightAttributes;
                        this._shape.highlighted = true;
                        this.initializeControlElements();
                        this.beginAction(terrainObject.position, this._allowManageControlPoint, this.controlPointsLayer.renderables[0]);
                    }

                    this.updateControlElements();
                    this.updateAnnotation(this.actionControlPoint);

                    this._worldWindow.redraw();
                }
            }

            // The editor provides vertex insertion and removal for SurfacePolygon and SurfacePolyline.
            // Double click when the cursor is over a control point will remove it.
            // Single click when the cursor is over a shadow control point will add it.
            if (this.actionType) {
                if (this._click0Time && this._click1Time) {
                    if (this._click1Time <= this._clickDelay) {
                        if (this.actionControlPoint
                            && this.actionType == 'location'
                            && terrainObject
                            && this._allowManageControlPoint) {
                            if (this.isCreatorEnabled() && this.activeEditorFragment !== null) {
                                // if we are in the creation process and a double click is detected
                                // we verify the minimum conditions for the shape to be created
                                // and resolve the promise (only for polylines and polygons we need
                                // the double click as the final action)
                                if (this.activeEditorFragment instanceof SurfacePolylineEditorFragment) {
                                    if (this._shape.boundaries < 2) {
                                        this._shape = null;
                                    } else {
                                        this._shape.boundaries.splice(-2,2);
                                        this._shape.boundaries.shift();
                                        this.controlPointsLayer.renderables.pop();
                                        this.updateControlElements();
                                        this.updateAnnotation(this.actionControlPoint);

                                        this._worldWindow.redraw();
                                    }
                                } else if (this.activeEditorFragment instanceof SurfacePolygonEditorFragment) {
                                    if (this._shape.boundaries < 3) {
                                        this._shape = null;
                                    }  else {
                                        this._shape.boundaries.pop();
                                        this._shape.boundaries.shift();
                                        this.controlPointsLayer.renderables.pop();
                                        this.updateControlElements();
                                        this.updateAnnotation(this.actionControlPoint);

                                        this._worldWindow.redraw();
                                    }
                                } else {
                                    this._shape = null;
                                }

                                var scope = this;
                                this.promisedShape.resolve(this._shape);
                                this.promisedShape.then(function(shape) {
                                    scope.setCreatorEnabled(false);
                                    scope.endAction();
                                }).finally(function () {
                                    scope.stop();
                                });
                            }
                            this.actionSecondaryBehavior = true;
                            this.reshape(terrainObject.position);
                        }
                    }
                    clearTimeout(this._dbclickTimeout);
                    this._click0Time = 0;
                    this._click1Time = 0;

                }

                if (!this.isCreatorEnabled() && this.activeEditorFragment !== null) {
                    this.endAction();
                }
            }
        };

        // Internal use only.
        ShapeEditor.prototype.beginAction = function (initialPosition, alternateAction, controlPoint) {
            // Define the active transformation
            if (controlPoint) {
                this.actionType = controlPoint.userProperties.purpose;
            } else {
                this.actionType = ShapeEditorConstants.DRAG;
            }

            this.actionControlPoint = controlPoint;
            this.actionControlPosition = initialPosition;
            this.actionSecondaryBehavior = alternateAction;

            var editingAttributes = null;

            if (this.isCreatorEnabled()) {
                this.creatorShapeLayer.addRenderable(this._shape);
            }

            // Place a shadow shape at the original location of the shape
            if (this.activeEditorFragment instanceof PlacemarkEditorFragment) {
                this.originalHighlightAttributes = null;
                this.originalPlacemarkHighlightAttributes = this._shape.highlightAttributes;

                editingAttributes = new PlacemarkAttributes(this.originalPlacemarkHighlightAttributes);
                editingAttributes.imageColor.alpha = editingAttributes.imageColor.alpha * 0.7;
            } else {
                this.originalHighlightAttributes = this._shape.highlightAttributes;
                this.originalPlacemarkHighlightAttributes = null;

                editingAttributes = new ShapeAttributes(this.originalHighlightAttributes);
                editingAttributes.interiorColor.alpha = editingAttributes.interiorColor.alpha * 0.7;
                editingAttributes.outlineColor.alpha = editingAttributes.outlineColor.alpha * 0.7;
            }

            this._shape.highlightAttributes = editingAttributes;

            var shadowShape = this.activeEditorFragment.createShadowShape(this._shape);

            if (this.activeEditorFragment instanceof PlacemarkEditorFragment) {
                shadowShape.altitudeMode = WorldWind.CLAMP_TO_GROUND;
                shadowShape.highlightAttributes = new PlacemarkAttributes(this.originalPlacemarkHighlightAttributes);
            } else {
                shadowShape.highlightAttributes = new ShapeAttributes(this.originalHighlightAttributes);
            }
            shadowShape.highlighted = true;

            this.shadowShapeLayer.addRenderable(shadowShape);

            this._worldWindow.redraw();
        };

        // Internal use only.
        ShapeEditor.prototype.endAction = function () {
            this.creatorShapeLayer.removeAllRenderables();
            this.shadowShapeLayer.removeAllRenderables();

            if (this.activeEditorFragment instanceof PlacemarkEditorFragment) {
                this._shape.highlightAttributes = this.originalPlacemarkHighlightAttributes;
            } else {
                this._shape.highlightAttributes = this.originalHighlightAttributes;
            }

            this.hideAnnotation();

            if (this.actionControlPoint) {
                this.actionControlPoint.highlighted = false;
            }

            this.actionControlPoint = null;
            this.actionType = null;
            this.actionControlPosition = null;

            this._worldWindow.redraw();
        };

        // Internal use only.
        ShapeEditor.prototype.reshape = function (newPosition) {
            var purpose = this.actionControlPoint.userProperties.purpose;

            if ((purpose === ShapeEditorConstants.ROTATION && this._allowRotate) ||
                (purpose !== ShapeEditorConstants.ROTATION && this._allowReshape) ||
                (purpose === ShapeEditorConstants.LOCATION && this._allowManageControlPoint && this.actionSecondaryBehavior) ||
                (purpose === ShapeEditorConstants.SHADOW && this._allowManageControlPoint && this.actionSecondaryBehavior)) {

                this.activeEditorFragment.reshape(
                    this._shape,
                    this._worldWindow.globe,
                    this.actionControlPoint,
                    newPosition,
                    this.actionControlPosition,
                    this.actionSecondaryBehavior
                );

                this.actionControlPosition = newPosition;

                this.updateControlElements();
                this.updateAnnotation(this.actionControlPoint);

                this._worldWindow.redraw();
            }
        };

        // Internal use only.
        ShapeEditor.prototype.drag = function (clientX, clientY) {
            // Get reference position for the shape that is dragged
            var refPos = this._shape.getReferencePosition();

            // Get point for referenced position
            var refPoint = this._worldWindow.globe.computePointFromPosition(
                refPos.latitude,
                refPos.longitude,
                0,
                new Vec3(0, 0, 0)
            );

            var screenRefPoint = new Vec3(0, 0, 0);
            this._worldWindow.drawContext.project(refPoint, screenRefPoint);

            // Check drag distance
            var dx = clientX - this.actionCurrentX;
            var dy = clientY - this.actionCurrentY;

            // Get the latest position of mouse to calculate drag distance
            this.actionCurrentX = clientX;
            this.actionCurrentY = clientY;

            // Find intersection of the screen coordinates ref-point with globe
            var x = screenRefPoint[0] + dx;
            var y = this._worldWindow.canvas.height - screenRefPoint[1] + dy;

            var ray = this._worldWindow.rayThroughScreenPoint(new Vec2(x, y));

            // Check if the mouse is over the globe and move shape
            var intersection = new Vec3(0, 0, 0);
            if (this._worldWindow.globe.intersectsLine(ray, intersection)) {
                var p = new Position(0, 0, 0);
                this._worldWindow.globe.computePositionFromPoint(intersection[0], intersection[1], intersection[2], p);
                this._shape.moveTo(this._worldWindow.globe, new Location(p.latitude, p.longitude));
            }

            // Update control points and shape annotation
            this.updateControlElements();
            this.updateShapeAnnotation();

            this._worldWindow.redraw();
        };

        // Internal use only.
        ShapeEditor.prototype.updateAnnotation = function (controlPoint) {
            this.annotationLayer.enabled = true;

            this.annotation.position = new Position(
                controlPoint.position.latitude,
                controlPoint.position.longitude,
                0
            );

            var annotationText;
            if (controlPoint.userProperties.size !== undefined) {
                annotationText = this.formatLength(controlPoint.userProperties.size);
            } else if (controlPoint.userProperties.rotation !== undefined) {
                annotationText = this.formatRotation(controlPoint.userProperties.rotation);
            } else {
                annotationText = this.formatLatitude(controlPoint.position.latitude)
                    + " "
                    + this.formatLongitude(controlPoint.position.longitude);
            }
            this.annotation.text = annotationText;
        };

        // Internal use only.
        ShapeEditor.prototype.hideAnnotation = function (controlPoint) {
            this.annotationLayer.enabled = false;
        };

        // Internal use only.
        ShapeEditor.prototype.updateShapeAnnotation = function () {
            var center = this.activeEditorFragment.getShapeCenter(this._shape, this._worldWindow.globe);

            var temporaryMarker = new Placemark(
                new Position(center.latitude, center.longitude, 0),
                null
            );

            this.updateAnnotation(temporaryMarker);
        };

        // Internal use only.
        ShapeEditor.prototype.formatLatitude = function (number) {
            var suffix = number < 0 ? "\u00b0S" : "\u00b0N";
            return Math.abs(number).toFixed(4) + suffix;
        };

        // Internal use only.
        ShapeEditor.prototype.formatLongitude = function (number) {
            var suffix = number < 0 ? "\u00b0W" : "\u00b0E";
            return Math.abs(number).toFixed(4) + suffix;
        };

        // Internal use only.
        ShapeEditor.prototype.formatLength = function (number) {
            var suffix = " km";
            return Math.abs(number / 1000.0).toFixed(3) + suffix;
        };

        // Internal use only.
        ShapeEditor.prototype.formatRotation = function (rotation) {
            return rotation.toFixed(4) + "°";
        };

        return ShapeEditor;
    }
);