javascript rubberband zoom box (ver 0.8)

Doug Williams williams at WEBSAWYER.COM
Thu Mar 9 11:17:00 EST 2006


<html>
<body>
<div>
This is a how-to for implementing a javascript zoombox.
This message is meant to be displayed as html in your email program.
Sometimes it works, sometimes it doesn't. If the formatting is no good, let me
know.  [cross my fingers and send...]
<p>
The code below is a simple PHP mapscript application, but the details outlined
here apply equally well to the other 

mapserver scripting methods.  If you have worked through the how-to <a 

href="http://mapserver.gis.umn.edu/docs/howto/phpmapscript-byexample">here</a>,
and have a working mapserver, then you 

should have no problem implementing this.  You will need to add the blue,
green, purple, and orange colored sections 

(orange optional).  The red section is replaced by the purple section.  The
lime section just needs your details.
<p>
This zoombox functionality 
<p>
You can cut and paste this code...just edit the mappath and mapfile variables
(lime colored)
<xmp>

<?php

 dl('php_mapscript.so');

// if you have not seen this function, you will find it on the UMN Mapserver
site somewhere
/************************************************************************/
/*function GMapPix2Geo($nPixPos, $dfPixMin, $dfPixMax, $dfGeoMin, dfGeoMax,*/
/*                           $nInversePix)                              */
/*                                                                      */
/*      Utility function to convert a pixel position to geocoded        */
/*      position.                                                       */
/*                                                                      */
/*       The parameter $nInversePix could be set to 1 for Y pixel       */
/*      coordinates where the UL > LR. Else set to 0.                   */
/************************************************************************/
function GMapPix2Geo($nPixPos, $dfPixMin, $dfPixMax, $dfGeoMin, $dfGeoMax,
                     $nInversePix)
{

    $dfWidthGeo = $dfGeoMax - $dfGeoMin;
    $dfWidthPix = $dfPixMax - $dfPixMin;


    $dfPixToGeo = $dfWidthGeo / $dfWidthPix;

    if (!$nInversePix)
        $dfDeltaPix = $nPixPos - $dfPixMin;
    else
        $dfDeltaPix = $dfPixMax - $nPixPos;

    $dfDeltaGeo = $dfDeltaPix * $dfPixToGeo;


    $dfPosGeo = $dfGeoMin + $dfDeltaGeo;

    return ($dfPosGeo);
}

/****************************************************************************/

/****************************************************************************/

// this function i made, it seems to keep my map fitting in my map window
reasonably

function corralit($map) {
   
   global $max_extent;

   if ( (abs($map->extent->maxx) + abs($map->extent->minx)) <=
(abs($max_extent->minx) + abs($max_extent->maxx)) ) {
      if ($map->extent->minx < $max_extent->minx) {
         $shift = $max_extent->minx - $map->extent->minx;
         $map->extent->minx = $map->extent->minx + $shift;
         $map->extent->maxx = $map->extent->maxx + $shift;
        
$map->setextent($map->extent->minx,$map->extent->miny,$map->extent->maxx,$map->extent->maxy);
      }
      if ($map->extent->maxx > $max_extent->maxx) {
         $shift = $map->extent->maxx - $max_extent->maxx;
         $map->extent->minx = $map->extent->minx - $shift;
         $map->extent->maxx = $map->extent->maxx - $shift;
        
$map->setextent($map->extent->minx,$map->extent->miny,$map->extent->maxx,$map->extent->maxy);
      }
   }else{
     
$map->setextent($max_extent->minx,$max_extent->miny,$max_extent->maxx,$max_extent->maxy);
   }

   if ( (abs($map->extent->maxy) + abs($map->extent->miny)) <=
(abs($max_extent->miny) + abs($max_extent->maxy)) ) {
      if ($map->extent->maxy > $max_extent->maxy) {
         $shift = $map->extent->maxy - $max_extent->miny;
         $map->extent->maxy = $map->extent->maxy - $shift;
         $map->extent->miny = $map->extent->miny - $shift;
        
$map->setextent($map->extent->minx,$map->extent->miny,$map->extent->maxx,$map->extent->maxy);
      }
      if ($map->extent->miny < $max_extent->miny) {
         $shift = $max_extent->miny - $map->extent->miny;
         $map->extent->maxy = $map->extent->maxy + $shift;
         $map->extent->miny = $map->extent->miny + $shift;
        
$map->setextent($map->extent->minx,$map->extent->miny,$map->extent->maxx,$map->extent->maxy);
      }
   }else{
     
$map->setextent($max_extent->minx,$max_extent->miny,$max_extent->maxx,$max_extent->maxy);
   }

}
/****************************************************************************/







// get the form variables
$zoom =  $_POST["zoom"];
$img_x =  $_POST["img_x"];
$img_y =  $_POST["img_y"];
$imgext =  $_POST["imgext"];
</xmp>
<xmp style="color:green">
$img_x1 =  $_POST["img_x1"];    // the _x1, _x2, _y1, _y2 variables hold zoom
box coordinates
$img_y1 =  $_POST["img_y1"];    // they are simply "hidden" form input variables
$img_x2 =  $_POST["img_x2"];    // that will be set by javascript on the browser
$img_y2 =  $_POST["img_y2"];    // I use img_x2 to determine if zoom box, as
browser
                                // does not send form variables that have
empty value
</xmp>
<xmp >
$gofull =  $_POST["go"];


</xmp>
<xmp style="color:lime">
 // just change this to point to the right things
 $map_path="/the/full/path/to/your/mapfile/";
 $map_file="yourmapfile.map";
</xmp>
<xmp>

 $map = ms_newMapObj($map_path.$map_file);


 $max_extent = ms_newrectObj();
 $max_extent->setextent($map->extent->minx,$map->extent->miny,$map->extent->maxx,$map->extent->maxy);



  if ($gofull) {
echo "<script>window.alert('full')</script>";

     // my map file has the map extent at full, so nothing needs to be done

</xmp>
<xmp style="color:green">

  } elseif ($img_x2) { // this is the code necessary to process the zoom box and
                      // set the map extent before drawing the new map
                      // if the browser did not send two x,y pairs, $img_x2
will be null
                      // because the browser will not have sent img_x2 without
a value
                      // you will see later where this is controlled

     // uncomment for debugging
     //echo "<script>window.alert('zoombox')</script>";
      
     $extent_to_set = explode(" ",$imgext);
     $nClickGeoX1 = GMapPix2Geo($img_x1, 0, $map->width,$extent_to_set[0],
$extent_to_set[2], 0);
     $nClickGeoY1 = GMapPix2Geo($img_y1, 0, $map->height, $extent_to_set[1],
$extent_to_set[3], 1);
     $nClickGeoX2 = GMapPix2Geo($img_x2, 0, $map->width, $extent_to_set[0],
$extent_to_set[2], 0);
     $nClickGeoY2 = GMapPix2Geo($img_y2, 0, $map->height, $extent_to_set[1],
$extent_to_set[3], 1);

     // put the georeferenced coordinates in proper order
     // it's not a given from the x,y coords!
     if (($nClickGeoX1 < $nClickGeoX2) && ($nClickGeoY2 < $nClickGeoY1)) {
        $map->setextent($nClickGeoX1,$nClickGeoY2,$nClickGeoX2,$nClickGeoY1);
     } elseif (($nClickGeoX1 < $nClickGeoX2) && ($nClickGeoY1 < $nClickGeoY2)) {
        $map->setextent($nClickGeoX1,$nClickGeoY1,$nClickGeoX2,$nClickGeoY2);
     } elseif (($nClickGeoX2 < $nClickGeoX1) && ($nClickGeoY1 < $nClickGeoY2)) {
        $map->setextent($nClickGeoX2,$nClickGeoY1,$nClickGeoX1,$nClickGeoY2);
     } elseif (($nClickGeoX2 < $nClickGeoX1) && ($nClickGeoY2 < $nClickGeoY1)) {
        $map->setextent($nClickGeoX2,$nClickGeoY2,$nClickGeoX1,$nClickGeoY1);
     }
</xmp>
<xmp>
     corralit($map); // this function will ensure the map fills the map window
                     // so you do not get part map, part outside the map 
                     // similar to setting the max extent in zoompoint!


 } elseif ( isset($img_x) ) {
     // uncomment for debugging
     echo "<script>window.alert('pointclick')</script>";

      $extent_to_set = explode(" ",$imgext);

      $map->setextent($extent_to_set[0],$extent_to_set[1],
                              $extent_to_set[2],$extent_to_set[3]);

      // uncomment for debugging
      //echo "<script>window.alert('pointclick map extent " .
$map->extent->minx . " - " . $map->extent->maxy . " - " . 

$map->extent->maxx . " - " . $map->extent->miny . "')</script>";

      $my_point = ms_newpointObj();
      $my_point->setXY($img_x,$img_y);

      $zoom_factor = $zoom;

     
$map->zoompoint($zoom_factor,$my_point,$map->width,$map->height,$map->extent,$max_extent);
      //corralit($map);  // having set max extent for zoompoint, it should not
need corralling

      // uncomment for debugging
      //echo "<script>window.alert('zoomed and corralled " .
$map->extent->minx . " - " . $map->extent->maxy . " - " . 

$map->extent->maxx . " - " . $map->extent->miny . "')</script>";

 } else {
   // initialize if need be - i.e. script was not called from itself
//echo "<script>window.alert('from outside')</script>";
   
 }



 $image=$map->draw();
 $image_url=$image->saveWebImage(MS_PNG,1,1,0);

 // here i grab the georef'd extent of the map sent to the page so it can be saved
 $extent_to_html = $map->extent->minx." ".$map->extent->miny." "
              .$map->extent->maxx." ".$map->extent->maxy;



///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// what is returned to the user starts here
//

?>

<html>
  <head>
    <title>Map Magic</title>
  </head>

  <body>
    <form name=mapserv method=post action="<? echo $_SERVER['PHP_SELF']; ?>" >
    <!-- i pass the point click location back in, i find it useful ... it
could be the center of the map -->
    <input type="hidden" name="img_x" value="<? echo $img_x; ?>">
    <input type="hidden" name="img_y" value="<? echo $img_y; ?>">
</xmp>
<xmp style="color:green">
    <!-- these are the form variables to hold the zoom box coordinates -->
    <!-- img_x1,img_y1 are redundant to img_x, img_y ... i have done this for
clarity -->
    <input type="hidden" name="img_x1" value="<? echo $img_x1; ?>"> <!-- just
because i did above -->
    <input type="hidden" name="img_y1" value="<? echo $img_y1; ?>"> <!--
though not necessary to set them -->
    <input type="hidden" name="img_x2" value="">
    <input type="hidden" name="img_y2" value="">
</xmp>
<xmp>
    <!-- here is the extent of the map being saved -->
    <input type="hidden" name="imgext" value="<?php echo $extent_to_html?>">

    <1-- here are some standard zoom in/out/pan functionality controls
    <a href="?go=full">full</a>&nbsp;
    <span onclick="document.mapserv.zoom.value=2">+</span>&nbsp;
    <span onclick="document.mapserv.zoom.value=-2">-</span>&nbsp;
    <span onclick="document.mapserv.zoom.value=1">pan</span>&nbsp;
    <input type="text" name="zoom" value="1">

    <center>
    <div>

</xmp>
<xmp style="color:orange">
    <!-- these elements are for holding values that will be written to them
later by javascript -->
    <!-- i have them here to see what is going on, but they could be useful,
for instance -->
    <!-- a javascript function could be added to convert x,y to lat-lon, so
real lat-lon was displayed -->
    <span style="font-size:7pt;" id="latspan" name="latspan" ></span>-<span
style="font-size:7pt;" id=lonspan 

name=lonspan></span>
    <span style="font-size:7pt;" id=x1 name=x1></span>-<span
style="font-size:7pt;" id=y1 name=y1></span>: 
    <span style="font-size:7pt;" id=x2 name=x2></span>-<span
style="font-size:7pt;" id=y2 name=y2></span> 

</xmp>
<xmp>

    </div>
    </center>

</xmp>
<xmp style="color:red">

<? /*    <input type="image" name="img" src="<?php echo $image_url?>"
border="0" height=300 width=600> 
         <!-- the input type image lacks the ability to do what we want -->*/ ?>

</xmp>
<xmp style="color:purple">
    <!-- we add some javascript functions to act on the mouse events of a div -->
    <!-- to do what input type image does, and more -->
    <!-- the absolutely positioned div inside the relatively positioned div
creates  -->
    <!-- x,y coordinate system (0,0 topleftcorner - map->width,map->height
lowerrightcorner) -->
    <!-- it is important that border and border-style do not exist -->
    <!-- the nested div is where the zoom box is created, through html
trickery -->
    <div style="position:relative;height:<? echo $map->height; ?>px;width:<?
echo $map->width; 

?>;border-style:none;background-image:url(<?php echo
$image_url?>);border:0px;" onmousemove="getMouseXY(event);" 

onmousedown="setMouseXY(event,'x1','y1');"
onmouseup="setMouseXY(event,'x2','y2');document.mapserv.submit();">
     <div id=drawdiv name=drawdiv  
style="position:absolute;top:0px;left:0px;height:<? echo $map->height;
?>px;width:<? 

echo $map->width; ?>px;border-style:none;border:0px;">
     </div>
    </div> 
</xmp>
<xmp>


    </form>

  </body>

</xmp>
<xmp style="color:blue">
<!-- from here down are the scripts to deal with the mouse events and to draw
the zoom box -->
<!-- drawing the zoom box is an html trick ... four squashed, red-bordered
div's (two vertically -->
<!-- and two horizontally) are created and destroyed on the fly to create the
resizing box -->

  <script language=javascript>

function DrawLinHor( x, y, size, color, div2){  // the lines are made by
drawing a div with a colored border
  var str;                                      // a horizontal line is a div
with height of one(1) pixel
  if (x>=0 && y>=0 && size>0){
    str = '<div style="position:absolute; left:' + x + 'px; top:' + y +
'px;width:' + size + 'px; 

height:1px;background-color:' + color + '"><table height=1 width=' + size +
'></table></div>\n';
  } else {
    str = '';
  }
//  document.write(str); // i have not considered old browser versions as yet
  document.getElementById(div2).innerHTML += str;  // this works with
"getElementById" browsers,
                                                   // but could easily be
modified to deal with
                                                   // those browsers that
don't support that object
}

function DrawLinVert( x, y, size, color, div2){  // the lines are made by
drawing a div with a colored border
  var str;                                       // a horizontal line is a div
with width of one(1) pixel
  if (x>=0 && y>=0 && size>0){
    str = '<div style="position:absolute; left:' + x + 'px; top:' + y +
'px;width:1px; height:' + size + 

'px;background-color:' + color + '"><table height=' + size + '
width=1></table></div>\n';
  } else {
  str = '';
  }
//  document.write(str);  // this is the old way of writing ... Note this code
does not accomodate old browsers
  document.getElementById(div2).innerHTML += str;
}

function DrawLine( x1, y1, x2, y2, color, div2 ){
  deltax=x2-x1;
  deltay=y2-y1;

  if (deltax>0){

    dstep=deltax;
    x=x1;
    y=y1;
    DrawLinHor( (x),(y),dstep,color,div2 );

  } else {

    dstep=deltay;
    y=y1;
    x=x1;
    DrawLinVert( (x),(y),dstep,color,div2 );
  }
}


// these drawing functions have been reduced from some code I found 
// (see my first posting about javascript rubberband zoom box to find that
reference)


/////////////////////////////////////////////////////////////////////////////////////////////
// these are some utility varibles
var mouseIsDown = false
var tmpX1
var tmpY1
var tmpX2
var tmpY2


// this code is modified from code I found to deal with getting screen x,y values
// (see my first posting about javascript rubberband zoom box to find that
reference)

var IE = document.all?true:false  // this is not really the right way to do
this check ...
// If NS -- that is, !IE -- then set up for mouse capture
if (!IE) window.captureEvents(Event.MOUSEMOVE || Event.MOUSEUP || Event.MOUSEDOWN)

function getMouseXY(e) {

   var tempX = 0
   var tempY = 0

   if (IE) { // grab the x-y pos.s if browser is IE
      tempX = event.offsetX
      tempY = event.offsetY
   } else {  // grab the x-y pos.s if browser is NS
      tempX = e.layerX
      tempY = e.layerY
<?
if(!(eregi("Firefox",$_SERVER['HTTP_USER_AGENT']) == false)){ 
?>                                                           
      tempX = e.layerX-1  // firefox gives pixel position 1 to (height (or width))
      tempY = e.layerY-1  // NS and IE give 0 to (height(or width)-1)
<?
}
?>
   }

   if (!((tempX == 0) || (tempY == 0))) {  // this is necessary because
sometimes the values
                                           // that end up in tempX or tempY
are relative to 
                                           // something other than the upper
left corner of the 
                                           // div, causing severe headaches
                                           // not sure why these values are
not always relative
                                           // to the upper left corner - can
anyone please tell me?

      // show the position values in the form named Show
      // in the text fields named MouseX and MouseY
</xmp>
<xmp style="color:orange">
      document.getElementById('lonspan').innerHTML = tempX
      document.getElementById('latspan').innerHTML = tempY
</xmp>
<xmp style="color:blue">

      if (mouseIsDown) {
         document.mapserv.img_x2.value = tempX
         document.mapserv.img_y2.value = tempY
</xmp>
<xmp style="color:orange">
         document.getElementById('x2').innerHTML = tempX
         document.getElementById('y2').innerHTML = tempY
</xmp>
<xmp style="color:blue">

         // this erases the box if one is drawn
         document.getElementById('drawdiv').innerHTML = '';

         if (document.mapserv.img_x1.value > tempX) {
            tmpX1 = tempX
            tmpX2 = document.mapserv.img_x1.value
         }else{
            tmpX1 = document.mapserv.img_x1.value
            tmpX2 = tempX
         }

         if (document.mapserv.img_y1.value > tempY) {
            tmpY1 = tempY
            tmpY2 = document.mapserv.img_y1.value
         }else{
            tmpY1 = document.mapserv.img_y1.value
            tmpY2 = tempY
         }

         // four lines are drawn to create the box
         if ((tmpY1 > 0) && (tmpX1 > 0) && (tmpY2 > 0) && (tmpX2 > 0)) {
            DrawLine(tmpX1,tmpY1,tmpX1,tmpY2,"#FF0000","drawdiv");
            DrawLine(tmpX1,tmpY1,tmpX2,tmpY1,"#FF0000","drawdiv");
            DrawLine(tmpX2,tmpY1,tmpX2,tmpY2,"#FF0000","drawdiv");
            DrawLine(tmpX1,tmpY2,tmpX2,tmpY2,"#FF0000","drawdiv");
         }
      }
   }

   return true

}


function setMouseXY(e,x,y) {
  var tempX
  var tempY

  if (x == 'x2') mouseIsDown = false
  if (IE) { // grab the x-y pos.s if browser is IE
    tempX = event.offsetX
    tempY = event.offsetY
  } else {  // grab the x-y pos.s if browser is NS
      tempX = e.layerX
      tempY = e.layerY
<?
// firefox seems to set relative offset values from 1 to height or 1 to width
// netscape and ie seem to set relative offset values from 0 to height-1 or 0
to width-1
// this browser inconsistency is the biggest problem,
// although one pixel may not be significant - someone more knowledgable would
have to say
// anyway this corrects for the descrepancy described
// i've checked this against few browsers
if(!eregi("Firefox",$_SERVER['HTTP_USER_AGENT'])){
?>
      tempX = e.layerX-1
      tempY = e.layerY-1
<?
}
?>
  }  

  if (y == "y1") {  // this means mousedown!!
</xmp>
<xmp style="color:orange">
    document.getElementById(x).innerHTML = tempX
    document.getElementById(y).innerHTML = tempY
</xmp>
<xmp style="color:blue">
    document.mapserv.img_x.value = tempX
    document.mapserv.img_y.value = tempY
    document.mapserv.img_x1.value = tempX
    document.mapserv.img_y1.value = tempY
    document.mapserv.img_x2.value = tempX
    document.mapserv.img_y2.value = tempY
  }
  if (y == "y2") {  // this means mouseup!!
    // this clause determines if a zoom box has been created, or if the
    // mouse action should treated as a simple click - the fudge value
    // allows the user to mousemove inadvertently a little and still get a
pointclick
    var fudge = 4
    if ((Math.abs(document.mapserv.img_x2.value -
document.mapserv.img_x1.value) <= fudge) && 

(Math.abs(document.mapserv.img_y2.value - document.mapserv.img_y1.value) <=
fudge)) {
      document.mapserv.img_x2.value = ''
      document.mapserv.img_y2.value = ''
    }else{
      if (document.mapserv.img_x2.value < document.mapserv.img_x1.value) {
        tempX = document.mapserv.img_x1.value
        document.mapserv.img_x1.value = document.mapserv.img_x2.value
        document.mapserv.img_x2.value = tempX
      }
      if (document.mapserv.img_y2.value < document.mapserv.img_y1.value) {
        tempY = document.mapserv.img_y1.value
        document.mapserv.img_y1.value = document.mapserv.img_y2.value
        document.mapserv.img_y2.value = tempY
      }
    }
  }
  if (x == 'x1') {  // this means mousedown!!
    mouseIsDown = true
  }
  return true
}

function resetZoomBox () {

   mouseIsDown = false;
   document.getElementById('drawdiv').innerHTML = '';

}

if (IE) {
  if (window.Event) document.captureEvents(Event.MOUSEUP);
  document.onmouseup = resetZoomBox;
} else {
  window.captureEvents(Event.MOUSEUP);
  window.onmouseup = resetZoomBox;
}
  </script>
</xmp>

<xmp>

</html>

</xmp>
</div>
</body>
</html>



More information about the mapserver-users mailing list