Initial Extent Issues

3113
4
08-10-2012 10:32 AM
NickHogan
New Contributor
I have notice two issues with setting the initial extent for a map.

1. When the map loads the initial extent values are being changed.

2. When the map begins to load it looks like the extent is set correctly, but then it zooms out one more level. If I then call map.setExtent with the initial extent, it goes to the correct extent.

I have attached some code that demonstrates the problem.
0 Kudos
4 Replies
__Rich_
Occasional Contributor III
I have notice two issues with setting the initial extent for a map.

1. When the map loads the initial extent values are being changed.

2. When the map begins to load it looks like the extent is set correctly, but then it zooms out one more level. If I then call map.setExtent with the initial extent, it goes to the correct extent.

I have attached some code that demonstrates the problem.

1.  I posted a question ages ago about the setExtent method mutating the extent argument, I wonder if the same thing happens with the extent passed to the map constructor?  (no idea if this mutation was ever addressed, I've just left my workaround in place)

2.  I think the 'extra zoom level' is because you're passing fitExtent:true to the map constructor, that setting guarantees your chosen extent will be completely in view and because of aspect ratios the only way for the map to achieve that is to zoom out.  Try changing the aspect ratio of the extent to match (exactly, or as near as you can) the aspect ratio of the map display itself, or, more simply, pass fitExtent:false.  Of course if you do the latter be prepared for some portion of your initial extent not being visible.

HTH

edit: yep, the initial extent is mutated by the time map.onLoad is raised so if you want to 'remember' your initial extent, pass a clone every time you want to use it including when you pass it to the constructor.

I see you're referencing v2.8 of the API, perhaps someone from ESRI can comment on whether the behaviour is the same in all more recent versions?
0 Kudos
__Rich_
Occasional Contributor III
Did this help, Nick?
0 Kudos
JoelBennett
MVP Regular Contributor

I've run into this as well...our map should default to one of several sites, but no amount of fooling with the fitExtent and extent parameters (including shrinking the default extent) produces a sure fit for all of them.  More often, the map starts up one level too far in for some, or one level too far out for others.  And there's also cases where the map should start up where the user previously left off, and even with the same screen size as before, the map still often starts at the wrong zoom level.  It's been giving me fits for awhile, but I've gotten to the bottom of it.

The reason this occurs is found in the _coremap module.  In the _addLayerHandler function, observe what happens when the map's graphics container is added (when the map loads):

if (a === this.graphics) {
  ...
  this._firstLayerId = null;
  ...
  d = this._fixExtent(b, i.fitExtent);
  this.extent = d.extent; //THIS LINE CAUSES THE PROBLEM
  this.__LOD = d.lod;
  this.__setExtent(this.extent, null, null, i.fitExtent);
  ...
}

Notice the call to __setExtent.  One of the first things that happens in __setExtent is another call to _fixExtent - with the already-fixed extent.  This extra call can result in an expanded extent that causes the map to start up one zoom level out from where it should.

The simplest fix is to rewrite the problem line to:

  this.extent = this.extent || d.extent;

If you have a local copy of the API, you can implement this fix on your own, but that can be tricky.  You'd have to update init.js, and finding that particular line can be difficult, especially since variable names can be different.

There are workarounds though.  I've written a function called setDefaultExtentOptions that adjusts your default extent to match the actual extent your map will show, and appends appropriate values to the options parameter (the second parameter) that you pass into the map's constructor.

The function and workflow would go like:

function setDefaultExtentOptions(defaultExtent, mapContainerID, options) {
 var contentBox = domGeom.getContentBox(document.getElementById(mapContainerID));
 var extentHeight = defaultExtent.getHeight();
 var extentWidth = defaultExtent.getWidth();
 var viewHeight, viewWidth;
 var lods = options.lods;
 var dY, dX;
 var center;
 var lod;

 options.spatialReference = defaultExtent.spatialReference;
 options.extent = defaultExtent;
 options.fitExtent = true;

 for (var x = lods.length - 1; x >= 0; x--) {
  lod = lods;

  viewHeight = lod.resolution * contentBox.h;
  viewWidth = lod.resolution * contentBox.w;

  if ((viewHeight > extentHeight) && (viewWidth > extentWidth)) {
   center = defaultExtent.getCenter();

   dY = viewHeight * 0.5;
   dX = viewWidth * 0.5;

   options.extent = new Extent(center.x - dX, center.y - dY, center.x + dX, center.y + dY, defaultExtent.spatialReference);
   options.fitExtent = false;
   options.scale = lod.scale;
   options.zoom = lod.level;
   options.center = center;

   return;
  }
 }
}

var defaultExtent = new Extent(minX, minY, maxX, maxY, new SpatialReference(wkid));
var divId = "map"; //set according to your div's ID.
var options = {
 //set map options here...MUST INLCUDE VALUE FOR lods
};

setDefaultExtentOptions(defaultExtent, divId, options);

var map = new Map(divId, options);

I implemented this workaround before actually figuring the problem out.  There are probably better workarounds, but optimally, a workaround wouldn't be necessary.

I see the question was initially raised for 2.8, but it's still happening in 3.13, which I'm using.

0 Kudos
JoelBennett
MVP Regular Contributor

This was fixed in 3.15, eliminating the need for a workaround.  In their "What's new in Version 3.15" document, notice the entry:

BUG-000088231: Fixed an issue when using map.setExtent's fit option did not work as expected.

Although the link below will probably only work as long as 3.15 is the current version, see also:

What's new in Version 3.15 | Guide | ArcGIS API for JavaScript

0 Kudos