Archive

Posts Tagged ‘esri.Graphic’

ArcGIS JavaScript API: How to implement Drag’n’Dropping of pushpin

July 15th, 2011 No comments

     It happened that in some part of our web based project I had to use ArcGIS to communicate with world map. I have a good experience in working with GoogleEarth and Bing/VirtualEarth and would prefer to deal with them, but the usage of ArcGIS was a strong requirement of the customer (moreover it has to be an old 1.2 version). I’ve been faced with the fact that the implementation of a simple drag’n’dropping of pushpin (a small image) is not a trivial task if you use ArcGIS JavaScript API. I couldn’t find a working solution. A built-in dojox.gfx.Moveable object doesn’t meet our requirements as well, because it assumes that, at first, user selects pushpin by clicking on it, and only then he will be able to click on pushpin again and start dragging it. It’s inconvenient and intuitively incomprehensible. As the result I’ve implemented this functionality by my own. The cornerstone is an object DragableGraphic, which acts as wrapper for esri.Graphic object and provides a drag’n’drop ability leaning on the appropriate map events:

// represents wrapper for graphic object to provide drag'n'drop ability
// map     - instance of esri.Map
// graphic - instance of esri.Graphic
function DragableGraphic(map, graphic) {
    
    this._map             = map;              // parent map
    this._graphic         = graphic;          // moveable graphic object
    this._currentLocation = graphic.geometry; // current location of graphic object
    this._isInUse         = false;            // true if graphic object is in dragging

    this._onMouseDownHandler    = null;  // pointer to onMouseDown    event handler
    this._onMouseUpHandler      = null;  // pointer to onMouseUp      event handler
    this._onMouseDragHandler    = null;  // pointer to onMouseDrag    event handler
    this._onMouseDragEndHandler = null;  // pointer to onMouseDragEnd event handler

    // initializes object
    this._init = function() {
        // bind to onMouseDown that fires when graphic object is clicked on
        if (this._onMouseDownHandler == null)
            this._onMouseDownHandler = dojo.connect(this._map.graphics, "onMouseDown", this, this.onMouseDown);                
    }
    
    // releases resources used by object
    this.Dispose = function() {
        // disconnect from onMouseDown
        if (this._onMouseDownHandler != null) {                    
            dojo.disconnect(this._onMouseDownHandler);
            this._onMouseDownHandler = null;
        }

        // if graphic object is in use, release it
        if (this._isInUse) {
            this._map.enableMapNavigation(); // enable default left button behavior
            this.unbindDragNDropEvents();    // disconnect from drag'n'drop events
            this._isInUse = false;           // mark graphic object as not in use
        }
    }

    // binds to drag'n'drop events
    this.bindToDragNDropEvents = function() {
        // bind to onMouseDrag that fires when graphic object is being dragged
        if (this._onMouseDragHandler == null)
            this._onMouseDragHandler = dojo.connect(this._map, "onMouseDrag", this, this.onMouseDrag);
        // bind to onMouseDragEnd that fires when graphic object is dropped
        if (this._onMouseUpHandler == null)
            this._onMouseUpHandler = dojo.connect(this._map.graphics, "onMouseUp", this, this.onMouseUp);
    }

    // unbinds from drag'n'drop events
    this.unbindDragNDropEvents = function() {
        // disconnect from onMouseDrag
        if (this._onMouseDragHandler != null) {
            dojo.disconnect(this._onMouseDragHandler);
            this._onMouseDragHandler = null;
        }
        // disconnect from onMouseUp
        if (this._onMouseUpHandler != null) {
            dojo.disconnect(this._onMouseUpHandler);
            this._onMouseUpHandler = null;
        }
    }

    // fires when graphic object is clicked on
    this.onMouseDown = function(event) {
        if (event.button != 1) // ignore if clicked button is not a left one
            return;

        // ignore if clicked graphic object is not an object contained in the current DragableGraphic-object
        if (event.graphic != this._graphic)
            return;

        // if graphic object isn't in use yet, start using it
        if (!this._isInUse) {
            this._isInUse = true;             // mark graphic object as in use
            this._map.disableMapNavigation(); // prevent default left button behavior
            this.bindToDragNDropEvents();     // connect to drag'n'drop events
        }
    }

    // fires when graphic object is dropped
    this.onMouseUp = function(event) {
        if (event.button != 1) // ignore if clicked button is not a left one
            return;

        // ignore if clicked graphic object is not an object contained in the current DragableGraphic-object
        if (event.graphic != this._graphic)
            return;

        // if graphic object is in use, release it
        if (this._isInUse) {
            this._currentLocation = event.mapPoint;    // preserve end graphic object location
            this._graphic.setGeometry(event.mapPoint); // move graphic object to the end location
            this._map.enableMapNavigation();           // enable default left button behavior
            this.unbindDragNDropEvents();              // disconnect from drag'n'drop events
            this._isInUse = false;                     // mark graphic object as not in use
        }
    }

    // fires when graphic object is being dragged
    this.onMouseDrag = function(event) {                
        if (this._isInUse) {
            this._currentLocation = event.mapPoint;    // preserve a new graphic object location
            this._graphic.setGeometry(event.mapPoint); // move graphic object to the new location
        }
    }
    
    this._init(); // initialize object
}

The next object represents a Pushpin: it creates esri.Graphic instance, adds it to the map and creates DragableGraphic inside itself:

// represents wrapper for DragableGraphic
// map      - instance of esri.Map
// imgUrl   - string-based image url
// height   - image height in pixels
// height   - image width  in pixels
// mapPoint - instance of esri.geometry.Point
function Pushpin(map, imgUrl, height, width, mapPoint) {

    this._map             = map;      // parent map
    this._initialLocation = mapPoint; // initial location of pushpin
    this._height          = height;   // pushpin image height
    this._width           = width;    // pushpin image width
    this._imgUrl          = imgUrl;   // pushpin image url
    this._dragableGraphic = null;     // reference to DragableGraphic object

    // initializes object
    this._init = function() {
        // create PictureMarkerSymbol
        var symbol = new esri.symbol.PictureMarkerSymbol(this._imgUrl, this._width, this._height);
        // create graphic object based on symbol
        var graphic = new esri.Graphic(this._initialLocation);
        graphic.setSymbol(symbol);
        // add graphic object to the map
        this._map.graphics.add(graphic);

        // create DragableGraphic object to provide drag'n'drop ability for graphic object
        this._dragableGraphic = new DragableGraphic(this._map, graphic);
    }

    // releases resources used by object
    this.Dispose = function() {                    
        if (this._dragableGraphic != null) {
            // remove graphic object from the map
            this._map.graphics.remove(this._dragableGraphic._graphic);
            
            // call Dispose of DragableGraphic
            this._dragableGraphic.Dispose();
        }
    }
    // returns the current location of Pushpin on the map
    this.GetCurrentLocation = function() {
        if (this._dragableGraphic != null)
            return this._dragableGraphic._currentLocation;
        else
            return null;
    }

    this._init(); // initialize object
}

The following piece of code demonstrates how to create Pushpin:

var mapPoint = new esri.geometry.Point(-13042885, 4033247, new esri.SpatialReference({ wkid: 102100 }));
var pushpin  = new Pushpin(map, "http://help.arcgis.com/en/webapi/javascript/arcgis/demos/images/play.png", 25, 25, mapPoint);

var mapPoint2 = new esri.geometry.Point(-13042885, 2033244, new esri.SpatialReference({ wkid: 102100 }));
var pushpin2  = new Pushpin(map, "http://help.arcgis.com/en/webapi/javascript/arcgis/demos/images/play.png", 25, 25, mapPoint2);

Below is an example of aspx-page that uses Pushpin:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ArcGISDragNDrop.aspx.cs" Inherits="ChoosePlaceOnMap.ArcGISDragNDrop" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <meta http-equiv="X-UA-Compatible" content="IE=7,IE=9" />     
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no" />      
    
    <title>ArcGIS Drag'n'drop sample</title>    
    
    <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/2.3/js/dojo/dijit/themes/claro/claro.css" />
    <link rel="stylesheet" type="text/css" href= "http://serverapi.arcgisonline.com/jsapi/arcgis/1.2/js/dojo/dijit/themes/tundra/tundra.css" />    
    
    <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis?v=1.2"></script>
    
    
    <script type="text/javascript">

	// ... DragableGraphic declaration has to be here
	// ... Pushpin declaration has to be here 

        var map      = null;
        var pushpin  = null;
        var pushpin2 = null;
        
        dojo.require("esri.map");

        dojo.addOnLoad(function() {
            dojo.style(dojo.byId("map"), { width: dojo.contentBox("map").w + "px", height: dojo.contentBox("map").h + "px" });
            esriConfig.defaults.map.slider = { right: "10px", top: "10px", width: null, height: "100px" };

            map = new esri.Map("map", { wrapAround180: true, nav: false });

            // pushpins will be added after map layers is loaded
            dojo.connect(map, "onLoad", function() {                
                var mapPoint = new esri.geometry.Point(-13042885, 4033247, new esri.SpatialReference({ wkid: 102100 }));
                pushpin = new Pushpin(map, "http://help.arcgis.com/en/webapi/javascript/arcgis/demos/images/play.png", 25, 25, mapPoint);

                var mapPoint2 = new esri.geometry.Point(-13042885, 2033244, new esri.SpatialReference({ wkid: 102100 }));
                pushpin2 = new Pushpin(map, "http://help.arcgis.com/en/webapi/javascript/arcgis/demos/images/play.png", 25, 25, mapPoint2);
            });

            var layer = new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer");
            map.addLayer(layer);            

            //var tmpPushpinCurretnLocation = pushpin2.GetCurrentLocation();

            //pushpin.Dispose();
            //pushpin2.Dispose();
        });        
    </script>
</head>
<body class="claro">    
    <form id="form1" runat="server">
        <div id="map" style="position:absolute; left:400px; top:100px; width:1024px; height:512px;border: 1px solid #A8A8A8; background-color:#ffffff; " 

/>                
    </form>    
</body>
</html>

ArcGIS Sample App Start

ArcGIS Sample App right after Start

ArcGIS Sample App After Dragging of pushpins

ArcGIS Sample App right After Dragging of pushpins

The full project of the example you can download from here.

Related posts: