How to zoom to a non-Web Mercator extent?

2063
12
Jump to solution
08-14-2013 03:56 AM
StephenLead
Regular Contributor III
Given an ArcGIS Server service which is stored in non-Web Mercator coordinates, how can I zoom to its extent when the map is using Web Mercator?

There is a method geographicToWebMercator - but what if the original coordinates are not geographic?

Below is some sample code to illustrate the problem.

Thanks,
Steve

<!DOCTYPE html> <html>   <head>     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">     <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">     <title>Zoom to non-WM dynamic layer</title>     <link rel="stylesheet" href="http://js.arcgis.com/3.6/js/esri/css/esri.css"/>     <style>       html,body,#mapDiv{         padding:0;         margin:0;         height:100%;       }     </style>     <script src="http://js.arcgis.com/3.6/"></script>     <script>       dojo.require("esri.map");       var map;       var url = "http://loczy.mfgi.hu/ArcGIS/rest/services/TJAM/surf_ujfalu_t/MapServer/"         function init() {                  //Get the layer's extent from ArcGIS Server         esri.request({        url: url,   content: {f:  "json"},   handleAs: "json",   callbackParamName: "callback",   load: function(response) {                //Create the map. Since we're specifying the Gray basemap it'll use Web Mercator    map = new esri.Map("mapDiv", {basemap: "gray"});      //Add the dynamic layer. Although it's not stored in Web Mercator, it's automatically    //projected on-the-fly and displays in the correct location    var dynamicMapServiceLayer = new esri.layers.ArcGISDynamicMapServiceLayer(url);            map.addLayer(dynamicMapServiceLayer);                //Zoom the map to the new layer's extent. This fails, since the extent doesn't use WM    map.setExtent(response.initialExtent);               },       error: function (error) {          alert("Sorry, there was a problem zooming to the new layer");               }  });   }         dojo.ready(init);     </script>   </head>   <body>     <div id="mapDiv"></div>\   </body> </html>
0 Kudos
1 Solution

Accepted Solutions
JasonZou
Occasional Contributor III
An alternative way is to use geometryService.project to reproject the extent in non-Web Mercator to Web Mercator, and use the result to zoom the map to in the callback function.

View solution in original post

12 Replies
JakubMalec
New Contributor III
If you know the initialExtent's Spatial Reference WKID, you can convert the coordinates using proj4js (http://trac.osgeo.org/proj4js/)

Using proj4js is easy, it'll look like this:

var srcProj = new Proj4js.Proj('EPSG:YOUR_WKID');
var destProj = new Proj4js.Proj('EPSG:3857'); // 3857 is the default map WKID

var bottomLeft = new Proj4js.Point(response.initialExtent.minx, response.initialExtent.miny);
Proj4js.transform(srcProj, destProj, bottomLeft);

var topRight = new Proj4js.Point(response.initialExtent.maxx, response.initialExtent.maxy);
Proj4js.transform(srcProj, destProj, topRight);

map.setExtent(bottomLeft.x, bottomLeft.y, topRight.x, topRight.y, map.spatialReference);

0 Kudos
StephenLead
Regular Contributor III
Thanks Jakub,

This looks really promising. I'm a bit confused as to which file to include, though.

I downloaded the package from https://github.com/proj4js/proj4js - I assumed that I should add dist/proj4.js or dist/proj4.min.js

But when I use either file, the line new Proj4js.Proj is not recognised.

The user guide at http://trac.osgeo.org/proj4js/wiki/UserGuide seems a bit older (possibly out of date), and references:

// include the library
<script src="lib/proj4js-combined.js"></script>


but this file is no longer included in the package. The GitHub repo contains mention of grunt - is this a requirement?

Thanks for any advice,
Steve

EDIT: nevermind, I used the legacy code from http://trac.osgeo.org/proj4js/wiki/Download and it seems to work better
0 Kudos
JasonZou
Occasional Contributor III
An alternative way is to use geometryService.project to reproject the extent in non-Web Mercator to Web Mercator, and use the result to zoom the map to in the callback function.
StephenLead
Regular Contributor III
Thanks Jason, that looks a bit more robust as it removes the external dependencies.

If anyone from Esri is reading this, it would be nice if the map object could do this automatically - calling map.setExtent(non-web-mercator-extent) could conceivably handle this on-the-fly.
0 Kudos
JohnGrayson
Esri Regular Contributor
Stephen,
  also, you don't need to use esri.request, just listen for the layer 'onLoad' event and then the initialExtent is a property of the layer.  Using a geometry service to project the extent is the way to go.

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
    <title>Zoom to non-WM dynamic layer</title>
    <link rel="stylesheet" href="http://js.arcgis.com/3.6/js/esri/css/esri.css"/>
    <style>
      html, body, #mapDiv {
        padding: 0;
        margin: 0;
        height: 100%;
      }
    </style>
    <script src="http://js.arcgis.com/3.6/"></script>
    <script>
      dojo.require("esri.map");
      dojo.require("esri.tasks.geometry");
      var map;
      var url = "http://loczy.mfgi.hu/ArcGIS/rest/services/TJAM/surf_ujfalu_t/MapServer/"
      var gsUrl = "http://server.domain.com/arcgis/rest/services/Geometry/GeometryServer"; // url to your geometry service 

      function init(){

        map = new esri.Map("mapDiv",{basemap: "gray"});

        var dynamicMapServiceLayer = new esri.layers.ArcGISDynamicMapServiceLayer(url);
        dynamicMapServiceLayer.on('load',function(){

          var projectParams = new esri.tasks.ProjectParameters();
          projectParams.geometries = [dynamicMapServiceLayer.initialExtent];
          projectParams.outSR  = map.spatialReference;

          var gs = new esri.tasks.GeometryService(gsUrl);
          gs.project(projectParams).then(function(projectedGeometries){
            var initialExtentWM = projectedGeometries[0];
            map.setExtent(initialExtentWM);
          });

        });
        map.addLayer(dynamicMapServiceLayer);
      }

      dojo.ready(init);
    </script>
  </head>
  <body>
    <div id="mapDiv"></div>
  </body>
</html>
0 Kudos
JasonZou
Occasional Contributor III
It took me quite a while to figure out that the layer properties will be populated when it's loaded. In addition, the documentation should also mention what properties are read-only, and what are read-write. For constructors, which parameters are required, and which are optional. These are the key info for developers to use the api properly. I couldn't remember how many times I spent lots of effort trying to figure out these simple things which should be documented. ESRI does need to improve the documentation.
0 Kudos
JohnGravois
Frequent Contributor
jason,

in the API reference optional parameters for contructors and methods are typically denoted with a '?'

i can't think of an occasion when i attempted to set a property on an object and found that it was 'read-only'.  can you share an example?
0 Kudos
JasonZou
Occasional Contributor III
Here is one example: featureLayer.maxRecordCount. I thought I can set the value, but turned out it's a value carried over from the map service. Actually there is no way to control the record count at the feature layer level. I am not sure if this property should exist in the api if its value cannot be set and be honored.
0 Kudos
StephenLead
Regular Contributor III
you don't need to use esri.request, just listen for the layer 'onLoad' event and then the initialExtent is a property of the layer.


Hi John,

I'm actually using esri.request to retrieve additional properties of the service (this is just a cut-down version for repro purposes) - but that's a good tip.

Cheers,
Steve
0 Kudos