JavaScript: How to convert Mercator Sphere coordinates to latitude and longitude

July 29th, 2011 No comments

     In one of my previous posts I told about How to implement Drag’n’Dropping of pushpin using ArcGIS JavaScript API. According to the ArcGIS JavaScript API help, the map’s spatial reference is based on the first layer added to the map and it cannot be overridden (http://resources.esri.com/help/9.3/arcgisserver/apis/javascript/arcgis/help/jshelp_start.htm#jshelp/inside_guidelines.htm). I use World Street Map as the first layer, that means that the current spatial reference corresponds to such coordinate system as Web Mercator Auxiliary Sphere (WKID 102100) with decimal degrees as Units of measure (http://www.arcgis.com/home/item.html?id=3b93337983e9436f8db950e38a8629af and http://help.arcgis.com/en/arcgisonline/content/index.html#//011q00000002000000.htm). Therefore, the coordinates of pushpin’s current position will be in the spherical Mercator projection.

When intending to integrate with other services or systems, it’s reasonable to convert Mercator Sphere coordinates to latitude and longitude that are the most popular for specifying geographical locations. Below is the JavaScript function, which performs the conversion:

function MercatorToLatLon(mercX, mercY) {

    var rMajor = 6378137; //Equatorial Radius, WGS84
    var shift  = Math.PI * rMajor;
    var lon    = mercX / shift * 180.0;
    var lat    = mercY / shift * 180.0;
    lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180.0)) - Math.PI / 2.0);

    return { 'Lon': lon, 'Lat': lat };            
}

How to use the function:

var currentPushpinPoint = pushpin.GetCurrentLocation();
var LatLon = MercatorToLatLon(currentPushpinPoint.x, currentPushpinPoint.y);
alert('Result: Lat = ' + LatLon.Lat + ', Lon = ' + LatLon.Lon);

The inverse JavaScript function is here

Related posts:

JavaScript: How to add a script-tag dynamically

July 21st, 2011 No comments

     Sometimes it’s necessary to add a JavaScript to a page dynamically. For example, if you have a huge and rarely used JavaScript, it’s unreasonable to load it jointly with the page. The more wisely to load it on demand, when it’s really needed. It’s, so called, lazy loading. One of the ways to achieve it is presented below:

// url      - a string-based url of js-file
// callback - reference to a user-defined method, which will be called when loading is finished
function AddScript(url, callback) {
    var script   = document.createElement('script');
    script.src   = url;
    script.type  = 'text/javascript';
    script.defer = false;

    if (typeof callback != "undefined" && callback != null) {
    
        // IE only, connect to event, which fires when JavaScript is loaded
        script.onreadystatechange = function() {
            if (this.readyState == 'complete' || this.readyState == 'loaded') {
                this.onreadystatechange = this.onload = null; // prevent duplicate calls                        
                callback();
            }
        }

        // FireFox and others, connect to event, which fires when JavaScript is loaded
        script.onload = function() {
            this.onreadystatechange = this.onload = null; // prevent duplicate calls                    
            callback();
        };
    }
    
    var head = document.getElementsByTagName('head').item(0);
    head.appendChild(script);
}

The method AddScript accepts the url of js-file, the contents of which should be loaded, and callback – an user-defined method that will be called when the load is complete.

Taking into account that browsers may (and I believe they do that) load JavaScript asynchronously, the callback is very useful if you need to do some actions based on just loaded JavaScript. In other words, your actions precisely will not be performed before loading has finished or even started, or your actions could cause an error on the page.

Usage:

AddScript("jquery-1.6.2.js",
    function() {
        alert($(document).attr('title'));
    });

ps The method was tested in FireFox and Internet Explorer

Related posts:
Categories: JavaScript Tags:

Silverlight for Windows Phone 7: How to check if the current thread is UI one

July 19th, 2011 No comments

     If in your wp7 application you are planning to use background threads for some long operations, you likely need from time to time to update controls to reflect state and/or result of operations. Any update has to be done in the UI thread, in which all controls are created. Being in another thread we can use object Dispatcher to invoke a method inside the UI thread:

Dispatcher.BeginInvoke(delegate() { /* update controls */ });

Before invoking a method, it’s a good practice to check whether the current calling thread is different from UI thread. And if the current thread is the UI thread, we can update controls directly, not using Dispatcher. To examine the current thread, there is a method CheckAccess() of Dispatcher object. It returns true if the calling thread is the UI thread, otherwise, false. Taking this fact into account, a typical way to update control is:

public void UpdateSomeTextBox(string text)
{
    if (Dispatcher.CheckAccess()) // if it's UI thread, just set the new text directly
        someTextBox.Text = text;
    else // otherwise, invoke UpdateSomeTextBox in UI thread
        Dispatcher.BeginInvoke(delegate()
        {
            UpdateSomeTextBox(text);
        });
}

Interestingly enough that the method Dispatcher.CheckAccess() is invisible for Intellisense, because, for some unknown reason, it has been marked with a [EditorBrowsable(EditorBrowsableState.Never)] attribute. It looks like Microsoft doesn’t want developers to use CheckAccess. But does an alternative exist? Let’s try to find it. First of all, let’s take a look at this method through the Reflector:

[EditorBrowsable(EditorBrowsableState.Never)]
public bool CheckAccess()
{
    return (NativeHost.Current.UIThreadID == Thread.CurrentThread.ManagedThreadId);
}

It just compares an identifier of UI thread with an identifier of the current thread. NativeHost is an internal class, therefore we can’t get NativeHost.Current.UIThreadID outside of a dll where property declared. How else can we get the identifier of UI thread? As we know all controls, including page, are created inside UI thread. It means that inside a page constructor the property Thread.CurrentThread.ManagedThreadId is the required UI thread identifier. So, we need take this identifier and preserve to use later. The following example demonstrates how it could look like:

using System;
using Microsoft.Phone.Controls;

namespace WindowsPhoneApplication1
{
    public partial class MainPage : PhoneApplicationPage
    {
        private int _uiThreadID = -1;

        private bool IsUIThread
        {
            get { return _uiThreadID == System.Threading.Thread.CurrentThread.ManagedThreadId; }
        }

        // Constructor
        public MainPage()
        {
	    // preserve UI thread id
            _uiThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
            InitializeComponent();
        }

        public void UpdateSomeTextBox(string text)
        {
            if (IsUIThread)  // if it's UI thread, just set the new text directly
                someTextBox.Text = text;
            else   // otherwise, invoke UpdateSomeTextBox in UI thread
                Dispatcher.BeginInvoke(delegate()
                {
                    UpdateSomeTextBox(text);
                });
        }
    }
}

Thanks!

Related posts:

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:

jQuery: Disable hyperlink

July 1st, 2011 No comments

     The simplest way to disable/enable a link by using jQuery is:

function DisableLinks(links) {
    links.attr('disabled', 'true');
}
function EnableLinks(links) {
    links.removeAttr('disabled');
    // or links.attr('disabled', 'false');
}

However, the ‘disabled‘-attribute is supported only by IE, and the given example won’t work, for instance, in Firefox.

To circumvent this problem I’d like to suggest the following code:

<head>
    <script type="text/javascript" src="jquery-1.6.2.js"></script>
    <style type="text/css">
      .disabledLink
      {
  	        color: Gray;
  	        text-decoration: none;
      }
    </style>    
    <script type="text/javascript">
    
        function DisableLinks(links) {
            links.each(
                function(index, linkDomElement) {
                    var link = $(linkDomElement);            

                    // get the 'href' and 'onclick' attribute values
                    var linkHRef    = link.attr('href');
                    var linkOnClick = link.attr('onclick');

                    // set the dummy attribute values
                    link.attr('href', '#').attr('onclick', 'return false;');

                    // add a temporary attribute to keep the original 'href' value
                    if (linkHRef != undefined)
                        link.attr('tmphref', linkHRef);

                    // add a temporary attribute to keep the original 'onclick' value
                    if (linkOnClick != undefined)
                        link.attr('tmponclick', linkOnClick);

                    // add the class decorating disabled links
                    link.addClass('disabledLink');
                });
        }
        
        function EnableLinks(links) {
            links.each(
                function(index, linkDomElement) {
                    var link = $(linkDomElement);

                    // get the original values of 'href' and 'onclick' attributes from temporary attributes
                    var originalLinkHRef    = link.attr('tmphref');
                    var originalLinkOnClick = link.attr('tmponclick');

                    // restore the original 'href' value
                    if (originalLinkHRef != undefined)
                        link.attr('href', originalLinkHRef);

                    // restore the original 'onclick' value
                    if (originalLinkOnClick != undefined)
                        link.attr('onclick', originalLinkOnClick);

                    // remove temporary attributes
                    link.removeAttr('tmphref').removeAttr('tmponclick');

                    // remove the class decorating disabled links
                    link.removeClass('disabledLink');
                });
        }
    </script>
</head>

The methods DisableLinks and EnableLinks work successfully in different browsers. The css-class disabledLink allows to define the style of disabled link.

Below the sample of usage:

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

<!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>
    // the head is the same as it was in previous listing
</head>
<body>  
    <a id="disableLinks" href="#" onclick="DisableLinks($('#linkDiv a')); $('#enableLinks').show(); $('#disableLinks').hide(); return false;">Disable the rest of links</a>
    <a id="enableLinks" style="display:none" href="#" onclick="EnableLinks($('#linkDiv a')); $('#enableLinks').hide(); $('#disableLinks').show(); return false;">Enable the rest of links</a>
    <br />
    <br />
    <br />
    <div id="linkDiv">                
        <a href="http://google.com" onclick="alert('Transfer to www.google.com');">Goooogle</a>
        <br />
        <a href="#" onclick="alert('Hello!'); return false;">Hello, alert!</a>
        <br />
    </div>    
</body>
</html>

Links are enabled

Links are enabled

Links are disabled

Links are disabled

Related posts: