Wednesday, July 4, 2012

Using different projections within ArcGis JavaScript Applications


1.     Introduction


Did you ever have a displacement of points of 100m on your map, then you clearly run into problems with datum corrections. This document explains how I tackle this issue , and how ArcGis 10.1 helps you.

Public geo databases as ‘Openstreet map’  , ‘Google maps’  or ‘Google Streetview’ are all based on the spatial reference WGS84 and this is in most cases not the spatial reference of the map.  When trying to integrate this information with a custom ArcGis JavaScript, you will need to project  your data towards WGS 84 spatial reference coordinates.

To project your data into WGS 84 spatial reference, ESRI expose a project transformation service through the method project of the  geometry service of an ArcGis server.  As from ArcGis 10.1 and JavaScript API 3.0 the project method has added a transformation parameter. The list of all transformations can be found at the URL  http://help.arcgis.com/en/webapi/javascript/arcgis/help/jshelp/dattrans.htm

In pre version 3.0, if a transformation is required, a SOAP  web service must be used, however using this in JavaScript requires some extra data processing,  a JSON approach is more practical.

Sometimes you will need using more than one transformation in the case no direct transformation of projection to another projection is available as outlined in the figure below.


2.     Transformation between WGS84 and WebMercator (102100/3857)


Because I could not find a transformation in the list on the URL specified in the ‘Introduction’ I wrote two  functions that handle this transformations in both directions based on some C++ code I found. Doing so, I did not need an async REST call to the ArcGIs Geometry service, speeding up the transformation.

The code for doing these transformations are :

function toGeographic(xMercator, yMercator) {
  if (Math.abs(xMercator) < 180 && Math.abs(yMercator) < 90)
     return null;
  if ((Math.abs(xMercator) > 20037508.3427892) || (Math.abs(yMercator) > 20037508.3427892))
      return null;
  var x = xMercator;
  var y = yMercator;
  var w1 = x = x / 6378137.0;
  var w2 = x * 57.295779513082323;
  var w3 = Math.floor((x + 180.0) / 360.0);
  x = w2 - (w3 * 360.0);
  y = (1.5707963267948966 - (2.0 * Math.atan(Math.exp((-1.0 * y) / 6378137.0)))) * 57.295779513082323;
  return {
    x: x,
    y: y
  }
}

function toWebMercator(xlon, ylat) {
  if ((Math.abs(mercatorX_lon) > 180 || Math.abs(mercatorY_lat) > 90))
    return null;
  var num = xlon * 0.017453292519943295;
  var x = 6378137.0 * num;
  var a = ylat * 0.017453292519943295;
  var y = 3189068.5 * Math.Log((1.0 + Math.Sin(a)) / (1.0 - Math.Sin(a)));
  return {
     x: x,
     y: y
  }
}

3.     Other Project transformation


Creation of a generic  function for doing projection transformation requires as input either a wkid or wkt of the transformation that corresponds to an item of the transformation list found at the URL specified earlier.  In a real world application a transformation happens between an input spatial reference (wkid) and an output spatial reference (wkid).
I could not find a relation between the transformation list wkid’s and the wkid’s  of spatial references.
The only solution for this lack of automatic solution based on the two wkid’s was to add a parameter at the layer configuration specifying the transformation needed for the WGS 84 spatial references conversion.  This solves all my issues for the transformation of country spatial references to WGS 84 spatial reference, making integration with Google Streetview possible and other WGS 84 related geographical data.
I also had a spatial reference problem in case WMS layers are used for doing spatial queries. A WMS query requires the same spatial reference as the WMS service expose. A solution is to add a parameter to the WMS configuration specifying the transformation needed for modifying the base layer spatial reference to the WMS spatial reference for the geometry involved.
The result of the project conversion consist of two cases, one that looks if it involves a Webmercator / WGS 84 transformation, and a second that will use the geometry service of the ArcGis server. The method requires version 3.0 of the ArcGis JavaScript API and ArcGis Server 10.1.

convertProjection: function (points, toSpacialReference, transformation, callback) {
   var wkidIn = points[0].spatialReference.wkid;
   var wkidOut = toSpacialReference.wkid;
   var result = null;
   var resultPoints = new Array();
   var point;
   if ((wkidIn == '102100' || wkidIn == '3857') && wkidOut == '4326') {
     for (var i = 0; i < points.length; i++) {
        result = toGeographic(points[0].x, points[0].y);
        point = new esri.geometry.Point([result.x, result.y], new esri.SpatialReference({ wkid: 4326 }));
        resultPoints.push(point);
     }
     callback(resultPoints);
   }
   else if (wkidIn == '4326' && (wkidOut == '102100' || wkidIn == '3857')) {
     for (var i = 0; i < points.length; i++) {
        result = toWebMercator(points[0].x, points[0].y);
        point = new esri.geometry.Point([result.x, result.y], new esri.SpatialReference({ wkid: 102100 }));
        resultPoints.push(point);
     }
     callback(resultPoints);
   }
   else {
     var params = new esri.tasks.ProjectParameters();
     params.geometries = points;
     params.outSR = toSpacialReference;
     params.transformation = transformation;
     params.transformationForward = false;
     GisOperation.getGeometryService().project(params,
       function (projectedPoints) {
          callback(projectedPoints);
       }, function errorprojected(err) {
          logging.logMessage("E", "projection convertion failed -->" + err.message, "GisGeoProcessing");
          callBack(null);
     });
   }
}

4.     Transformation in an application


The above transformation worked well for me having as spatial reference of Lambert 72 (31370) of the map, and using Google Streetview to show the location selected. The transformation used was 1610.

2 comments:

  1. I was having a really hard time trying to make my map have a specific spatial reference just for the sake of not having to use a service to project points. This helped and is a lot simpler for what I was trying to do.

    Thanks!

    ReplyDelete
  2. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me..
    I am a regular follower of your blog. Really very informative post you shared here.
    Kindly keep blogging. If anyone wants to become a Front end developer learn from Javascript Training in Chennai .
    or Javascript Training in Chennai.
    Nowadays JavaScript has tons of job opportunities on various vertical industry. ES6 Training in Chennai

    ReplyDelete