Fit the complete infoTemplate contents on your map

4735
5
01-26-2015 12:10 PM
TracySchloss
Frequent Contributor

This is not a new problem, but I have yet to come up with a very satisfactory solution.  How do you keep your infoTemplate from getting cut off?  My content isn't very complicated.  I have used a map.infoWindow.resize to make it a little wider. I have a function to format the contents. 

map.infoWindow.resize(340,300);

I have a listener on the show event of the map.infoWindow.   The trick apparently isn't to move your infoTemplate, but instead to move your map if it detects the window is going to get cut off,  panning the map enough so the tag isn't cropped.

on(map.infoWindow, "show", adjustMapExtent);

I got this from an older thread and it seems to be working.

  function adjustMapExtent(){
      var maxPoint = new Point(map.extent.xmax, map.extent.ymax, spatialReference);  
      var centerPoint = new Point(map.extent.getCenter());  
      var mapPt = new Point(map.infoWindow.location, spatialReference);
      screenPt = map.toScreen(mapPt);
      //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 - screenPt.x) - 350;  
      var yDiff = Math.abs(maxPointScreen.y - screenPt.y) - 310;  


      //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, spatialReference);  
      map.centerAt(centerPoint);
}

Here's my problem/complaint.  This works the first time.  However, unless you've dismissed the tag, the 'show' listener doesn't get fired again, because there is still an infoWindow displayed.  The event doesn't fire for every new infoWindow, just 'when an infoWindow in visible'.  This is an important distinction.

Is there some other event I could be listening for?  I tried adding an event listener on the map click to do a 'map.infoWindow.hide()', but that seems to prevent the info tag from ever displaying.

Tags (1)
0 Kudos
5 Replies
JoelBennett
MVP Regular Contributor

I've had similar problems, and the solution I've come up with is to revise the _setPosition method of the Popup "class".  It ensures the popup always opens "towards" the center of the map (i.e. where it will have the most space).  The full popup is guaranteed to be visible if (1) the popup width inlcuding the pointer is less than half the width of the map and (2) the popup height inlcuding the pointer is less than half the height of the map.

The trick is to execute the code below before any popups are instantiated.  However, if the only popup in the application is the map's, you could replace

Popup.prototype._setPosition = function(screenPoint) {

with

map.infoWindow._setPosition = function(screenPoint) {

(assuming "map" is a reference to your map object).

As I'm preparing this post, the code below looks kind of garbled, and not nicely formatted like in the source file, so hopefully it comes out ok.  If not, I've also attached the text file containing it.

require(["dojo/dom-geometry", "dojo/dom-style", "esri/kernel", "esri/dijit/Popup"], function(domGeom, domStyle, esriNS, Popup) {
 /** modified to open popup with best possible placement (towards center of screen) **/
 Popup.prototype._setPosition = function(screenPoint) {
  domStyle.set(this.domNode, {
   left: screenPoint.x + "px",
   top: screenPoint.y + "px",
   right: null,
   bottom: null
  });
  var mapBox = domGeom.position(this.map.container, true);
  var horizontalPosition = "";
  var verticalPosition = "";
  if (screenPoint.x < (mapBox.w * 0.33))
   horizontalPosition = "Left";
  else if (screenPoint.x > (mapBox.w * 0.67))
   horizontalPosition = "Right";
  if (screenPoint.y < (mapBox.h * 0.33))
   verticalPosition = "top";
  else if (screenPoint.y > (mapBox.h * 0.67))
   verticalPosition = "bottom";
  else if (horizontalPosition == "") {
   horizontalPosition = ((screenPoint.x < (mapBox.w * 0.5)) ? "Left" : "Right");
   /*verticalPosition = ((screenPoint.y < (mapBox.h * 0.5)) ? "top" : "bottom");*/
  }
  var anchorText = verticalPosition + horizontalPosition;
  var offsetX = ((this.offsetX) ? this.offsetX : 0);
  var offsetY = ((this.offsetY) ? this.offsetY : 0);
  var positionerHeight = 0;
  var positionerWidth = 0;
  switch (anchorText) {
   case "top":
   case "bottom":
    positionerHeight = 14;
    break;
   case "Left":
   case "Right":
    positionerWidth = 13;
    break;
   default:
    var esriVersion = esriNS.version.toString().split(".");
    esriVersion[0] = parseInt(esriVersion[0], 10);
    esriVersion[1] = parseInt(esriVersion[1], 10);
    if ((esriVersion[0] > 3) || ((esriVersion[0] == 3) && (esriVersion[1] >= 8))) {
     positionerHeight = 14;
     positionerWidth = -16;
    } else
     positionerHeight = 45;
    break;
  }
  var positionerBox = domGeom.getContentBox(this._positioner);
  var positionerStyle = {
   left: null,
   right: null,
   top: null,
   bottom: null
  };
  if (horizontalPosition == "")
   positionerStyle.left = (positionerBox.w * -0.5) + "px";
  else
   positionerStyle[horizontalPosition.toLowerCase()] = (positionerWidth + offsetX) + "px";
  if (verticalPosition == "")
   positionerStyle.top = (positionerBox.h * -0.5) + "px";
  else
   positionerStyle[verticalPosition] = (positionerHeight + offsetY) + "px";
  domStyle.set(this._positioner, positionerStyle);
  this._showPointer(anchorText);
 };
});
SteveCole
Frequent Contributor

I don't want to hijack Tracy's thread but this has frustrated me as well so I was watching this thread. I want/need to implement this into a legacy 3.3 API project but it's choking on my dojo.require. I tried "esri.kernel" instead of "esri/kernel" but it can't find it.

Any guesses on how to reference this under legacy coding?

0 Kudos
KenBuja
MVP Esteemed Contributor

"esri/kernel" was introduced at version 3.8. You might want to try what's mentioned in the help of using "esri.version".

When coding legacy (non-AMD) style, there is no need to require the module. All methods and properties are available in the namespace. For example, esri.version.

SteveCole
Frequent Contributor

Cool- thanks, Ken! I've converted Joel's snippit "back" to legacy if anyone also needs it.

The workaround does seem to work- not perfectly, but much better than without it!

0 Kudos
TracySchloss
Frequent Contributor

I get what you're saying, but I'm not any good with separating things into modules OR how to create my own variation of what ESRI has written.  All the examples I've found to get me started makes some assumptions about what background and programming experience you come from (which I don't have).  I get lost right away with how to get everything talking to each other.

0 Kudos