Thursday, January 22, 2009

Constraining Map Extent

Though sometimes an ArcGIS map service extends the full world, an application that uses this map service will want to restrict the extent to a way smaller extent. In this post, I will be talking about how to achieve this. Check out the application in action. Note that when you pan the map, it "springs" back to the initial extent. And even after you zoom in, the spring is still in effect. So how did I do this is as follows: I created a sub class of the Map class. In the constructor, I added a LOAD event listener. The LOAD event handler adds two EXTENT_CHANGE event listeners, where one of them is at a lower priority to guarantee that it will called last. This low priority listener sole purpose is to get and save the map extent as the initial extent. In addition, the initial scale is computed and saved too. Now, upon an extent change, we check if the initial extent intersects the new extent. if it does, then we compute the horizontal and vertical overlap distance and we adjust the extent in such a way that there is not overlap. we get the "spring" effect for free (cool, eh?) by invoking a callLater. The callLater handler sets a "bounded" extent, thus the spring effect. Now one last thing, we have to make sure we do not zoom out beyond the initial extent. we accomplish this by overriding the extent setter function, where we convert the given extent to a scale value and we compare that to the initial scale. If it is smaller, we proceed by calling the super function, otherwise we just return. Hope you find this post useful. And like usual you can download the source code from here.

18 comments:

Greg Knight said...

I think this app is broken... I cant get out of Africa! :)

thunderhead said...

No - lol - that is the point of the app - it constrains the extent of the map and the scale :-)

Don said...

Brilliant piece. Spent months looking for similar!! I am kind of stuck with its use though. I'm using 9.2 vS2008 SP6. and a newbie to programming as a whole.

In my Default.aspx.cs I have

protected void Page_PreRender(object sender, EventArgs ea)
{

if (!Page.IsPostBack)
{

ESRI.ArcGIS.ADF.Web.Geometry.Envelope initextent = new

// minx, miny, maxx, maxy
ESRI.ArcGIS.ADF.Web.Geometry.Envelope(-45.0, -45.0, 45.0, 45.0);

Map1.Extent = initextent;

}

}


Could you help or give me some pointers on how I can incorporate this master piece in my web App? I would very grateful for any help.

Cheers, Don

Don said...

Nice name this has given me... but really not an ArcGuru. Just a learner! :)

Don

Greg Knight said...

I was just joshin' ya bro! Nice work indeed!

DCAKen said...

Nice application...however, where is com.esri.ags.utils.ProjUtils coming from?

thunderhead said...

ProjUtils is a function with esri_internal modifier - should be in the current swc and should be implicitly imported. If not in the current release - we are about to release 1.2 next week.

Unknown said...

Nice sample. I am getting error in line if( extent.containsExtent( m_initialExtent ))

"Call to a possibly undefined method containsExtent through a reference with static type com.esri.ags.geometry:Extent."
I use 1.1 version and extent method doesnot have contiansExtent method. Is this V1.2 upgrade?

Unknown said...

Yes. Contains Extent method available in V1.2 . I have updated the my swc. My mxml contains Map, which has many AGSdynamic, and tiled service layer. I have placed my map control inside Sample. But it throws error


sample:SampleMap
ESRI:Map
ESRI:ArcGISTiledMapServiceLayer
ESRI:ArcGISDynamicMapServiceLayer
/ESRI:Map
/sample:SampleMap

Another error:

TypeError: Error #1034: Type Coercion failed: cannot convert com.esri.ags::Map@38ed0a1 to com.esri.ags.layers.Layer.

thunderhead said...

At what line this occurs in SampleMap ?

Abdul Mannan said...
This comment has been removed by the author.
Abdul Mannan said...

Hi,
This is really excellent Post, almost million dollar!

I successfully able to run it however while running in Sample Flex viewer, The first display screen goes blank and then when i click on full extent then it display the extent that I set inside sample.as. From then onwards, neither its zoom in nor any effect.
I dont know what am I doing wrong here!
One change I noticed that the street map you are using has initial extent -xmin -ymin, +Xmax +Ymax. But mine is -xmin +ymin, -Xmax +Ymax.
Please advice. your comment is highly appreciated. Thanks

Unknown said...

All,

I have modified this to work in flexviewer and the code and changes that need to be made to MapManager can be found here. http://forums.esri.com/Thread.asp?c=158&f=2421&t=295234&mc=0

Thanks for the great code Mansour.

thunderhead said...

U R Welcome

Jesse said...

I don't know if this is still being tracked, but tried to implement this with the new ArcGIS Online Mercator services, and I can't seem to zoom in or out... Is there something that I will need to change? I noticed that the units were set to Decimal Degrees in SampleMap.as, but I don't know what it would take to fix this.

Thanks
-Jesse

Pete said...

Mansour,

We implemented this piece of code and it's suited our client very well for two years. Thanks!

I'm writing though to bring up a problem that we've had with it recently and see if you have any ideas.

Our users call it the 'earthquake' symptom. The problem happens only when the map control is 're-sized' very narrow or very wide. The code, in this scenario, enters an endless loop between the extent change event and the extent property. Each trying to set the extent and the scale respectively and with the new shape of the window, neither will fit.

They symptom causes the map to 'quake' back and forth as it attempts to correct the scale\extent.

Right now I've coded a timer that can detect when this endless loop condition happens. When it does, the code just gives up and leaves the map extent wherever it may fall.

We thought maybe you'd have a better idea - one idea we had was to somehow detect the 'size changed' event of the map control and attempt to put the window size back to the initial size - is there a way to do this?

Consider that when you set the extent on a map control, that isn't necessarily the extent that it will be when finished. What the map control does is 'fit' the extent in the window - this leaves areas exposed either above and below (tall narrow window) or left and right (wide short window).

In this scenario, what I had originally tried to do (and failed) as to come up with a compromise between scale and extent so that no matter how narrow you make the window it would zoom in to compensate. But I'm not sure how to do that. In the extent change event you'd have to be sure that the extent you set would be acceptable to the conditional logic that will follow when the event responds to it's own reset.

Wow - that's probably very confusing - sorry.

If you have any ideas or would like to see a video of the problem let me know.

THANKS!

Pete Mahoney
GIS Inc.
pmahoney@gisinc.com

mg said...

Mansour, this code works pretty well in Flex Viewer 3.2, but as others have mentioned, zoom in/out has a strange effect on the map.

Whenever I try to zoom in/out, I get the aforementioned "earthquake effect." Is this something you were able to solve, or do you have any advice in constraining the extent in Flex Viewer?

Thanks so much.

Pete said...

I can't attach files to this blog but if you'd like the before and after limitMapExtent.as files, we did figure this out with a kind of counter that detected when earthquakes :) happen and then defensive code to stop the bad behavior (which is the endless loop to move the extent around)