Auto-panning map so that infowindow is fully visible

13908
14
07-15-2011 12:17 PM
LisaEidson
New Contributor III
I'm looking for some guidance on how to make my map pan when an infowindow is launched (result of an identify) if the infowindow is not completely within view.

Take this example:

Here's my map prototype: satellite Bing layer with wilderness boundaries ArcGIS dynamic service that my colleague maintains overlayed.


When you click within the Absaroka-Beartooth Wilderness boundary you get the infowindow with the results of an identify (grabs and ID number then outputs data from an array). Although I've used tabs to ensure that the infowindow can contain a fair amount of information but isn't too large, depending on where you click it will often be partially obscured (with the rest being outside the current map view).


If the infowindow isn't fully shown, I want the map to pan so that it is. So in this example, I want the map to pan to the below view.


This seems like a pretty useful operation (Google Maps api, for example, automatically takes care of this) but I haven't been able to find any examples or anything about how to do it. So any assistance would be much appreciated. Thanks in advance.

The code for the prototype map from which I took the above screen snaps is available on my website at: http://www.wilderness.net/mapTest_esri3_strip.htm
14 Replies
StephenLead
Regular Contributor III
Your code contains:

map.infoWindow.resize(415,230);
map.infoWindow.show(evt.screenPoint, esri.dijit.InfoWindow.ANCHOR_UPPERRIGHT);


so it looks like you always want to show the infoWindow to the upper-right of the evt.screenPoint (the location the user clicks) and have it appear at this fixed size.

You can use this logic to calculate whether the infoWindow will fit at a given point:

function executeIdentifyTask(evt) {
map.graphics.clear();

//Set the infoWindow to open at the top right of the point at all times
map.infoWindow.setFixedAnchor(esri.dijit.InfoWindow.ANCHOR_UPPERRIGHT);

//Determine the upper right, and center, coordinates of the map
var maxPoint = new esri.geometry.Point(map.extent.xmax, map.extent.ymax)
var centerPoint = new esri.geometry.Point(map.extent.getCenter());

//Convert these to screen coordinates
var maxPointScreen = map.toScreen(maxPoint);
var centerPointScreen = map.toScreen(centerPoint);

//Subtract the size of the infoWindow, including a buffer.
//This will show whether the infoWindow would spill out of the current view.
var xDiff = Math.abs(maxPointScreen.x - evt.screenPoint.x) - 435;
var yDiff = Math.abs(maxPointScreen.y - evt.screenPoint.y) - 285;

//If required, recalculate a new centerpoint which accounts for the infoWindow
if (xDiff < 0) {centerPointScreen.x -= xDiff;}
if (yDiff < 0) {centerPointScreen.y += yDiff;}

//Pan the map to the new centerpoint (in Map coordinates)
centerPoint = map.toMap(centerPointScreen);
map.centerAt(centerPoint);

//Display the infoWindow at the original point clicked
map.infoWindow.show(evt.screenPoint, esri.dijit.InfoWindow.ANCHOR_UPPERRIGHT);


Steve
LisaEidson
New Contributor III
A few adjustments to the buffer amounts and this works flawlessly! Just what I wanted. Thanks much!
0 Kudos
derekswingley1
Frequent Contributor
Steve's approach works, but why not use map.getInfoWindowAnchor? It's demonstrated one of the queryTask samples.
0 Kudos
StephenLead
Regular Contributor III
why not use map.getInfoWindowAnchor?


Wouldn't that give the same coordinate as evt.screenPoint, though? And I'm not sure how it helps to determine whether the map needs to pan.

Cheers,
Steve
0 Kudos
derekswingley1
Frequent Contributor
Instead of panning the map, the map opens the info window so that it is completely within the map. This isn't exactly what OP asked for but figured I'd put it out there anyway...
0 Kudos
StephenLead
Regular Contributor III
Yeah I personally don't like the way that Google Maps can sometimes pan unpredictably to accommodate the infoWindow, but sometimes it makes sense.

Given that the infoWindow (apparently?) has four possible angles from the anchor point to the actual window, sometimes it isn't possible to show it completely within the current view without panning.

I guess another option could be to do away with the anchor point and arrow, and just position it somewhere constant (eg a sidebar)
0 Kudos
derekswingley1
Frequent Contributor
Personally, I'd rather see the info window accomodate the map rather than the map moving around. But maybe that's just because I've worked with this API so much and I'm just used to that behavior. I also like the sidebar approach.

The info window has four possible orientations: upper left, upper right, lower left and lower right. To figure out where to show it, the map is divided into four quadrants. If the anchor point for the info window is in the upper left, display the info window pointing to the lower right. If the anchor point is in the lower left, display the info window pointing to the upper right. Basically just show it so that it displays opposite the quadrant that was clicked so that the entire info window is visible.
0 Kudos
JeffSmith4
New Contributor
Your code contains:

map.infoWindow.resize(415,230);
map.infoWindow.show(evt.screenPoint, esri.dijit.InfoWindow.ANCHOR_UPPERRIGHT);


so it looks like you always want to show the infoWindow to the upper-right of the evt.screenPoint (the location the user clicks) and have it appear at this fixed size.

You can use this logic to calculate whether the infoWindow will fit at a given point:

function executeIdentifyTask(evt) {
map.graphics.clear();

//Set the infoWindow to open at the top right of the point at all times
map.infoWindow.setFixedAnchor(esri.dijit.InfoWindow.ANCHOR_UPPERRIGHT);

//Determine the upper right, and center, coordinates of the map
var maxPoint = new esri.geometry.Point(map.extent.xmax, map.extent.ymax)
var centerPoint = new esri.geometry.Point(map.extent.getCenter());

//Convert these to screen coordinates
var maxPointScreen = map.toScreen(maxPoint);
var centerPointScreen = map.toScreen(centerPoint);

//Subtract the size of the infoWindow, including a buffer.
//This will show whether the infoWindow would spill out of the current view.
var xDiff = Math.abs(maxPointScreen.x - evt.screenPoint.x) - 435;
var yDiff = Math.abs(maxPointScreen.y - evt.screenPoint.y) - 285;

//If required, recalculate a new centerpoint which accounts for the infoWindow
if (xDiff < 0) {centerPointScreen.x -= xDiff;}
if (yDiff < 0) {centerPointScreen.y += yDiff;}

//Pan the map to the new centerpoint (in Map coordinates)
centerPoint = map.toMap(centerPointScreen);
map.centerAt(centerPoint);

//Display the infoWindow at the original point clicked
map.infoWindow.show(evt.screenPoint, esri.dijit.InfoWindow.ANCHOR_UPPERRIGHT);


Steve


Just wanted to say thanks to Steve for this code, which works perfectly and saved me a ton of time.  +100 Internets for you!  

I'm migrating my application from the Google Maps extension for the JSAPI to v2.7 of the ESRI Javascript API (no google maps).  My infowindows contain a large amount of information and were previously designed with the assumption that the anchor would be fixed and the map would automatically pan so that the whole infowindow would be visible.  This is the default behavior of an infowindow in the google API.  When I migrated the app, I was finding that in certain situations, the infowindow would display partially outside the map bounds and the user would have to manually pan to see the whole thing. 

I was getting hung up on trying to programatically figure out the top corner of the infowindow and then do some math to figure out if it falls outside the map bounds, but Steve's solution avoids that whole issue by using the constants.  Very nice!  Just tweak the constants for your particular situation.
0 Kudos
JayFowler
Esri Contributor
Thanks Steve! That worked great and was easy to implement.
0 Kudos