
//<![CDATA[

// This shows range rings on the map. Interval is user choice or automatic
// Bill Chadwick Jan 2008

// point is the centre or origin of the rings which are drawn with colour, weight (width in pixels) and opacity (0-1).
// The interval parameter is either
//      1 - null in which case the interval is automatic depending on zoom level, showing several rings on screen
//      2 - A single number specifying a fixed interval between rings in metres
//      3 - An array of ring spacings in metres
// MaxRings is used for auto and fixed interval to limit the number of rings drawn, not used with an interval array
// if it is null, rings will be drawn to twice the map's diagonal
// if user labels are wanted, labels should be an array of length maxRings or the length of the interval array
// if interval is an array, colour can be a matching length array

function BdccRangeRings(point, colour, weight, opacity, interval, maxRings, labels) {

  this.colour = colour || "#0000FF";
  this.weight = weight || 3;
  this.opacity = opacity || 0.7;
  this.centre = point;
  this.interval = interval;
  this.maxRings = maxRings;
  this.labels = labels;

}
BdccRangeRings.prototype = new GOverlay();

BdccRangeRings.prototype.initialize = function(map) {

  this.map = map;
  this.circles = new Array();
  this.divs = new Array();//labels

  this.drawFirst = true;
  this.lstnMove = null;
  this.lstnStart = null;

}

BdccRangeRings.prototype.remove = function() {

        this.unDraw();

        //remove handlers we use to trigger redraw / undraw
        if(this.lstnMove != null)
                GEvent.removeListener(this.lstnMove);
        if(this.lstnStart != null)
                GEvent.removeListener(this.lstnStart);

}

BdccRangeRings.prototype.unDraw = function() {

  var div = this.map.getPane(G_MAP_MARKER_SHADOW_PANE);

  try{

        var i = 0;
        for(i=0; i< this.circles.length; i++)
                this.map.removeOverlay(this.circles[i]);
  }
  catch(e){
  }

  try{

        var i = 0;
      for(i=0; i< this.divs.length; i++)
              div.removeChild(this.divs[i]);
  }
  catch(e){
  }
  this.circles = new Array();
  this.divs = new Array();

}

BdccRangeRings.prototype.copy = function() {
  return new BdccRangeRings(this.point, this.colour, this.weight, this.opacity, this.interval, this.maxRings, this.labels);
}

//This normally does nothing due to reentrancy problems and problems removing overlays from within an overlay
//Instead we use the moveend event to trigger a redraw, this event occurs after zoom and map type changes
BdccRangeRings.prototype.redraw = function(force) {

        //but draw it the very first time
        if(this.drawFirst)
        {
                this.safeRedraw();

                // We use the moveend event to trigger further redraws
                var rdrw = GEvent.callback(this,this.safeRedraw );
                this.lstnMove = GEvent.addListener(this.map,"moveend",function(){rdrw ();});

                // And undraw during moves - for speed
                var udrw = GEvent.callback(this,this.unDraw );
                this.lstnStart = GEvent.addListener(this.map,"movestart",function(){udrw ();});

                this.drawFirst = false;

        }
}


// Redraw the rings based on the current projection and zoom level
BdccRangeRings.prototype.safeRedraw = function() {

  //clear old
  this.unDraw();

  var bnds = this.map.getBounds();
  var sz = this.map.getSize();
  var pxDiag = Math.sqrt((sz.width*sz.width) + (sz.height*sz.height));
  var diagKm = bnds.getNorthEast().distanceFrom(bnds.getSouthWest()) / 1609;
  var pxPerKm = pxDiag/diagKm;

  //alert(pxDiag + ' / ' + diagKm + ' = ' + pxPerKm);

  var d;//km initial/min interval

  if(this.interval == null)
  {
        //auto interval
        //Multiple by .621371 to allow ring to auto-scale to miles
        var width = (sz.width / pxPerKm) * .621371;
        var height = (sz.height / pxPerKm) * .621371;
        var ww = Math.max(width,height);

        if(ww < 1.0)
                d = 0.1;
        else if(ww < 2.0)
                d = 0.2;
        else if(ww < 5.0)
                d = 0.5;
        else if(ww < 10.0)
                d = 1.0;
        else if(ww < 20.0)
                d = 2.0;
        else if(ww < 50.0)
                d = 5.0;
        else if(ww < 100.0)
                d = 10.0;
        else if(ww < 200.0)
                d = 20.0;
        else if(ww < 500.0)
                d = 50.0;
        else if(ww < 1000.0)
                d = 100.0;
        else if(ww < 2000.0)
                d = 200.0;
        else if(ww < 5000.0)
                d = 500.0;
        else
                d = 1000.0;

  }
  else if (this.interval.constructor.toString().indexOf("Array") == -1)
        d = this.interval / 1000.0;// to km
  else
        d = this.interval;//array of distances

  if (d.constructor.toString().indexOf("Array") == -1){
      var p = (d >= 10.0) ? 0 : 1;//precision for label distances in km

        if(this.maxRings == null){
                //draw the rings to no more than twice the screen diagonal, auto or fixed interval
                // no custom labels as # rings drawn varies
                for(var r = d; (r < (diagKm * 2)); r += d)
                  this.drawCircle (r ,r.toFixed(p) + "mi",this.colour);
        }
        else{
                //draw maxRings rings auto or fixed interval
                var r = d;
                for(var i = 0; i<this.maxRings; i++){
                  this.drawCircle (r ,(this.labels == null)?r.toFixed(p)+ "mi":this.labels[i],this.colour);
                  r += d;
                }
        }
  }
  else{
        // interval array used
        for(var i = 0; i<d.length; i++){
            var c;
            if (this.colour.constructor.toString().indexOf("Array") == -1)
                c = this.colour;
            else
                  c = this.colour[i];
            this.drawCircle (d[i]/1000.0 ,(this.labels == null)?d[i].toFixed(p)+ "mi":this.labels[i],c);
        }

  }

}

  // get a new GLatLng distanceMeters away on the compass bearing azimuthDegrees
  // from the GLatLng point - accurate to better than 200m in 140km (20m in 14km) in the UK

  BdccRangeRings.prototype.PointAtRangeAndBearing = function (point, distanceMeters, azimuthDegrees)
        {
             var latr = point.lat() * Math.PI / 180.0;
             var lonr = point.lng() * Math.PI / 180.0;

             var coslat = Math.cos(latr);
             var sinlat = Math.sin(latr);
             var az = azimuthDegrees* Math.PI / 180.0;
             var cosaz = Math.cos(az);
             var sinaz = Math.sin(az);
             var dr = distanceMeters / 6378137.0; // distance in radians using WGS84 Equatorial Radius
             var sind = Math.sin(dr);
             var cosd = Math.cos(dr);

            return new GLatLng(Math.asin((sinlat * cosd) + (coslat * sind * cosaz)) * 180.0 / Math.PI,
            (Math.atan2((sind * sinaz), (coslat * cosd) - (sinlat * sind * cosaz)) + lonr) * 180.0 / Math.PI);
        }



BdccRangeRings.prototype.drawCircle = function (rKm, label, colour)
{
  if (r < 0)
        return;

  if (r > (6378137.0 * Math.PI * 2))
        return;

  var mapDiv = this.map.getPane(G_MAP_MARKER_SHADOW_PANE);
  var c;
  var b;
  var pts = new Array();
  //var r = rKm * 1000.0;
  var r = rKm * 1609.0;  //* Covert KM to MI

  // use 40 segment polyline to approximate a circle

  for(b=0; b<=360; b+=9){

        pts.push(this.PointAtRangeAndBearing(this.centre,r,b));

      if ((b %45 == 0)&&(b!=360)){

                //draw a label every 45 degrees

                var p = this.map.fromLatLngToDivPixel(pts[pts.length-1]);
                var dv = document.createElement("DIV");
                mapDiv.insertBefore(dv,null);
                dv.style.position = "absolute";
                dv.style.border = "none";
                dv.style.color = colour;
                dv.style.fontFamily='Arial';
                dv.style.fontSize='x-small';
                dv.innerHTML = label;
                var dx;
                var dy;

                //offset the labels to be just outside the ring

                if(b==0) {
                        dx = -dv.offsetWidth * 0.5;
                        dy = -dv.offsetHeight;
                }
                else if(b == 45){
                        dx = 3;
                        dy = -dv.offsetHeight;
                }
                else if(b == 90){
                        dx = 3;
                        dy = -dv.offsetHeight * 0.5;
                }
                else if(b == 135){
                        dx = 3;
                        dy = 3;
                }
                else if(b == 180){
                        dx = -dv.offsetWidth * 0.5;
                        dy = 3;
                }
                else if(b == 225){
                        dx = -dv.offsetWidth -3;
                        dy = 3;
                }
                else if(b == 270){
                        dx = -dv.offsetWidth -3;
                        dy = -dv.offsetHeight * 0.5;
                }
                else if(b == 315){
                        dx = -dv.offsetWidth -3;
                        dy = -dv.offsetHeight;
                }

                dv.style.left = (p.x+dx).toString() + "px";
                dv.style.top = (p.y+dy).toString() + "px";

                this.divs.push(dv);
        }
  }

  var c = new GPolyline(pts,colour,this.weight,this.opacity);
  this.map.addOverlay(c);
  this.circles.push(c);

}


