//*****************************************************************************
//
// USGS Scale Tool 
// Created By:  Chris Rusanowski
// Installation:
//   -Copy USGS_Scale_Tool.js into the javascript directory of the HTML Viewer
//   -Include the javascript/USGS_Scale_Tool.js in MapFrame.htm with the code below
//    <script type="text/javascript" LANGUAGE="JavaScript" SRC="javascript/USGS_Scale_Tool.js"></script>
//   -Write out the function DrawScaleTool() where you want the scale tool to appear in the toc. IE: with the code below:
//    if (parent.MapFrame.UseScaleTool) { document.writeln(parent.MapFrame.DrawScaleTool()); }
//
//*****************************************************************************
//UseScaleTool is used to check if we should display the scalebar tool or not.  
var UseScaleTool=true;

// ScaleToolDPI is the dots per inch (pixels per inch) of the image
// 96 is the value specified in the ArcXML users guide as a default.  97.6923076923077 is the value calculated by Dave Bollinger in the ArcIMS Users Forum
var ScaleToolDPI=97.6923076923077;
// GRS80 spheroid radius from John Snyders Map Projections manual. The GRS80 Equatorial radius is 6,378,137 meters which converts to 251,107,755.9 inches
var RadiusEarth = 251107755.9;

//Variable Definitions for determining scale  (From Markus Bedel code on ArcIMS User Forum.)
var dPixelsPerMiles = 63360 * ScaleToolDPI;
var dPixelsPerFeet = 12.0 * ScaleToolDPI;
var dPixelsPerKilometers = 39370.07874016 * ScaleToolDPI;
var dPixelsPerMeters = 39.37007874 * ScaleToolDPI;
var dPixelsPerCentimeters = 0.39370079 * ScaleToolDPI;

//Show_Scale_Title determines if the Scale Title is displayed when drawing the TOC
var Show_Scale_Title = true;

//*****************************************************************************
// Function HaversineScale
// This function will calculate the Haversince scale in inches for a given extent and image
// The 4 coordinates are the two end points of a line.  The imageWidthInPixels is the length of that line.
// If you use the extent of the image, then the imageWidthInPixels would be the diagonal across the the image.
// The horizonttal scale across the center of the image would be calculated by giving the left and right coordinates
// and specifying both the top and bottom as the center coordinate.  Flip-flop this to do a vertical scale across the center.
// Haversine Equation  from http://mathforum.org/library/drmath/view/51879.html
// which draws from R. W. Sinnott, "Virtues of the Haversine,"  Sky and Telescope, vol. 68, no. 2, 1984, p. 159
// Code for doing scale when not in Decimal Degrees is an adaptation of code Markus Bedel posted on the ArcIMS User Forum.
// Code for other map units always uses width and deltaLong, so to find vertical scale you need to enter the 
// Top and Bottom as Right and Left and image Height at the imageWidth.  (Don't do this for decimal degrees...)
// Map Unit scales other than Decimal Degrees were not tested for accuracy.
//*****************************************************************************
function HaversineScale(degreesLeft, degreesBottom, degreesRight, degreesTop, imageWidthInPixels, DisplayDPI) {
  // Start with the Default DPI
  var MonitorDPI = ScaleToolDPI;
  // Change the DPI if one is passed in (IE: if you're using a different DPI in your XML requests...)
  if (!isNaN(DisplayDPI)) {
    MonitorDPI = DisplayDPI;
  }
  // Find the difference in Map units
  var deltaLat =  Math.abs(degreesTop - degreesBottom);
  var deltaLong = Math.abs(degreesRight - degreesLeft);
  // Default the Scale to 0.0
  var ApproximateScale = 0.0;
  // Calculate the scale based upon the Map Units  All except decimal degees use default values from variable above...
  if ( MapUnits.toUpperCase() == "DEGREES" ) {
    // Decimal Degrees, so change the difference in map units from degrees to radians
    var deltaLatRadians =  deltaLat * (Math.PI / 180);
    var deltaLongRadians = deltaLong * (Math.PI / 180);
    // Now do the Haversine equations
    var a = Math.sin(deltaLatRadians/2) * Math.sin(deltaLatRadians/2) + Math.cos(degreesBottom * (Math.PI / 180)) * Math.cos(degreesTop * (Math.PI / 180)) * Math.sin(deltaLongRadians/2) * Math.sin(deltaLongRadians/2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    var d = RadiusEarth * c;
    var InchesPerPixel = d / imageWidthInPixels;
    //Return the scale using calculated inches per pixel
    ApproximateScale = Math.round( InchesPerPixel * MonitorDPI);
  } else if ( MapUnits.toUpperCase() == "CENTIMETERS" ) {
    ApproximateScale = Math.round( (deltaLong/(imageWidthInPixels/dPixelsPerCentimeters)) );
  } else if ( MapUnits.toUpperCase() == "FEET" ) {
    ApproximateScale = Math.round( (deltaLong/(imageWidthInPixels/dPixelsPerFeet))*100 ) / 100;
  } else if ( MapUnits.toUpperCase() == "INCHES" ) {
    ApproximateScale = Math.round( (deltaLong/(imageWidthInPixels/ScaleToolDPI)) );
  } else if ( MapUnits.toUpperCase() == "KILOMETERS" ) {
    ApproximateScale = Math.round( (deltaLong/(imageWidthInPixels/dPixelsPerKilometers))*100 ) / 100;
  } else if ( MapUnits.toUpperCase() == "METERS" ) {
    ApproximateScale = Math.round( (deltaLong/(imageWidthInPixels/dPixelsPerMeters))*100 ) / 100;
  } else if ( MapUnits.toUpperCase() == "MILES" ) {
    ApproximateScale = Math.round( (deltaLong/(imageWidthInPixels/dPixelsPerMiles))*100 ) / 100;
  }
  return ApproximateScale;
}

//*****************************************************************************
// Function DrawScaleTool
// This function draws a scale bar tool with 25 lines, a title, zoom in, zoom out and the approximate 1 inch scale
// all contained in a table whose width is 100%.  Meant to span width of TOCFrame...
//*****************************************************************************
function DrawScaleTool(ShowActualScale) {
  //alert("in DrawScaleTool");
  //alert("top=" + eTop + "   bottom=" + eBottom + "   left=" + eLeft + "   right=" + eRight);
  if (!Boolean(ShowActualScale)) { ShowActualScale=true; }
  var ScaleToolHTML = '';
  if (Show_Scale_Title) { ScaleToolHTML += ('<div align="left" class="title_label">&nbsp;Scale Information</div>'); }
  ScaleToolHTML += ('<table cellspacing="0" cellpadding="0" summary="layout for the scale bar tool" width="100%">');
  ScaleToolHTML += ('<tr>');
  ScaleToolHTML += ('<td align="center" nowrap>');
  ScaleToolHTML += ('<a href="javascript:void(0);" title="Zooms the map Out" onclick="parent.MapFrame.zoomButton(0);">');
  ScaleToolHTML += ('<img src="/Image_Library/buttons/zoomout_1.gif" width="16" height="16" hspace="0" vspace="0" border="0" alt="Zooms the map Out" name="zoomcenter" /><br /><small><small>Out</small></small></a>');
  ScaleToolHTML += ('</td><td align="center" nowrap>');
  //Get the Current Approximate Haversine Scale for the current image (Done at center of image...)
  var CenterAdjust = Math.abs(eRight - eLeft);
  var ApproxScale = HaversineScale((eLeft + CenterAdjust), eBottom, (eLeft + CenterAdjust), eTop, iHeight, ScaleToolDPI);
  //Get the Approximate Haversine Scale for the full extent  (Done at center of image...)  Multiplied by 2 to create a slightly larger scale range.
  var CenterAdjust2 = Math.abs(limitRight - limitLeft);
  var theMaxScale = 2 *HaversineScale((limitLeft + CenterAdjust2), limitBottom, (limitLeft + CenterAdjust2), limitTop, iHeight, ScaleToolDPI);
  //Scale display is made by comparing the Current Scale versus factors of two of the Full Extent Scale
  var CurrentStage = 0;
  var StageScale = 0;
  var NextStageScale = 0;
  var ActualStage = 0;
  for (var j=30; j>10 ; j--) {
     CurrentStage = Math.abs(j-31);
     StageScale = theMaxScale/Math.pow(2,CurrentStage);
     NextStageScale = theMaxScale/Math.pow(2,CurrentStage+1);
     if ( ( ApproxScale > NextStageScale ) && ( CurrentStage <= 1 ) ) {
      ActualStage = 1;
     } else if ( (ApproxScale < NextStageScale ) && ( CurrentStage >= 25 ) ) {
      ActualStage = 25;
     } else if ( (ApproxScale > NextStageScale ) && (ApproxScale < StageScale ) ) {
      ActualStage = CurrentStage;
     }
  }
  //Now create the scale display, and calculate zoom envelopes to make that diplay clickable
  var StageEnvelopeX1 = 0;
  var StageEnvelopeY1 = 0;
  var StageEnvelopeX2 = 0;
  var StageEnvelopeY2 = 0;
  var xAdjustment = 0;
  var yAdjustment = 0;
  //alert("eLeft=" + eLeft + "    eBottom=" + eBottom);
  //alert("eRight=" + eRight + "   eTop=" + eTop);
  var CenterX = parent.MapFrame.eLeft + ((parent.MapFrame.eRight - parent.MapFrame.eLeft)/2.0);
  var CenterY = parent.MapFrame.eBottom + ((parent.MapFrame.eTop - parent.MapFrame.eBottom)/2.0);
  //alert("CenterX=" + CenterX + "    CenterY=" + CenterY);
  
  for (var i=30; i>10 ; i--) {
     CurrentStage = Math.abs(i-31);
     if (CurrentStage < ActualStage) {
       //Zoom Out
       xAdjustment = parent.MapFrame.xHalf * Math.pow(2, Math.abs(ActualStage - CurrentStage) - 1);
       yAdjustment = parent.MapFrame.yHalf * Math.pow(2, Math.abs(ActualStage - CurrentStage) - 1);
     } else {
       xAdjustment = parent.MapFrame.xHalf / Math.pow(2, Math.abs(ActualStage - CurrentStage) );
       yAdjustment = parent.MapFrame.yHalf / Math.pow(2, Math.abs(ActualStage - CurrentStage) );
     }
     StageEnvelopeX1 = CenterX - xAdjustment;
     StageEnvelopeX2 = CenterX + xAdjustment;
     StageEnvelopeY1 = CenterY - yAdjustment;
     StageEnvelopeY2 = CenterY + yAdjustment;
     if ( CurrentStage == ActualStage ) {
      ScaleToolHTML += ('<img src="/Image_Library/pixel.gif" width="3" height="' + i + '" hspace="0" vspace="0" border="0" alt="Current Zoom Range" style="background-color: ' + parent.MapFrame.zoomBoxColor + ';" />');
      ScaleToolHTML += ('<img src="/Image_Library/pixel.gif" width="1" height="' + i + '" hspace="0" vspace="0" border="0" alt="Current Zoom Range" />');
     } else {
      ScaleToolHTML += ('<a href="javascript:void(0);" onclick="parent.MapFrame.zoomToEnvelope(' + StageEnvelopeX1 + ',' + StageEnvelopeY1 + ',' + StageEnvelopeX2 + ',' + StageEnvelopeY2 + ')" title="Zoom by factor of ' + (ActualStage - CurrentStage) + '">');
      ScaleToolHTML += ('<img src="/Image_Library/pixel_black.gif" width="3" height="' + i + '" hspace="0" vspace="0" border="0" alt="Zoom by factor of ' + (ActualStage - CurrentStage) + '" style="background-color: #000000;" /></a>');
      ScaleToolHTML += ('<a href="javascript:void(0);" onclick="parent.MapFrame.zoomToEnvelope(' + StageEnvelopeX1 + ',' + StageEnvelopeY1 + ',' + StageEnvelopeX2 + ',' + StageEnvelopeY2 + ')" title="Zoom by factor of ' + (ActualStage - CurrentStage) + '">');
      ScaleToolHTML += ('<img src="/Image_Library/pixel.gif" width="1" height="' + i + '" hspace="0" vspace="0" border="0" alt="Zoom by factor of ' + (ActualStage - CurrentStage) + '" /></a>');
     }
  }
  ScaleToolHTML += ('</td><td align="center" nowrap>');
  ScaleToolHTML += ('<a href="javascript:void(0);" title="Zooms the map In" onclick="parent.MapFrame.zoomButton(1);">');
  ScaleToolHTML += ('<img src="/Image_Library/buttons/zoomin_1.gif" width="16" height="16" hspace="0" vspace="0" border="0" alt="Zooms the map In" name="zoomcenter" /><br /><small><small>In</small></small></a>');
  ScaleToolHTML += ('</td></tr>');
  if (ShowActualScale) {
    ScaleToolHTML += ('<tr><td align="center" nowrap colspan="3">');
    ScaleToolHTML += ('<a href="javascript:void(0);" title="Scale Information" onclick="parent.MapFrame.ScaleToolInfo();"><small><small>Scale ~ 1');
    if ( MapUnits.toUpperCase() == "CENTIMETERS" || MapUnits.toUpperCase() == "FEET" || MapUnits.toUpperCase() == "KILOMETERS" || MapUnits.toUpperCase() == "METERS" || MapUnits.toUpperCase() == "MILES" ) {
      ScaleToolHTML += (' Inch:');
    } else {
      ScaleToolHTML += (':');
    }
    ScaleToolHTML += (number_format(ApproxScale));
    if ( MapUnits.toUpperCase() == "CENTIMETERS" ) {
      ScaleToolHTML += (' Centimeters');
    } else if ( MapUnits.toUpperCase() == "FEET" ) {
      ScaleToolHTML += (' Feet');
    } else if ( MapUnits.toUpperCase() == "KILOMETERS" ) {
      ScaleToolHTML += (' Kilometers');
    } else if ( MapUnits.toUpperCase() == "METERS" ) {
      ScaleToolHTML += (' Meters');
    } else if ( MapUnits.toUpperCase() == "MILES" ) {
      ScaleToolHTML += (' Miles');
    }
    ScaleToolHTML += ('</small></small></a></td></tr>');
  }
  ScaleToolHTML += ('</table>');
  //alert("leaving DrawScaleTool with: " + ScaleToolHTML);
  return ScaleToolHTML;
}

//*****************************************************************************
// Function ScaleToolInfo
// This function displays a window that explains how the scale is calculated
//*****************************************************************************
function ScaleToolInfo() {
	var theFrame = "parent.MapFrame";
  var WindowContent='';
	if ((useExternalWindow) || (!useTextFrame)) {
    theFrame = "opener";
    if (parent.MapFrame!=null) { theFrame = "opener.parent.MapFrame"; }
	}
  WindowContent += ('<strong>Scale Units:</strong><ul>');
  WindowContent += ('Units are in inches for Decimal Degrees, or 1 inch = X map units for other map units. ');
  //Get the Current Approximate Haversine Scale for the current image (Done at center of image...)
  var ApproxScale = 0.0;
  var CenterAdjust = 0.0;
  CenterAdjust = Math.abs(eRight - eLeft);
  ApproxScale = HaversineScale((eLeft + CenterAdjust), eBottom, (eLeft + CenterAdjust), eTop, iHeight, ScaleToolDPI);
  WindowContent += ('The current Vertical Map Scale (at center) is 1 inch = ' + number_format(ApproxScale) + ' ');
  if ( MapUnits.toUpperCase() == "CENTIMETERS" ) {
    WindowContent += ('Centimeters');
  } else if ( MapUnits.toUpperCase() == "FEET" ) {
    WindowContent += ('Feet');
  } else if ( MapUnits.toUpperCase() == "KILOMETERS" ) {
    WindowContent += ('Kilometers');
  } else if ( MapUnits.toUpperCase() == "METERS" ) {
    WindowContent += ('Meters');
  } else if ( MapUnits.toUpperCase() == "MILES" ) {
    WindowContent += ('Miles');
  } else {
    WindowContent += ('Inches');
  }
  WindowContent += ('.</ul>');
  WindowContent += ('<strong>Scale Calculation</strong><ul>');
  WindowContent += ('This scale is only an approximation.  Initial tests showed it to be usually accurate within 5% for Decimal Degrees, but this may vary from system to system and exhaustive testing was not done.  ');
  WindowContent += ('It works by calculating the map units per pixel for the image and combining that with the pixels per inch for the image.  ');
  WindowContent += ('For Decimal Degrees, it uses the Haversine Equation from <a href = "http:\/\/mathforum.org/library/drmath/view/51879.html" target="_blank">http://mathforum.org/library/drmath/view/51879.html</a> ');
  WindowContent += ('which draws from <a href="http:\/\/skyandtelescope.com/magazinearchive/search/results.asp?start_date=&end_date=&terms=&title=&author=Sinnot&start_date_month=08&start_date_year=1984&end_date_month=08&end_date_year=1984&go.x=36&go.y=6" target="_blank">R. W. Sinnott, "Virtues of the Haversine,"  Sky and Telescope, vol. 68, no. 2, 1984, p. 159.</a>  ');
  WindowContent += ('Other map units are calculated using an adaptation of code posted by Markus Bedel to the ArcIMS Users Forum.<br /><br />');
  WindowContent += ('96 is the image DPI value specified in the ArcXML users guide as a default, but 97.6923076923077 is the value calculated by Dave Bollinger in the ArcIMS Users Forum.  This code uses the second value.  ');
  WindowContent += ('The radius of the Earth is taken from the GRS80 spheroid radius in <a href="http:\/\/www.amazon.com/exec/obidos/tg/detail/-/0748403035/qid=1043947573/sr=1-1/ref=sr_1_1/102-6707265-0538530?v=glance&s=books" target="_blank">Map Projections: A Reference Manual</a> by by Lev M. Bugayevskiy, and John Parr Snyder. (6,378,137 meters which converts to 251,107,755.9 inches).  ');
  WindowContent += ('Values are not rounded to avoid introducing possible error and make the coding simpler.  All calculations are done along a vertical cross-section at the center of the image.</ul>');

  Create_Pop_Up_Window('Current Scale Information', '', WindowContent, 'ScaleInfo', 'width=375,height=400,scrollbars=yes,resizable=yes');
}

//*****************************************************************************
// Function number_format
// This is taken from http://developer.irt.org/script/8.htm and is one of the
// three examples that format a number with commas.  This one was used because
// the other two involve string manipulation which can be slow, and this one
// uses arrays.  (Hopefully faster, but not sure...)
//*****************************************************************************
function number_format(n) {
  var arr=new Array('0'), i=0; 
  while (n>0) 
    {arr[i]=''+n%1000; n=Math.floor(n/1000); i++;}
  arr=arr.reverse();
  for (var i in arr) if (i>0) //padding zeros
    while (arr[i].length<3) arr[i]='0'+arr[i];
  return arr.join();
}