<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
    <script type="text/javascript" src="BDCCParallelLines.gmap3.js"></script>
    <script src="script.js" type="text/javascript"></script>
  </head>

<body onload="linesmap()">
<h2>Google Maps v3 Parallel Lines. </h2>
  <p>Create parallel lines (and even polygons).
  You can click on the map to create polylines with parallel lines.
    
  </p> 
  Gap (meters): <input type="text" id="gap" value="7" /> <button onclick="createPolygon();">Plot Geofence</button>
  
  <div id="map_canvas" ></div> 


</body> 

</html>
  html { height: 100% }
  body { height: 100%; margin: 0px; padding: 0px }
  #map_canvas { height: 100%; width: 100%;}
// Parallel Polylines
// http://matthewschwartz.me/parallel-lines-and-google-maps-v3/
// Original Google Maps V2 awesome code by: Bill Chadwick March 2008
// Released as Free for any use @ http://wtp2.appspot.com/ParallelLines.htm
//
// Modified for use with GMaps V3 by: Matthew Schwartz (schwartz.matthew@schwartzlink.net)
// Also released as free for any use

// Modified again for use by Jereme Causing. Fixed some errors and added parameters -June 2013

/*
* BDCCParallelLines(Array points<lat,lng>, String color, float weight, float opacity, float gap, String _type)
* _type can be either polyline or polygon. polyline is the default
* returns OverlayView();
*/
function BDCCParallelLines(points, color, weight, opacity, gapPx, _type, _editable) {

  this.gapPx = gapPx;
  this.points = points;
  this.color = color;
  this.weight = weight;
  this.opacity = opacity;
  this.prj = null;
  this.line1 = null;
  this.line2 = null;
  this.zoomListener = null;
  this._type = _type;
  this._editable = false;
 


  this.polygon = null;
  if(_editable){

    this._editable = _editable; //boolean
  }
}


BDCCParallelLines.prototype = new google.maps.OverlayView();

// BDCCParallelLines implements the OverlayView interface
// Methods that need to be implemented in GMaps 3 = onAdd(), draw(), and onRemove()




BDCCParallelLines.prototype.onAdd = function() {
  this.setProjection();
  var foo = this;
  var zoomRecalc = function() {
    foo.onRemove();
    foo.setProjection();
  };

  this.zoomListener = google.maps.event.addListener(this.map, 'zoom_changed', zoomRecalc);
}

BDCCParallelLines.prototype.setProjection = function() {
  this.map = this.getMap();
  var overlay = new google.maps.OverlayView();
  overlay.draw = function() {};
  overlay.setMap(this.map);
  this.prj = overlay.getProjection();
}

BDCCParallelLines.prototype.onRemove = function() {
  if(this.line2) {
    this.line2.setMap(null);
    this.line2 = null;
  }
  if(this.line1) {
    this.line1.setMap(null);
    this.line1 = null;
  }
  if (this.prj) {
    this.prj = null;
  }
  if(this.zoomListener != null) {
    google.maps.event.removeListener(this.zoomListener);
  }
  if(this.polygon){
    this.polygon.setMap(null);
  }
}
BDCCParallelLines.prototype.draw = function(map) {
  if(this.line2) {
    this.line2.setMap(null);
    this.line2 = null;
  }
  if(this.line1) {
    this.line1.setMap(null);
    this.line1 = null;
  }
  this.recalc();
  return;
}

BDCCParallelLines.prototype.redraw = function(force) {
    return; //do nothing
}


BDCCParallelLines.prototype.recalc = function() {

 // var zoom = this.map.getZoom();
  var zoom = this.map.getZoom();

  //left and right swapped throughout!

  var pts1 = new Array();//left side of center
  var pts2 = new Array();//right side of center

  //shift the pts array away from the centre-line by half the gap + half the line width
  var o = (this.gapPx + this.weight)/2;

  var p2l,p2r;

  for (var i=1; i<this.points.length; i++){
    var p1lm1;
    var p1rm1;
    var p2lm1;
    var p2rm1;
    var thetam1;

    var p1 = this.prj.fromLatLngToContainerPixel(this.points[i-1]);
    var p2 = this.prj.fromLatLngToContainerPixel(this.points[i]);
    var theta = Math.atan2(p1.x-p2.x,p1.y-p2.y) + (Math.PI/2);
    var dl = Math.sqrt(((p1.x-p2.x)*(p1.x-p2.x))+((p1.y-p2.y)*(p1.y-p2.y)));
      if(theta > Math.PI)
          theta -= Math.PI*2;
    var dx = Math.round(o * Math.sin(theta));
    var dy = Math.round(o * Math.cos(theta));

    var p1l = new google.maps.Point(p1.x+dx,p1.y+dy);
    var p1r = new google.maps.Point(p1.x-dx,p1.y-dy);
    p2l = new google.maps.Point(p2.x+dx,p2.y+dy);
    p2r = new google.maps.Point(p2.x-dx,p2.y-dy);

    if(i==1){   //first point
      pts1.push(this.prj.fromContainerPixelToLatLng(p1l));
      pts2.push(this.prj.fromContainerPixelToLatLng(p1r));
    }

    else{ // mid points

    if(theta == thetam1){
      // adjacent segments in a straight line
      pts1.push(this.prj.fromContainerPixelToLatLng(p1l));
      pts2.push(this.prj.fromContainerPixelToLatLng(p1r));
    }
    else{
      var pli = this.intersect(p1lm1,p2lm1,p1l,p2l);
      var pri = this.intersect(p1rm1,p2rm1,p1r,p2r);

      var dlxi = (pli.x-p1.x);
      var dlyi = (pli.y-p1.y);
      var drxi = (pri.x-p1.x);
      var dryi = (pri.y-p1.y);
      var di = Math.sqrt((drxi*drxi)+(dryi*dryi));
      var s = o / di;

      var dTheta = theta - thetam1;
      if(dTheta < (Math.PI*2))
        dTheta += Math.PI*2;
      if(dTheta > (Math.PI*2))
        dTheta -= Math.PI*2;

      if(dTheta < Math.PI){
        //intersect point on outside bend
        pts1.push(this.prj.fromContainerPixelToLatLng(p2lm1));
        pts1.push(this.prj.fromContainerPixelToLatLng(new google.maps.Point(p1.x+(s*dlxi),p1.y+(s*dlyi)),zoom));
        pts1.push(this.prj.fromContainerPixelToLatLng(p1l));
      }
      else if (di < dl){
        pts1.push(this.prj.fromContainerPixelToLatLng(pli));
      }
      else{
        pts1.push(this.prj.fromContainerPixelToLatLng(p2lm1));
        pts1.push(this.prj.fromContainerPixelToLatLng(p1l));
      }

      var dxi = (pri.x-p1.x)*(pri.x-p1.x);
      var dyi = (pri.y-p1.y)*(pri.y-p1.y);
      if(dTheta > Math.PI){
        //intersect point on outside bend
        pts2.push(this.prj.fromContainerPixelToLatLng(p2rm1));
        pts2.push(this.prj.fromContainerPixelToLatLng(new google.maps.Point(p1.x+(s*drxi),p1.y+(s*dryi)),zoom));
        pts2.push(this.prj.fromContainerPixelToLatLng(p1r));
      }
      else if(di<dl)
  pts2.push(this.prj.fromContainerPixelToLatLng(pri));
      else{
        pts2.push(this.prj.fromContainerPixelToLatLng(p2rm1));
        pts2.push(this.prj.fromContainerPixelToLatLng(p1r));
      }
    }
}

    p1lm1 = p1l;
    p1rm1 = p1r;
    p2lm1 = p2l;
    p2rm1 = p2r;
    thetam1 = theta;
  }

  if(this._type == 'polyline' || this._type == null) //default
  {
   
  pts1.push(this.prj.fromContainerPixelToLatLng(p2l));//final point
  pts2.push(this.prj.fromContainerPixelToLatLng(p2r));

  if(this.line1)
    this.line1.setMap(null);
  this.line1 = new google.maps.Polyline({
    path: pts1,
    strokeColor: this.color,
    strokeOpacity: this.opacity,
    strokeWeight: this.weight,
    editable: this._editable
  });
  this.line1.setMap(this.map);
   if(this.line2)
     this.line1.setMap(null);
   this.line2 = new google.maps.Polyline({
     path: pts2,
     strokeColor: this.color,
     strokeOpacity: this.opacity,
     strokeWeight: this.weight
   });
   this.line2.setMap(this.map);
  }else if(this._type == 'polygon' ){

  pts1.push(this.prj.fromContainerPixelToLatLng(p2l));//final point
  pts2.push(this.prj.fromContainerPixelToLatLng(p2r));


  var newpts = pts1.concat(pts2.reverse());
  if(this.polygon){
      this.polygon.setMap(null);
    }
  this.polygon = new google.maps.Polygon({
    paths: newpts,
    editable: true,
    strokeColor: this.color,
    fillColor: this.color,
    strokeOpacity: this.opacity,
    strokeWeight: this.weight,
    editable: this._editable
  });
  this.polygon.setMap(this.map);

  /* if(this.line2)
     this.line1.setMap(null);
   this.line2 = new google.maps.Polyline({
     path: pts2,
     strokeColor: this.color,
     strokeOpacity: this.opacity,
     strokeWeight: this.weight
   });
   this.line2.setMap(this.map);
*/



  }


}

BDCCParallelLines.prototype.intersect = function(p0,p1,p2,p3)
{
// this function computes the intersection of the sent lines p0-p1 and p2-p3
// and returns the intersection point,

var a1,b1,c1, // constants of linear equations
    a2,b2,c2,
    det_inv,  // the inverse of the determinant of the coefficient matrix
    m1,m2;    // the slopes of each line

var x0 = p0.x;
var y0 = p0.y;
var x1 = p1.x;
var y1 = p1.y;
var x2 = p2.x;
var y2 = p2.y;
var x3 = p3.x;
var y3 = p3.y;

// compute slopes, note the cludge for infinity, however, this will
// be close enough

if ((x1-x0)!==0)
   m1 = (y1-y0)/(x1-x0);
else
   m1 = 1e+10;   // close enough to infinity

if ((x3-x2)!==0)
   m2 = (y3-y2)/(x3-x2);
else
   m2 = 1e+10;   // close enough to infinity

// compute constants

a1 = m1;
a2 = m2;

b1 = -1;
b2 = -1;

c1 = (y0-m1*x0);
c2 = (y2-m2*x2);

// compute the inverse of the determinate

det_inv = 1/(a1*b2 - a2*b1);

// use Kramers rule to compute xi and yi

var xi=((b1*c2 - b2*c1)*det_inv);
var yi=((a2*c1 - a1*c2)*det_inv);

return new google.maps.Point(Math.round(xi),Math.round(yi));

}
var map2;
var cPoly = null;
var cPolyM = null;
var cPts;
var sampleRoute;
var geofence;
function linesmap(){


    var myOptions = {
    zoom: 14,
    center: new google.maps.LatLng(51.425, -0.955),
    mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map2 = new google.maps.Map(document.getElementById("map_canvas"), myOptions);


        var pts = new Array();

  	pts.push (new google.maps.LatLng(51.42, -0.97));
		pts.push (new google.maps.LatLng(51.43, -0.96));
		pts.push (new google.maps.LatLng(51.425, -0.955));
		pts.push (new google.maps.LatLng(51.42, -0.95));//straight at zoom = 13
		pts.push (new google.maps.LatLng(51.43, -0.94));
		pts.push (new google.maps.LatLng(51.43, -0.9375));//horz & straight
		pts.push (new google.maps.LatLng(51.43, -0.935));
		pts.push (new google.maps.LatLng(51.425, -0.935));
		pts.push (new google.maps.LatLng(51.42, -0.935));//vert & straight


  var test_line = new google.maps.Polyline({
    path: pts,
    strokeColor: "#FF0000",
    strokeOpacity: 1.0,
    strokeWeight: 2
  });

  test_line.setMap(map2);

sampleRoute = test_line;

 

  var cPts = new Array();
 google.maps.event.addListener(map2, 'click', function(event) {
    if (event.latLng){
      cPts.push(event.latLng);
      if(cPoly) cPoly.setMap(null);
      if(cPolyM) cPolyM.setMap(null);
      if(cPts.length > 1){
        cPoly = new BDCCParallelLines(cPts,"#0000FF",4,0.5,10,'polyline');
        var cPolyM = new google.maps.Polyline({
          path: cPts,
          strokeColor: "#FF0000",
          strokeOpacity: 1.0,
          strokeWeight: 2
        });
        cPoly.setMap(map2);
        cPolyM.setMap(map2);
      }
    }
  });


 




}

 function getarraypath(shape){

   var item = []; 

          for(var x = 0; x < shape.getPath().getLength(); x++){
         // var lat = shape.getPath().getAt(x).lat();
         // var lng = shape.getPath().getAt(x).lng();

          item.push(shape.getPath().getAt(x));

         

          }

    return item;


}



function createPolygon(){

	var pts = getarraypath(sampleRoute)

	var gap = parseFloat(document.getElementById('gap').value);
	if(geofence){
		geofence.setMap(null);
	}
	 geofence = new BDCCParallelLines(pts,"#00FF00",4,0.3,gap,'polygon',false);

 	geofence.setMap(map2);
}