var DrawGeometryHelper = (function () {

    let ellipsoid = Cesium.Ellipsoid.WGS84;

    let material = Cesium.Material.fromType(Cesium.Material.ColorType);

    material.uniforms.color = Cesium.Color.RED;


    var defaultBillboard = {
        iconUrl: "./Build/Cesium/ThirdParty/Draw/img/dragIcon.png",
        shiftX: 0,
        shiftY: 0
    };

    var dragBillboard = {
        iconUrl: "./Build/Cesium/ThirdParty/Draw/img/dragIcon.png",
        shiftX: 0,
        shiftY: 0
    };

    var dragHalfBillboard = {
        iconUrl: "./Build/Cesium/ThirdParty/Draw/img/dragIconLight.png",
        shiftX: 0,
        shiftY: 0
    };

    var defaultShapeOptions = {
        ellipsoid: Cesium.Ellipsoid.WGS84,
        textureRotationAngle: 0.0,
        height: 0.0,
        asynchronous: true,
        show: true,
        debugShowBoundingVolume: false
    };
    var defaultSurfaceOptions = copyOptions(defaultShapeOptions, {
        appearance: new Cesium.PerInstanceColorAppearance({
            translucent: false,
            closed: true,
            aboveGround: true
        }),
        granularity: Math.PI / 180.0
    });
    var defaultPolygonOptions = copyOptions(defaultShapeOptions, {
        appearance: new Cesium.PerInstanceColorAppearance({
            translucent: false,
            closed: true,
            aboveGround: true
        }),
        geodesic: true,
        granularity: 10000,
    });
    var defaultExtentOptions = copyOptions(defaultShapeOptions, {
        appearance: new Cesium.PerInstanceColorAppearance({
        }),
        material: material
    });
    var defaultCircleOptions = copyOptions(defaultShapeOptions, {});
    var defaultEllipseOptions = copyOptions(defaultSurfaceOptions, {rotation: 0});
    var defaultPolylineOptions = copyOptions(defaultShapeOptions, {
        width: 5,
        geodesic: true,
        granularity: 10000,
        appearance: new Cesium.PolylineMaterialAppearance({
            aboveGround: false
        }),
        material: material
    });
    var defaultGlobeOptions = copyOptions(defaultShapeOptions, {
        width: 5,
        geodesic: true,
        granularity: 10000,
        appearance: new Cesium.PolylineMaterialAppearance({
            aboveGround: false
        }),
        material: material
    });
    var defaultCylinderOptions = copyOptions(defaultShapeOptions, {
        geodesic: true,
        appearance: new Cesium.PerInstanceColorAppearance({
            // //不透明
            translucent: false,
            closed: true
        }),
        material: material
    });
    var defaultCuboidOptions = copyOptions(defaultSurfaceOptions, {
        geodesic: true,
        appearance: new Cesium.PerInstanceColorAppearance({
            // //不透明
            translucent: false,
            closed: true
        }),
        material: material
    });

    function screen2Cartesian(x, y) {
        //return viewer.scene.globe.pick(viewer.camera.getPickRay(new Cesium.Cartesian2(x, y)), viewer.scene);
        var cartesian = null;
        if (viewer.scene.mode === 3) {
            cartesian = viewer.scene.pickPosition({"x": x, "y": y});
            // if(!cartesian){
            //     var pick = new Cesium.Cartesian2(x, y);
            //     var ray = viewer.camera.getPickRay(pick);
            //     cartesian = viewer.scene.globe.pick(ray, viewer.scene);
            // }
        } else if (viewer.scene.mode === 2) {
            var pick = new Cesium.Cartesian2(x, y);
            cartesian = viewer.camera.pickEllipsoid(pick, scene.globe.ellipsoid);
        }
        return cartesian;
    };

    function getLonLatByCartesian(cartesian) {
        var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
        var lon = Cesium.Math.toDegrees(cartographic.longitude);
        var lat = Cesium.Math.toDegrees(cartographic.latitude);
        var height = cartographic.height;
        return [lon, lat, height];
    };

    function Screen2LonLat(x, y) {
        return viewer.scene.globe.pick(viewer.camera.getPickRay(new Cesium.Cartesian2(x, y)), viewer.scene);
    };

    function _(cesiumWidget) {
        this._scene = cesiumWidget.scene;
        this._surfaces = [];
        this.initialiseHandlers();
        this.enhancePrimitives();
    }

    function setListener(primitive, type, callback) {
        primitive[type] = callback;
    }

    function copyOptions(options, defaultOptions) {
        let newOptions = clone(options), option;
        for (option in defaultOptions) {
            if (newOptions[option] === undefined) {
                newOptions[option] = clone(defaultOptions[option]);
            }
        }
        return newOptions;
    }

    function clone(from, to) {
        if (from == null || typeof from !== "object") return from;
        if (from.constructor !== Object && from.constructor !== Array) return from;
        if (from.constructor === Date || from.constructor === RegExp || from.constructor === Function ||
            from.constructor === String || from.constructor === Number || from.constructor === Boolean)
            return new from.constructor(from);

        to = to || new from.constructor();

        for (let name in from) {
            to[name] = typeof to[name] === "undefined" ? clone(from[name], null) : to[name];
        }

        return to;
    }

    function fillOptions(options, defaultOptions) {
        options = options || {};
        var option;
        for (option in defaultOptions) {
            if (options[option] === undefined) {
                options[option] = clone(defaultOptions[option]);
            }
        }
    }

    function getExtent(startPoint, endPoint) {
        //var e = new Cesium.Rectangle();
        var p1 = drawGeometryHelper.getLonLatByCartesian(startPoint);
        var p2 = drawGeometryHelper.getLonLatByCartesian(endPoint);

        var e = Cesium.Rectangle.fromDegrees(Math.min(p1[0], p2[0]),
            Math.min(p1[1], p2[1]), Math.max(p1[0], p2[0]), Math.max(p1[1], p2[1]));

        return e;
    };

    function getExtentCorners(value) {
        return ellipsoid.cartographicArrayToCartesianArray([Cesium.Rectangle.northwest(value), Cesium.Rectangle.northeast(value), Cesium.Rectangle.southeast(value), Cesium.Rectangle.southwest(value)]);
    }

    _.prototype.initialiseHandlers = function () {
        let scene = this._scene;
        let _self = this;
        // scene events
        let handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);

        function callPrimitiveCallback(name, position) {
            if (_self._handlersMuted === true) return;
            let pickedObject = scene.pick(position);
            if (pickedObject && pickedObject.primitive && pickedObject.primitive[name]) {
                pickedObject.primitive[name](position);
            }
        }

        handler.setInputAction(
            function (movement) {
                callPrimitiveCallback('leftClick', movement.position);
            }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
        handler.setInputAction(
            function (movement) {
                callPrimitiveCallback('leftDoubleClick', movement.position);
            }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
        let mouseOutObject;
        handler.setInputAction(
            function (movement) {
                if (_self._handlersMuted === true) return;
                let pickedObject = scene.pick(movement.endPosition);
                if (mouseOutObject && (!pickedObject || mouseOutObject !== pickedObject.primitive)) {
                    !(mouseOutObject.isDestroyed && mouseOutObject.isDestroyed()) && mouseOutObject.mouseOut(movement.endPosition);
                    mouseOutObject = null;
                }
                if (pickedObject && pickedObject.primitive) {
                    pickedObject = pickedObject.primitive;
                    if (pickedObject.mouseOut) {
                        mouseOutObject = pickedObject;
                    }
                    if (pickedObject.mouseMove) {
                        pickedObject.mouseMove(movement.endPosition);
                    }
                }
            }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        handler.setInputAction(
            function (movement) {
                callPrimitiveCallback('leftUp', movement.position);
            }, Cesium.ScreenSpaceEventType.LEFT_UP);
        handler.setInputAction(
            function (movement) {
                callPrimitiveCallback('leftDown', movement.position);
            }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
    };

    _.prototype.setListener = function (primitive, type, callback) {
        primitive[type] = callback;
    };

    _.prototype.muteHandlers = function (muted) {
        this._handlersMuted = muted;
    };

    _.prototype.startDrawing = function (cleanUp) {
        if (this.editCleanUp) {
            this.editCleanUp();
        }
        this.editCleanUp = cleanUp;
        this.muteHandlers(true);
    };

    _.prototype.stopDrawing = function () {
        // check for cleanUp first
        if (this.editCleanUp) {
            this.editCleanUp();
            this.editCleanUp = null;
        }
        this.muteHandlers(false);
    };

    _.prototype.getLonLatByCartesian = function (cartesian) {
        var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
        var lon = Cesium.Math.toDegrees(cartographic.longitude);
        var lat = Cesium.Math.toDegrees(cartographic.latitude);
        return [lon, lat];
    };

    _.prototype.Screen2LonLat = function (x, y) {
        return viewer.scene.globe.pick(viewer.camera.getPickRay(new Cesium.Cartesian2(x, y)), viewer.scene);
    };

    _.prototype.screen2Cartesian = function (x, y) {
        //return viewer.scene.globe.pick(viewer.camera.getPickRay(new Cesium.Cartesian2(x, y)), viewer.scene);
        var cartesian = null;
        if (viewer.scene.mode == 3) {
            var pick = new Cesium.Cartesian2(x, y);
            var ray = viewer.camera.getPickRay(pick);
            cartesian = viewer.scene.globe.pick(ray, viewer.scene);
        } else if (viewer.scene.mode == 2) {
            var pick = new Cesium.Cartesian2(x, y);
            cartesian = viewer.camera.pickEllipsoid(pick, scene.globe.ellipsoid);
        }
        return cartesian;
    };

    _.prototype.cartesian2Cartographic = function (cartesian) {
        return Cesium.Cartographic.fromCartesian(cartesian);
    };

    _.prototype.cartographic2Lonlat = function (cartographic) {
        var lon = Cesium.Math.toDegrees(cartographic.longitude);
        var lat = Cesium.Math.toDegrees(cartographic.latitude);
        var heigh = Cesium.Math.toDegrees(cartographic.height);
        return [lon, lat];
    };

    var ChangeablePrimitive = (function () {
        function _() {
        }

        _.prototype.initialiseOptions = function (options) {
            fillOptions(this, options);
            this._ellipsoid = undefined;
            this._granularity = undefined;
            this._height = undefined;
            this._textureRotationAngle = undefined;
            this._id = undefined;

            // set the flags to initiate a first drawing
            this._createPrimitive = true;
            this._primitive = undefined;
            this._outlinePolygon = undefined;

        };

        _.prototype.setAttribute = function (name, value) {
            this[name] = value;
            this._createPrimitive = true;
        };

        _.prototype.getAttribute = function (name) {
            return this[name];
        };

        /**
         * @private
         */
        _.prototype.update = function (context, frameState, commandList) {

            if (!Cesium.defined(this.ellipsoid)) {
                throw new Cesium.DeveloperError('this.ellipsoid must be defined.');
            }

            if (!Cesium.defined(this.appearance)) {
                throw new Cesium.DeveloperError('this.material must be defined.');
            }

            if (this.granularity < 0.0) {
                throw new Cesium.DeveloperError('this.granularity and scene2D/scene3D overrides must be greater than zero.');
            }

            if (!this.show) {
                return;
            }

            if (!this._createPrimitive && (!Cesium.defined(this._primitive))) {
                // No positions/hierarchy to draw
                return;
            }

            if (this._createPrimitive ||
                (this._ellipsoid !== this.ellipsoid) ||
                (this._granularity !== this.granularity) ||
                (this._height !== this.height) ||
                (this._textureRotationAngle !== this.textureRotationAngle) ||
                (this._id !== this.id)) {

                var geometry = this.getGeometry();
                if (!geometry) {
                    return;
                }

                this._createPrimitive = false;
                this._ellipsoid = this.ellipsoid;
                this._granularity = this.granularity;
                this._height = this.height;
                this._textureRotationAngle = this.textureRotationAngle;
                this._id = this.id;

                this._primitive = this._primitive && this._primitive.destroy();

                if (this.isPolygon) {
                    this._primitive = new Cesium.GroundPrimitive({
                        geometryInstances: new Cesium.GeometryInstance({
                            geometry: geometry,
                            attributes: {
                                color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromAlpha(Cesium.Color.RED, 0.4)),//color  必须设置 不然没有效果
                            },
                        }),
                        appearance: this.appearance,
                        asynchronous: this.asynchronous
                    });
                } else if (this.isGlobe) {
                    if (this.drawState === "DrawRadius") {
                    }
                    this._primitive = new Cesium.Primitive({
                        geometryInstances: new Cesium.GeometryInstance({
                            geometry: geometry,
                            // id: this.id,
                            // pickPrimitive: this
                            modelMatrix: Cesium.Matrix4.multiplyByTranslation(
                                Cesium.Transforms.eastNorthUpToFixedFrame(this.center),
                                new Cesium.Cartesian3(0.0, 0.0, 0.0),
                                new Cesium.Matrix4()
                            ),
                        }),
                        appearance: this.appearance,
                        asynchronous: this.asynchronous
                    });

                } else if (this.isCylinder) {
                    this._primitive = new Cesium.Primitive({
                        geometryInstances: new Cesium.GeometryInstance({
                            geometry: geometry,
                            // id: this.id,
                            // pickPrimitive: this
                            modelMatrix: Cesium.Matrix4.multiplyByTranslation(
                                Cesium.Transforms.eastNorthUpToFixedFrame(this.getCenter()),
                                new Cesium.Cartesian3(0.0, 0.0, 0.0),
                                new Cesium.Matrix4()
                            ),
                        }),
                        appearance: this.appearance,
                        asynchronous: this.asynchronous
                    });

                } else if (this.isRectangle) {
                    this._primitive = new Cesium.Primitive({
                        geometryInstances: new Cesium.GeometryInstance({
                            geometry: geometry,
                        }),
                        appearance: this.appearance,
                        asynchronous: this.asynchronous
                    });

                } else if (this.isCuboid) {
                    this._primitive = new Cesium.Primitive({
                        geometryInstances: new Cesium.GeometryInstance({
                            geometry: geometry,
                            modelMatrix: Cesium.Matrix4.multiplyByTranslation(
                                Cesium.Transforms.eastNorthUpToFixedFrame(this.getCenter()),
                                new Cesium.Cartesian3(0.0, 0.0, 0.0),
                                new Cesium.Matrix4()
                            ),
                        }),
                        appearance: this.appearance,
                        asynchronous: this.asynchronous
                    });
                } else {
                    this._primitive = new Cesium.Primitive({
                        geometryInstances: new Cesium.GeometryInstance({
                            geometry: geometry,
                            // id: this.id,
                            // pickPrimitive: this
                        }),
                        appearance: this.appearance,
                        asynchronous: this.asynchronous
                    });
                }

                this._outlinePolygon = this._outlinePolygon && this._outlinePolygon.destroy();
                if (this.strokeColor && this.getOutlineGeometry) {
                    // create the highlighting frame
                    this._outlinePolygon = new Cesium.Primitive({
                        geometryInstances: new Cesium.GeometryInstance({
                            geometry: this.getOutlineGeometry(),
                            attributes: {
                                color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
                            }
                        }),
                        appearance: new Cesium.PerInstanceColorAppearance({
                            // flat: true,
                            // renderState: {
                            //     depthTest: {
                            //         enabled: true
                            //     },
                            //     lineWidth: Math.min(this.strokeWidth || 4.0)
                            // }
                        })
                    });
                }
            }

            var primitive = this._primitive;
            //primitive.appearance.material = this.material;
            primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;
            primitive.update(context, frameState, commandList);
            this._outlinePolygon && this._outlinePolygon.update(context, frameState, commandList);

        };

        _.prototype.isDestroyed = function () {
            return false;
        };

        _.prototype.destroy = function () {
            this._primitive = this._primitive && this._primitive.destroy();
            return Cesium.destroyObject(this);
        };

        _.prototype.setStrokeStyle = function (strokeColor, strokeWidth) {
            if (!this.strokeColor || !this.strokeColor.equals(strokeColor) || this.strokeWidth !== strokeWidth) {
                this._createPrimitive = true;
                this.strokeColor = strokeColor;
                this.strokeWidth = strokeWidth;
            }
        };

        return _;
    })();

    _.prototype.createBillboardGroup = function (points, options, callbacks) {
        var markers = new _.BillboardGroup(this, options);
        markers.addBillboards(points, callbacks);
        return markers;
    };

    _.BillboardGroup = function (DrawGeometryHelper, options) {

        this._DrawGeometryHelper = DrawGeometryHelper;
        this._scene = DrawGeometryHelper._scene;

        this._options = copyOptions(options, defaultBillboard);

        // create one common billboard collection for all billboards
        var b = new Cesium.BillboardCollection();
        this._scene.primitives.add(b);
        this._billboards = b;
        // keep an ordered list of billboards
        this._orderedBillboards = [];
    };

    _.BillboardGroup.prototype.createBillboard = function (position, callbacks) {

        var billboard = this._billboards.add({
            show: true,
            position: position,
            pixelOffset: new Cesium.Cartesian2(this._options.shiftX, this._options.shiftY),
            eyeOffset: new Cesium.Cartesian3(0.0, 0.0, 0.0),
            horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
            verticalOrigin: Cesium.VerticalOrigin.CENTER,
            scale: 1.0,
            image: this._options.iconUrl,
            color: new Cesium.Color(1.0, 1.0, 1.0, 1.0),
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
        });

        // if editable
        if (callbacks) {
            var _self = this;
            var screenSpaceCameraController = this._scene.screenSpaceCameraController;

            function enableRotation(enable) {
                screenSpaceCameraController.enableRotate = enable;
            }

            function getIndex() {
                // find index
                for (var i = 0, I = _self._orderedBillboards.length; i < I && _self._orderedBillboards[i] != billboard; ++i) ;
                return i;
            }

            if (callbacks.dragHandlers) {
                var _self = this;
                setListener(billboard, 'leftDown', function (position) {
                    // TODO - start the drag handlers here
                    // create handlers for mouseOut and leftUp for the billboard and a mouseMove
                    function onDrag(position) {
                        billboard.position = position;
                        // find index
                        for (var i = 0, I = _self._orderedBillboards.length; i < I && _self._orderedBillboards[i] != billboard; ++i) ;
                        callbacks.dragHandlers.onDrag && callbacks.dragHandlers.onDrag(getIndex(), position);
                    }

                    function onDragEnd(position) {
                        handler.destroy();
                        enableRotation(true);
                        callbacks.dragHandlers.onDragEnd && callbacks.dragHandlers.onDragEnd(getIndex(), position);
                    }

                    var handler = new Cesium.ScreenSpaceEventHandler(_self._scene.canvas);

                    handler.setInputAction(function (movement) {
                        var cartesian = _self._scene.camera.pickEllipsoid(movement.endPosition, ellipsoid);
                        if (cartesian) {
                            onDrag(cartesian);
                        } else {
                            onDragEnd(cartesian);
                        }
                    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

                    handler.setInputAction(function (movement) {
                        onDragEnd(_self._scene.camera.pickEllipsoid(movement.position, ellipsoid));
                    }, Cesium.ScreenSpaceEventType.LEFT_UP);

                    enableRotation(false);

                    callbacks.dragHandlers.onDragStart && callbacks.dragHandlers.onDragStart(getIndex(), _self._scene.camera.pickEllipsoid(position, ellipsoid));
                });
            }
            if (callbacks.onDoubleClick) {
                setListener(billboard, 'leftDoubleClick', function (position) {
                    callbacks.onDoubleClick(getIndex());
                });
            }
            if (callbacks.onClick) {
                setListener(billboard, 'leftClick', function (position) {
                    callbacks.onClick(getIndex());
                });
            }
            // if (callbacks.tooltip) {
            //     setListener(billboard, 'mouseMove', function (position) {
            //        // _self._DrawGeometryHelper._tooltip.showAt(position, callbacks.tooltip());
            //     });
            //     setListener(billboard, 'mouseOut', function (position) {
            //         //_self._DrawGeometryHelper._tooltip.setVisible(false);
            //     });
            // }
        }
        return billboard;
    };

    _.BillboardGroup.prototype.insertBillboard = function (index, position, callbacks) {
        this._orderedBillboards.splice(index, 0, this.createBillboard(position, callbacks));
    };

    _.BillboardGroup.prototype.addBillboard = function (position, callbacks) {
        let billboard = this.createBillboard(position, callbacks);
        this._orderedBillboards.push(billboard);
        this._billboards.add(billboard)
    };

    _.BillboardGroup.prototype.addBillboards = function (positions, callbacks) {
        var index = 0;
        for (; index < positions.length; index++) {
            this.addBillboard(positions[index], callbacks);
        }
    };

    _.BillboardGroup.prototype.updateBillboardsPositions = function (positions) {
        var index = 0;
        for (; index < positions.length; index++) {
            this.getBillboard(index).position = positions[index];
        }
    };

    _.BillboardGroup.prototype.countBillboards = function () {
        return this._orderedBillboards.length;
    };

    _.BillboardGroup.prototype.getBillboard = function (index) {
        return this._orderedBillboards[index];
    };

    _.BillboardGroup.prototype.removeBillboard = function (index) {
        this._billboards.remove(this.getBillboard(index));
        this._orderedBillboards.splice(index, 1);
    };

    _.BillboardGroup.prototype.remove = function () {
        this._billboards = this._billboards && this._billboards.removeAll() && this._billboards.destroy();
    };

    _.BillboardGroup.prototype.setOnTop = function () {
        this._scene.primitives.raiseToTop(this._billboards);
    };

    _.PolylinePrimitive = (function () {

        function _(options) {

            options = copyOptions(options, defaultPolylineOptions);

            this.initialiseOptions(options);

        }

        _.prototype = new ChangeablePrimitive();
        _.prototype.isPolygon = false;
        _.prototype.setPositions = function (positions) {
            this.setAttribute('positions', positions);
        };

        _.prototype.setWidth = function (width) {
            this.setAttribute('width', width);
        };

        _.prototype.setGeodesic = function (geodesic) {
            this.setAttribute('geodesic', geodesic);
        };

        _.prototype.getPositions = function () {
            return this.getAttribute('positions');
        };

        _.prototype.getWidth = function () {
            return this.getAttribute('width');
        };

        _.prototype.getGeodesic = function (geodesic) {
            return this.getAttribute('geodesic');
        };

        _.prototype.getGeometry = function () {

            if (!Cesium.defined(this.positions) || this.positions.length < 2) {
                return;
            }

            return new Cesium.PolylineGeometry({
                positions: this.positions,
                height: this.height,
                width: this.width < 1 ? 1 : this.width,
                vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                ellipsoid: this.ellipsoid
            });
        }

        return _;
    })();

    _.PolygonPrimitive = (function () {

        function _(options) {

            options = copyOptions(options, defaultSurfaceOptions);

            this.initialiseOptions(options);

            this.isPolygon = true;

        }

        _.prototype = new ChangeablePrimitive();

        _.prototype.setPositions = function (positions) {
            this.setAttribute('positions', positions);
        };

        _.prototype.getPositions = function () {
            return this.getAttribute('positions');
        };

        _.prototype.getGeometry = function () {

            if (!Cesium.defined(this.positions) || this.positions.length < 3) {
                return;
            }

            return Cesium.PolygonGeometry.fromPositions({
                positions: this.positions,
                // height: this.height,
                vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
                // stRotation: this.textureRotationAngle,
                // ellipsoid: this.ellipsoid,
                // granularity: this.granularity,
            });
        };

        _.prototype.getOutlineGeometry = function () {
            return Cesium.PolygonOutlineGeometry.fromPositions({
                positions: this.getPositions()
            });
        }

        return _;
    })();

    _.CirclePrimitive = (function () {

        function _(options) {

            if (!(Cesium.defined(options.center) && Cesium.defined(options.radius))) {
                throw new Cesium.DeveloperError('Center and radius are required');
            }

            options = copyOptions(options, defaultSurfaceOptions);

            this.initialiseOptions(options);

            this.setRadius(options.radius);

        }

        _.prototype = new ChangeablePrimitive();

        _.prototype.setCenter = function (center) {
            this.setAttribute('center', center);
        };

        _.prototype.setRadius = function (radius) {
            this.setAttribute('radius', Math.max(0.1, radius));
        };

        _.prototype.getCenter = function () {
            return this.getAttribute('center');
        };

        _.prototype.getRadius = function () {
            return this.getAttribute('radius');
        };

        _.prototype.getGeometry = function () {

            if (!(Cesium.defined(this.center) && Cesium.defined(this.radius))) {
                return;
            }

            return new Cesium.CircleGeometry({
                center: this.center,
                radius: this.radius,
                height: this.height,
                vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                stRotation: this.textureRotationAngle,
                ellipsoid: this.ellipsoid,
                granularity: this.granularity
            });
        };

        _.prototype.getOutlineGeometry = function () {
            return new Cesium.CircleOutlineGeometry({
                center: this.getCenter(),
                radius: this.getRadius()
            });
        }

        return _;
    })();

    _.ExtentPrimitive = (function () {
        function _(options) {

            if (!Cesium.defined(options.extent)) {
                throw new Cesium.DeveloperError('Extent is required');
            }

            options = copyOptions(options, defaultSurfaceOptions);

            this.initialiseOptions(options);

            this.setExtent(options.extent);

        }

        _.prototype = new ChangeablePrimitive();

        _.prototype.setExtent = function (extent) {
            this.setAttribute('extent', extent);
        };

        _.prototype.getExtent = function () {
            return this.getAttribute('extent');
        };

        _.prototype.getGeometry = function () {

            if (!Cesium.defined(this.extent)) {
                return;
            }

            return new Cesium.RectangleGeometry({
                rectangle: this.extent,
                // height: this.height,
                // vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
                // stRotation: this.textureRotationAngle,
                // ellipsoid: this.ellipsoid,
                // granularity: this.granularity
            });
        };

        _.prototype.getOutlineGeometry = function () {
            return new Cesium.RectangleOutlineGeometry({
                rectangle: this.extent
            });
        }

        return _;
    })();

    _.CuboidPrimitive = (function () {
        function _(options) {

            if (!Cesium.defined(options.firstPoint) && !Cesium.defined(options.scendPoint) && !Cesium.defined(options.height)) {
                throw new Cesium.DeveloperError('center is required');
            }

            options = copyOptions(options, defaultSurfaceOptions);

            this.initialiseOptions(options);

            this.setFirstPoint(options.firstPoint);
            this.setScendPoint(options.scendPoint);

        }

        _.prototype = new ChangeablePrimitive();

        _.prototype.setDimensions = function () {
            var p1 = new Cesium.Cartesian3.fromDegrees(this.firstPoint[0], this.scendPoint[1], this.height);
            var p2 = new Cesium.Cartesian3.fromDegrees(this.scendPoint[0], this.scendPoint[1], this.height);
            var l = Cesium.Cartesian3.distance(p1, p2);
            p1 = new Cesium.Cartesian3.fromDegrees(this.firstPoint[0], this.firstPoint[1], this.height);
            p2 = new Cesium.Cartesian3.fromDegrees(this.firstPoint[0], this.scendPoint[1], this.height);
            var w = Cesium.Cartesian3.distance(p1, p2);


            this.setAttribute('dimensions', new Cesium.Cartesian3(l, w, this.height));
        };

        // _.prototype.setCenter = function (center) {
        //     this.setAttribute('center', center);
        // };

        _.prototype.setFirstPoint = function (firstPoint) {
            this.setAttribute('firstPoint', firstPoint);
        };

        _.prototype.setScendPoint = function (scendPoint) {
            this.setAttribute('scendPoint', scendPoint);
        };

        _.prototype.setHeight = function (height) {
            this.setAttribute('height', height);
        };

        _.prototype.setCenter = function () {
            var x = (this.firstPoint[0] + this.scendPoint[0]) / 2;
            var y = (this.firstPoint[1] + this.scendPoint[1]) / 2;
            var center = Cesium.Cartesian3.fromDegrees(x, y, this.height / 2);
            this.setAttribute('center', center);
        };

        _.prototype.getDimensions = function () {
            return this.getAttribute('dimensions');
        };

        _.prototype.getFirstPoint = function () {
            return this.getAttribute('firstPoint');
        };

        _.prototype.getScendPoint = function () {
            return this.getAttribute('scendPoint');
        };

        _.prototype.getHeight = function () {
            return this.getAttribute('height');
        };
        _.prototype.getCuboidCorners = function () {
            var p1 = this.firstPoint;
            var p2 = this.scendPoint;
            var h = this.height;
            var b1 = new Cesium.Cartesian3.fromDegrees(Math.min(p1[0], p2[0]), Math.min(p1[1], p2[1]), 0);
            var b2 = new Cesium.Cartesian3.fromDegrees(Math.min(p1[0], p2[0]), Math.max(p1[1], p2[1]), 0);
            var b3 = new Cesium.Cartesian3.fromDegrees(Math.max(p1[0], p2[0]), Math.min(p1[1], p2[1]), 0);
            var b4 = new Cesium.Cartesian3.fromDegrees(Math.max(p1[0], p2[0]), Math.max(p1[1], p2[1]), 0);
            var b5 = new Cesium.Cartesian3.fromDegrees(Math.min(p1[0], p2[0]), Math.min(p1[1], p2[1]), h);
            var b6 = new Cesium.Cartesian3.fromDegrees(Math.min(p1[0], p2[0]), Math.max(p1[1], p2[1]), h);
            var b7 = new Cesium.Cartesian3.fromDegrees(Math.max(p1[0], p2[0]), Math.min(p1[1], p2[1]), h);
            var b8 = new Cesium.Cartesian3.fromDegrees(Math.max(p1[0], p2[0]), Math.max(p1[1], p2[1]), h);
            return [b1, b2, b3, b4, b5, b6, b7, b8];
        };

        _.prototype.getCenter = function () {
            return this.getAttribute('center');
        };

        _.prototype.getGeometry = function () {

            if (!Cesium.defined(this.center)) {
                return;
            }

            return new Cesium.BoxGeometry.fromDimensions({
                vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL,
                dimensions: this.dimensions
            })
        };

        _.prototype.getOutlineGeometry = function () {
            return new Cesium.BoxOutlineGeometry.fromDimensions({
                vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL,
                dimensions: this.dimensions
            })
        };

        return _;
    })();

    _.GlobePrimitive = (function () {

        function _(options) {

            if (!(Cesium.defined(options.center) && Cesium.defined(options.radius))) {
                throw new Cesium.DeveloperError('Center and radius are required');
            }

            options = copyOptions(options, defaultSurfaceOptions);

            this.initialiseOptions(options);

            this.setRadius(options.radius);

        }

        _.prototype = new ChangeablePrimitive();

        _.prototype.setCenter = function (center) {
            this.setAttribute('center', center);
        };

        _.prototype.setRadius = function (radius) {
            this.setAttribute('radius', Math.max(0.1, radius));
        };

        _.prototype.getCenter = function () {
            return this.getAttribute('center');
        };

        _.prototype.getRadius = function () {
            return this.getAttribute('radius');
        };

        _.prototype.getGeometry = function () {

            if (!(Cesium.defined(this.center) && Cesium.defined(this.radius))) {
                return;
            }

            return new Cesium.SphereGeometry({
                radius: this.radius,
                vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL
            });
        };

        _.prototype.getOutlineGeometry = function () {
            return new Cesium.SphereGeometry({
                radius: this.radius,
                vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL
            });
        };

        return _;
    })();

    _.CylinderPrimitive = (function () {

        function _(options) {

            if (!(Cesium.defined(options.center))) {
                throw new Cesium.DeveloperError('Center and radius are required');
            }

            options = copyOptions(options, defaultSurfaceOptions);

            this.initialiseOptions(options);

            this.setRadius(options.radius);

        }

        _.prototype = new ChangeablePrimitive();

        _.prototype.setLength = function (length) {
            this.setAttribute('length', length);
        };

        _.prototype.setCenter = function (center) {
            this.setAttribute('center', center);
        };

        _.prototype.setBound = function (bound) {
            this.setAttribute('bound', bound);
        };

        _.prototype.setRadius = function (radius) {
            this.setAttribute('radius', Math.max(0.1, radius));
        };

        _.prototype.setTopRadius = function (topRadius) {
            this.setAttribute('topRadius', Math.max(0.1, topRadius));
        };

        _.prototype.setBottomRadius = function (bottomRadius) {
            this.setAttribute('bottomRadius', Math.max(0.1, bottomRadius));
        };

        _.prototype.getLength = function () {
            return this.getAttribute('length');
        };

        _.prototype.getCenter = function () {
            var p = drawGeometryHelper.getLonLatByCartesian(this.center);
            var c = new Cesium.Cartesian3.fromDegrees(p[0], p[1], this.length / 2);
            return c;
        };

        _.prototype.getRadius = function () {
            return this.getAttribute('radius');
        };

        _.prototype.getBound = function () {
            return this.getAttribute('bound');
        };

        _.prototype.getTopRadius = function () {
            return this.getAttribute('topRadius');
        };

        _.prototype.getBottomRadius = function () {
            return this.getAttribute('bottomRadius');
        };

        _.prototype.getGeometry = function () {

            if (!(Cesium.defined(this.center) && Cesium.defined(this.radius))) {
                return;
            }

            return new Cesium.CylinderGeometry({
                length: this.length,
                topRadius: this.topRadius,
                bottomRadius: this.bottomRadius,
                vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL,
            });
        };

        _.prototype.getOutlineGeometry = function () {
            return new Cesium.CylinderGeometry({
                length: this.length,
                topRadius: this.topRadius,
                bottomRadius: this.bottomRadius,
                vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL,
            })
        };

        return _;
    })();

    _.prototype.startDrawingMarker = function (options) {

        var options = copyOptions(options, defaultBillboard);
        this.startDrawing(
            function () {
                markers.remove();
                mouseHandler.destroy();
                //tooltip.setVisible(false);
            }
        );

        var _self = this;
        var scene = this._scene;
        var primitives = scene.primitives;

        var markers = new _.BillboardGroup(this, options);

        var mouseHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
        // Now wait for start
        mouseHandler.setInputAction(function (movement) {
            if (movement.position != null) {
                //var cartesian = scene.camera.pickEllipsoid(movement.position, ellipsoid);
                var cartesian = viewer.scene.pickPosition(movement.position);
                if (cartesian) {
                    markers.addBillboard(cartesian);
                    _self.stopDrawing();
                    options.callback(cartesian);
                }
            }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

        mouseHandler.setInputAction(function (movement) {
            var position = movement.endPosition;
            if (position != null) {
                var cartesian = scene.camera.pickEllipsoid(position, ellipsoid);
                //var cartesian = this._DrawGeometryHelper.screen2Cartesian(movement.position.x, movement.position.y)
                // if (cartesian) {
                //     tooltip.showAt(position, "<p>Click to add your marker. Position is: </p>" + getDisplayLatLngString(ellipsoid.cartesianToCartographic(cartesian)));
                // } else {
                //     tooltip.showAt(position, "<p>Click on the globe to add your marker.</p>");
                // }
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    };

    _.prototype.startDrawingPolyline = function (options) {
        var options = copyOptions(options, defaultPolylineOptions);
        options.state = "drawPolyline";
        this.startDrawingPolyshape(false, options);
    }

    _.prototype.startDrawingPolygon = function (options) {

        var options = copyOptions(options, defaultPolygonOptions);
        options.state = "drawPolygon";
        this.startDrawingPolyshape(true, options);
    }

    _.prototype.startDrawingPolyshape = function (isPolygon, options) {
        this.startDrawing(
            function () {
                primitives.remove(poly);
                markers.remove();
                mouseHandler.destroy();
                //tooltip.setVisible(false);
            }
        );
        this.isPolygon = isPolygon;

        var _self = this;
        var scene = this._scene;
        var primitives = scene.primitives;
        //var tooltip = this._tooltip;

        var minPoints = isPolygon ? 3 : 2;
        var poly;
        options.isPolygon = isPolygon;
        if (isPolygon) {
            poly = new DrawGeometryHelper.PolygonPrimitive(options);
        } else {
            poly = new DrawGeometryHelper.PolylinePrimitive(options);
        }
        poly.asynchronous = false;
        primitives.add(poly);

        var positions = [];
        var markers = new _.BillboardGroup(this, defaultBillboard);

        var mouseHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);

        mouseHandler.setInputAction(function (movement) {
            var position = movement.position;
            if (position != null) {
                if (positions.length < minPoints + 2) {
                    return;
                } else {
                    var cartesian = screen2Cartesian(movement.position.x, movement.position.y)
                    if (cartesian) {
                        _self.stopDrawing();
                        options.isPolygon = false;
                        // options.callback(cartesian, "drawEnd");
                        options.callback(cartesian, "drawEnd");
                        if (typeof options.callback === 'function') {
                            if (options.state === "drawPolyline") {

                            } else if (options.state === "drawPolygon") {

                            }

                        }

                    }
                }
            }

        }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

        mouseHandler.setInputAction(function (movement) {
            if (movement.position != null) {
                var cartesian = screen2Cartesian(movement.position.x, movement.position.y)
                if (cartesian) {
                    var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
                    if (positions.length === 0) {
                        positions.push(cartesian.clone());
                        markers.addBillboard(positions[0]);
                    }
                    if (positions.length >= minPoints) {
                        poly.positions = positions;
                        poly._createPrimitive = true;
                    }
                    positions.push(cartesian);
                    markers.addBillboard(cartesian);
                    if (typeof options.callback === 'function') {
                        // options.callback(cartesian, "drawClick");
                        options.callback(cartesian, "drawClick");
                    }
                }
            }

        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

        mouseHandler.setInputAction(function (movement) {
            var position = movement.endPosition;
            if (position != null) {
                if (positions.length === 0) {
                    //tooltip.showAt(position, "<p>Click to add first point</p>");
                } else {
                    var cartesian = screen2Cartesian(movement.endPosition.x, movement.endPosition.y);
                    if (cartesian) {
                        positions.pop();
                        //cartesian.y += (1 + Math.random());
                        positions.push(cartesian);
                        if (positions.length >= minPoints) {
                            poly.positions = positions;
                            poly._createPrimitive = true;
                        }
                        if (typeof options.callback === 'function') {
                            //options.callback(position, "drawMove");
                        }
                        markers.getBillboard(positions.length - 1).position = cartesian;
                        //tooltip.showAt(position, "<p>Click to add new point (" + positions.length + ")</p>" + (positions.length > minPoints ? "<p>Double click to finish drawing</p>" : ""));
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    }

    _.prototype.startDrawingCircle = function (options) {

        var options = copyOptions(options, defaultSurfaceOptions);

        this.startDrawing(
            function cleanUp() {
                if (circle != null) {
                    primitives.remove(circle);
                }
                markers.remove();
                mouseHandler.destroy();
                // tooltip.setVisible(false);
            }
        );

        var _self = this;
        var scene = this._scene;
        var primitives = this._scene.primitives;

        var circle = null;
        var markers = null;

        var mouseHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);

        mouseHandler.setInputAction(function (movement) {
            if (movement.position != null) {
                var cartesian = drawGeometryHelper.screen2Cartesian(movement.position.x, movement.position.y)
                if (cartesian) {
                    if (circle == null) {
                        circle = new _.CirclePrimitive({
                            center: cartesian,
                            radius: 0,
                            asynchronous: false,
                            material: options.material
                        });
                        primitives.add(circle);
                        markers = new _.BillboardGroup(_self, defaultBillboard);
                        markers.addBillboards([cartesian]);
                    } else {
                        if (typeof options.callback === 'function') {
                            options.callback(circle.getCenter(), circle.getRadius());
                        }
                        _self.stopDrawing();
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

        mouseHandler.setInputAction(function (movement) {
            var position = movement.endPosition;
            if (position != null) {
                if (circle == null) {
                    //tooltip.showAt(position, "<p>Click to start drawing the circle</p>");
                } else {
                    var cartesian = drawGeometryHelper.screen2Cartesian(movement.endPosition.x, movement.endPosition.y)
                    if (cartesian) {
                        circle.setRadius(Cesium.Cartesian3.distance(circle.getCenter(), cartesian));
                        markers.updateBillboardsPositions(cartesian);
                        //tooltip.showAt(position, "<p>Move mouse to change circle radius</p><p>Click again to finish drawing</p>");
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    };

    _.prototype.startDrawingGlobe = function (options) {
        var options = copyOptions(options, defaultGlobeOptions);
        options.state = "drawglobe";
        this.startDrawingGlobeshape(true, options);
    };

    _.prototype.startDrawingCylinder = function (options) {
        var options = copyOptions(options, defaultCylinderOptions);
        options.state = "drawCircle";
        this.startDrawingCylindershape(true, options);
    };

    _.prototype.startDrawingExtent = function (options) {

        var options = copyOptions(defaultShapeOptions, options);
        //var defaultExtentOptions = copyOptions(defaultShapeOptions, {});
        this.startDrawing(
            function () {
                if (extent != null) {
                    primitives.remove(extent);
                }
                markers.remove();
                mouseHandler.destroy();
                options.isRectangle = false;
            }
        );

        options.isRectangle = true;
        var _self = this;
        var scene = this._scene;
        var primitives = this._scene.primitives;

        var firstPoint = null;
        var extent = null;
        var markers = null;

        var mouseHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);

        function updateExtent(value) {
            if (extent == null) {
                options.extent = value;
                extent = new _.ExtentPrimitive(options);
                extent.asynchronous = false;
                primitives.add(extent);
            }
            extent.setExtent(value);
            var corners = getExtentCorners(value);
            // create if they do not yet exist
            if (markers == null) {
                markers = new _.BillboardGroup(_self, defaultBillboard);
                markers.addBillboards(corners);
            } else {
                markers.updateBillboardsPositions(corners);
            }
        }

        // Now wait for start
        mouseHandler.setInputAction(function (movement) {
            if (movement.position != null) {
                // var cartesian = scene.camera.pickEllipsoid(movement.position, ellipsoid);
                var cartesian = drawGeometryHelper.Screen2LonLat(movement.position.x, movement.position.y)
                if (cartesian) {
                    if (extent == null) {
                        // create the rectangle
                        firstPoint = cartesian;//ellipsoid.cartesianToCartographic(cartesian);
                        var value = getExtent(firstPoint, firstPoint);
                        updateExtent(value);
                    } else {
                        _self.stopDrawing();
                        if (typeof options.callback === 'function') {
                            options.callback(getExtent(firstPoint, cartesian));//ellipsoid.cartesianToCartographic(cartesian)
                        }
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

        mouseHandler.setInputAction(function (movement) {
            var position = movement.endPosition;
            if (position != null) {
                if (extent == null) {
                    //tooltip.showAt(position, "<p>Click to start drawing rectangle</p>");
                } else {
                    //var cartesian = scene.camera.pickEllipsoid(position, ellipsoid);
                    var cartesian = drawGeometryHelper.Screen2LonLat(movement.endPosition.x, movement.endPosition.y)
                    if (cartesian) {
                        var value = getExtent(firstPoint, cartesian);
                        ellipsoid.cartesianToCartographic(cartesian)
                        updateExtent(value);
                        //tooltip.showAt(position, "<p>Drag to change rectangle extent</p><p>Click again to finish drawing</p>");
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    }

    _.prototype.startDrawingGlobeshape = function (isGlobe, options) {

        this.startDrawing(
            function cleanUp() {
                if (globe != null) {
                    primitives.remove(globe);
                }
                markers.remove();
                mouseHandler.destroy();
                options.isGlobe = null
                // tooltip.setVisible(false);
            }
        );
        options.isGlobe = isGlobe;
        this.drawState = options.state;
        var _self = this;
        var scene = this._scene;
        var primitives = this._scene.primitives;

        var globe = null;
        var markers = null;
        var mouseHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
        mouseHandler.setInputAction(function (movement) {
            if (movement.position != null) {
                var cartesian = drawGeometryHelper.screen2Cartesian(movement.position.x, movement.position.y);
                if (cartesian) {
                    if (globe == null) {
                        globe = new _.GlobePrimitive({
                            center: cartesian,
                            radius: 0,
                            asynchronous: false,
                            material: options.material
                        });
                        primitives.add(globe);
                        markers = new _.BillboardGroup(_self, defaultBillboard);
                        markers.addBillboards([cartesian]);
                        drawGeometryHelper.addPoint = true;
                        drawGeometryHelper.drawState = "DrawRadius";
                    } else {
                        if (typeof options.callback === 'function') {
                            options.callback(globe.getCenter(), globe.getRadius());
                        }
                        _self.stopDrawing();
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
        mouseHandler.setInputAction(function (movement) {
            var position = movement.endPosition;
            if (position != null) {
                if (globe == null) {
                    //tooltip.showAt(position, "<p>Click to start drawing the circle</p>");
                } else {
                    var cartesian = drawGeometryHelper.screen2Cartesian(movement.endPosition.x, movement.endPosition.y);
                    if (cartesian) {
                        globe.setRadius(Cesium.Cartesian3.distance(globe.getCenter(), cartesian));
                        markers.updateBillboardsPositions(cartesian);
                        //tooltip.showAt(position, "<p>Move mouse to change circle radius</p><p>Click again to finish drawing</p>");
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    };

    _.prototype.startDrawingCylindershape = function (isCylinder, options) {

        var options = copyOptions(options, defaultSurfaceOptions);

        this.startDrawing(
            function cleanUp() {
                if (cylinder != null) {
                    primitives.remove(cylinder);
                }
                drawGeometryHelper.state = null;
                drawGeometryHelper.isCylinder = null;
                markers.remove();
                mouseHandler.destroy();
                this.isCylinder = null;
                // tooltip.setVisible(false);
            }
        );
        this.state = options.state;
        options.isCylinder = isCylinder;
        var _self = this;
        var scene = this._scene;
        var primitives = this._scene.primitives;

        var cylinder = null;
        var markers = null;

        var mouseHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);

        mouseHandler.setInputAction(function (movement) {
            if (movement.position != null) {
                var cartesian = drawGeometryHelper.screen2Cartesian(movement.position.x, movement.position.y)
                if (cartesian) {
                    if (cylinder == null) {
                        cylinder = new _.CylinderPrimitive({
                            center: cartesian,
                            length: 100,
                            topRadius: 1,
                            bottomRadius: 1,
                            asynchronous: false,
                            material: options.material
                        });
                        primitives.add(cylinder);
                        markers = new _.BillboardGroup(_self, defaultBillboard);
                        markers.addBillboards([cartesian]);
                    } else {
                        if (drawGeometryHelper.state === "drawCircle") {
                            cylinder.setBound(cartesian);
                            drawGeometryHelper.state = "drawCylinder";
                        } else if (drawGeometryHelper.state === "drawCylinder") {
                            if (typeof options.callback === 'function') {
                                options.callback(cylinder.getCenter(), cylinder.getLength(), cylinder.getBottomRadius(), cylinder.getTopRadius());
                            }
                            _self.stopDrawing();
                        }
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

        mouseHandler.setInputAction(function (movement) {
            var position = movement.endPosition;
            if (position != null) {
                if (cylinder == null) {
                    //tooltip.showAt(position, "<p>Click to start drawing the circle</p>");
                } else {
                    var cartesian = drawGeometryHelper.screen2Cartesian(movement.endPosition.x, movement.endPosition.y)
                    if (cartesian) {
                        if (drawGeometryHelper.state === "drawCircle") {
                            cylinder.setTopRadius(Cesium.Cartesian3.distance(cylinder.getCenter(), cartesian));
                            cylinder.setBottomRadius(Cesium.Cartesian3.distance(cylinder.getCenter(), cartesian));
                            markers.updateBillboardsPositions(cartesian);
                            //tooltip.showAt(position, "<p>Move mouse to change circle radius</p><p>Click again to finish drawing</p>");
                        } else if (drawGeometryHelper.state === "drawCylinder") {
                            cylinder.setLength(Cesium.Cartesian3.distance(cylinder.getBound(), cartesian));
                            markers.updateBillboardsPositions(cartesian);
                        }
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    };

    _.prototype.startDrawingCuboid = function (options) {

        var options = copyOptions(defaultCuboidOptions, options);
        //var defaultExtentOptions = copyOptions(defaultShapeOptions, {});
        this.startDrawing(
            function () {
                if (cuboid != null) {
                    primitives.remove(cuboid);
                }
                markers.remove();
                mouseHandler.destroy();
                options.isCuboid = false;
                this.state = null;
            }
        );
        this.state = null;
        options.isCuboid = true;
        var _self = this;
        var scene = this._scene;
        var primitives = this._scene.primitives;

        var cuboid = null;
        var markers = null;

        var mouseHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);

        function updateExtent() {
            if (cuboid == null) {
                cuboid = new _.CuboidPrimitive(options);
                cuboid.asynchronous = false;
                primitives.add(cuboid);
            }
            cuboid.setCenter();
            cuboid.setDimensions();
            var corners = cuboid.getCuboidCorners();
            // create if they do not yet exist
            if (markers == null) {
                markers = new _.BillboardGroup(_self, defaultBillboard);
                markers.addBillboards(corners);
            } else {
                markers.updateBillboardsPositions(corners);
            }
        }

        // Now wait for start
        mouseHandler.setInputAction(function (movement) {
            if (movement.position != null) {
                // var cartesian = scene.camera.pickEllipsoid(movement.position, ellipsoid);
                var cartesian = drawGeometryHelper.Screen2LonLat(movement.position.x, movement.position.y)
                if (cartesian) {
                    if (cuboid == null) {
                        // create the rectangle
                        //firstPoint = cartesian;//ellipsoid.cartesianToCartographic(cartesian);
                        var lonlat = drawGeometryHelper.getLonLatByCartesian(cartesian);
                        options.firstPoint = lonlat;
                        options.scendPoint = lonlat;
                        options.height = 100;
                        updateExtent();
                        this.state = "drawRectangle";
                    } else {
                        if (this.state === "drawRectangle") {
                            this.state = "drawCuboid";
                        } else if (this.state === "drawCuboid") {
                            if (typeof options.callback === 'function') {
                                options.callback(cuboid.getCenter(), cuboid.getDimensions(), cuboid.getFirstPoint(), cuboid.getScendPoint());//ellipsoid.cartesianToCartographic(cartesian)
                            }
                            _self.stopDrawing();
                        }
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

        mouseHandler.setInputAction(function (movement) {
            var position = movement.endPosition;
            if (position != null) {
                if (cuboid == null) {
                    //tooltip.showAt(position, "<p>Click to start drawing rectangle</p>");
                } else {
                    //var cartesian = scene.camera.pickEllipsoid(position, ellipsoid);
                    var cartesian = drawGeometryHelper.Screen2LonLat(movement.endPosition.x, movement.endPosition.y)
                    if (cartesian) {
                        var lonlat = drawGeometryHelper.getLonLatByCartesian(cartesian);
                        // cartesian = new Cesium.Cartesian3.fromDegrees(lonlat[0], lonlat[1], 1);
                        if (this.state === "drawRectangle") {
                            cuboid.setScendPoint(lonlat);
                        } else if (this.state === "drawCuboid") {
                            var p1 = new Cesium.Cartesian3.fromDegrees(lonlat[0], lonlat[1]);
                            var p2 = new Cesium.Cartesian3.fromDegrees(cuboid.getScendPoint()[0], cuboid.getScendPoint()[1]);
                            cuboid.setHeight(Cesium.Cartesian3.distance(p1, p2))
                        }
                        updateExtent();
                        //tooltip.showAt(position, "<p>Drag to change rectangle extent</p><p>Click again to finish drawing</p>");
                    }
                }
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    }

    _.prototype.enhancePrimitives = function () {

        var DrawGeometryHelper = this;

        Cesium.Billboard.prototype.setEditable = function () {
            if (this._editable) {
                return;
            }
            this._editable = true;
            var billboard = this;
            var _self = this;

            function enableRotation(enable) {
                DrawGeometryHelper._scene.screenSpaceCameraController.enableRotate = enable;
            }

            setListener(billboard, 'leftDown', function (position) {
                function onDrag(position) {
                    billboard.position = position;
                    _self.executeListeners({name: 'drag', positions: position});
                }

                function onDragEnd(position) {
                    handler.destroy();
                    enableRotation(true);
                    _self.executeListeners({name: 'dragEnd', positions: position});
                }

                var handler = new Cesium.ScreenSpaceEventHandler(DrawGeometryHelper._scene.canvas);
                handler.setInputAction(function (movement) {
                    var cartesian = DrawGeometryHelper._scene.camera.pickEllipsoid(movement.endPosition, ellipsoid);
                    if (cartesian) {
                        onDrag(cartesian);
                    } else {
                        onDragEnd(cartesian);
                    }
                }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
                handler.setInputAction(function (movement) {
                    onDragEnd(DrawGeometryHelper._scene.camera.pickEllipsoid(movement.position, ellipsoid));
                }, Cesium.ScreenSpaceEventType.LEFT_UP);

                enableRotation(false);
            });
            enhanceWithListeners(billboard);
        }

    };

    function enhanceWithListeners(element) {

        element._listeners = {};

        element.addListener = function (name, callback) {
            this._listeners[name] = (this._listeners[name] || []);
            this._listeners[name].push(callback);
            return this._listeners[name].length;
        };

        element.executeListeners = function (event, defaultCallback) {
            if (this._listeners[event.name] && this._listeners[event.name].length > 0) {
                var index = 0;
                for (; index < this._listeners[event.name].length; index++) {
                    this._listeners[event.name][index](event);
                }
            } else {
                if (defaultCallback) {
                    defaultCallback(event);
                }
            }
        }

    };

    return _;

})();

