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 right after Start
|
ArcGIS Sample App right After Dragging of pushpins
|
The full project of the example you can download from here.
Related posts: