4

I am trying to use google maps API V3 to draw a line from a default starting point and into a given direction given the distance. So for instance lets say my default starting point was San Francisco and the user inputs a distance, I want to be able to draw a straight line going eastward across the US based on that input distance. I came across this thing called bearing but im not sure how to use it correctly. Here is the code that I am working with:

function initializeMap(){

var mapOptions = {
center: new google.maps.LatLng(36.033036, -93.8655744),
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
};   

var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);

var R = 6378137;
var dis = parseInt(document.form['input']['val'].value);
var bearing = 80; 
var lat2 = Math.asin(   Math.sin(37.470625)*Math.cos(dis/R)+Math.cos(37.470625)*Math.sin(dis/R)*Math.cos(bearing) );
var lon2 = -122.266823 + Math.atan2(Math.sin(bearing)*Math.sin(dis/R)*Math.cos(37.470625),Math.cos(dis/R)-Math.sin(37.470625)*Math.sin(-122.266823));


var impactP = new google.maps.Polyline({
  path: new google.maps.LatLng(lat2,lon2), 
              strokeColor: "#FF0000",
  strokeOpacity: 1.0,
          strokeWeight: 2
        });
impactP.setMap(map);
}

Can someone help me out please. I really want to get this to work.

peterh
  • 11,875
  • 18
  • 85
  • 108
cclerv
  • 2,921
  • 8
  • 35
  • 43
  • I need more information about what exactly you are trying to do. I can help with the Google Maps part, the math I don't really understand. I do recommend you to replace 37.470625 and -122.266823 with variables. Are those your destination latlng? I made some changes to your code to draw a line from the map center to this computed lat2, lon2. http://jsfiddle.net/X25dZ/ – Heitor Chang Apr 19 '12 at 14:24
  • Basically I want a user to be able to input in a distance. I then want to draw a line on google maps of this distance but only to the east of my default starting point. So essentially I will have a line the will be drawn a across the US since I want my starting point to be in San Francisco. The challenging thing is I dont explicitly have an end point. I only have the default starting point, and the distance of the line. So for example in the end my map would look something like this: http://jsfiddle.net/X25dZ/1/ – cclerv Apr 19 '12 at 15:02

3 Answers3

3

The maths is all explained here: http://www.movable-type.co.uk/scripts/latlong.html#destPoint

var lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) + 
              Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );
var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1), 
                     Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));

Note that lat1,lon1 (your starting point) and lat2,lon2 (the end point) are in radians not degrees. d is the distance travelled and R the radius of the Earth. These can be in any units as long as they are both the same units.

It looks like your code in the question could work if you work in radians.

If you start out on a bearing of 90° in the northern hemisphere you will end up heading slightly south (your final bearing will be more than 90°). Spherical geometry is a wonderful thing!

If you want a line of constant bearing, so a 90° line is straight and horizontal on the map, you need Rhumb lines: http://www.movable-type.co.uk/scripts/latlong.html#rhumblines. That's a lot more complicated, but again that page allows end-point to be calculated from distance and bearing, and explains the maths and the Javascript necessary.


The same page suggests adding methods to Number to convert from degrees to radians and back again:

/** Converts numeric degrees to radians */
if (typeof(Number.prototype.toRad) === "undefined") {
  Number.prototype.toRad = function() {
    return this * Math.PI / 180;
  }
}

/** Converts radians to numeric (signed) degrees */
if (typeof(Number.prototype.toDeg) === "undefined") {
  Number.prototype.toDeg = function() {
    return this * 180 / Math.PI;
  }
}

Thus (78).toRad() is 1.3613568.

Andrew Leach
  • 12,945
  • 1
  • 40
  • 47
0

I put in the Google Maps scaffolding http://jsfiddle.net/khwLd/ but my math is off :( You can see the lines veer off to the south. Googling "find terminal coordinates given a bearing and a distance" gave me a python function which I converted and checked but is still not giving the expected result.

Calculating coordinates given a bearing and a distance

What you need to do is fix all the math at the beginning so that:

getDestination(origin_lat, origin_lng, bearing, distance) returns google.maps.LatLng(destination_lat, destination_lng)

Sorry I wasn't able to produce a full working solution :(

Community
  • 1
  • 1
Heitor Chang
  • 6,038
  • 2
  • 45
  • 65
0

You can use the method rhumbDestinationPoint from the library https://github.com/chrisveness/geodesy by Chris Veness, see also here http://www.movable-type.co.uk/scripts/latlong.html

rhumbDestinationPoint is applied to a LatLon object and takes as first argument the distance in meters and as second argument the bearing in sexagesimal degree, i.e. 0 is North, 90 is East, 180 is South and 270 is West.

I just pasted rhumbDestinationPoint from https://raw.githubusercontent.com/chrisveness/geodesy/master/latlon-spherical.js to the snippet below (adapted from the OP http://jsfiddle.net/X25dZ/1/)

function initializeMap() {
  mapCenter = new google.maps.LatLng(36.033036, -93.8655744);
  defaultStart = new google.maps.LatLng(37.470625, -122.266823);
  var mapOptions = {
    center: mapCenter,
    zoom: 2,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);

  var distance_in_meter = 600000;
  var bearing = 90;

  var start = new LatLon(defaultStart.lat(), defaultStart.lng());
  var destination = start.rhumbDestinationPoint(distance_in_meter, bearing);

  var impactP = new google.maps.Polyline({
    map: map,
    path: [defaultStart,
      new google.maps.LatLng(destination.lat, destination.lon)
    ],
    strokeColor: "#FF0000",
    strokeOpacity: 1.0,
    strokeWeight: 2
  });
}

google.maps.event.addDomListener(window, 'load', initializeMap);
html {
  height: 100%
}
body {
  height: 100%;
  margin: 0;
  padding: 0
}
#map-canvas {
  height: 100%
}
#menu {
  position: absolute;
  top: 0px;
  left: 0px;
  padding: 0px;
  font-family: Arial, sans-serif;
}
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>


<script type="text/javascript">
// Taken from https://raw.githubusercontent.com/chrisveness/geodesy/master/latlon-spherical.js 
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/* Latitude/longitude spherical geodesy tools                         (c) Chris Veness 2002-2016  */
/*                                                                                   MIT Licence  */
/* www.movable-type.co.uk/scripts/latlong.html                                                    */
/* www.movable-type.co.uk/scripts/geodesy/docs/module-latlon-spherical.html                       */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
'use strict';
if (typeof module!='undefined' && module.exports) var Dms = require('./dms'); // ≡ import Dms from 'dms.js'
/**
 * Library of geodesy functions for operations on a spherical earth model.
 *
 * @module   latlon-spherical
 * @requires dms
 */
/**
 * Creates a LatLon point on the earth's surface at the specified latitude / longitude.
 *
 * @constructor
 * @param {number} lat - Latitude in degrees.
 * @param {number} lon - Longitude in degrees.
 *
 * @example
 *     var p1 = new LatLon(52.205, 0.119);
 */
function LatLon(lat, lon) {
// allow instantiation without 'new'
if (!(this instanceof LatLon)) return new LatLon(lat, lon);
this.lat = Number(lat);
this.lon = Number(lon);
}

/**
 * Returns the destination point having travelled along a rhumb line from ‘this’ point the given
 * distance on the  given bearing.
 *
 * @param   {number} distance - Distance travelled, in same units as earth radius (default: metres).
 * @param   {number} bearing - Bearing in degrees from north.
 * @param   {number} [radius=6371e3] - (Mean) radius of earth (defaults to radius in metres).
 * @returns {LatLon} Destination point.
 *
 * @example
 *     var p1 = new LatLon(51.127, 1.338);
 *     var p2 = p1.rhumbDestinationPoint(40300, 116.7); // 50.9642°N, 001.8530°E
 */
LatLon.prototype.rhumbDestinationPoint = function(distance, bearing, radius) {
radius = (radius === undefined) ? 6371e3 : Number(radius);
var δ = Number(distance) / radius; // angular distance in radians
var φ1 = this.lat.toRadians(), λ1 = this.lon.toRadians();
var θ = Number(bearing).toRadians();
var Δφ = δ * Math.cos(θ);
var φ2 = φ1 + Δφ;
// check for some daft bugger going past the pole, normalise latitude if so
if (Math.abs(φ2) > Math.PI/2) φ2 = φ2>0 ? Math.PI-φ2 : -Math.PI-φ2;
var Δψ = Math.log(Math.tan(φ2/2+Math.PI/4)/Math.tan(φ1/2+Math.PI/4));
var q = Math.abs(Δψ) > 10e-12 ? Δφ / Δψ : Math.cos(φ1); // E-W course becomes ill-conditioned with 0/0
var Δλ = δ*Math.sin(θ)/q;
var λ2 = λ1 + Δλ;
return new LatLon(φ2.toDegrees(), (λ2.toDegrees()+540) % 360 - 180); // normalise to −180..+180°
};


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/**
 * Checks if another point is equal to ‘this’ point.
 *
 * @param   {LatLon} point - Point to be compared against this point.
 * @returns {bool}   True if points are identical.
 *
 * @example
 *   var p1 = new LatLon(52.205, 0.119);
 *   var p2 = new LatLon(52.205, 0.119);
 *   var equal = p1.equals(p2); // true
 */
LatLon.prototype.equals = function(point) {
if (!(point instanceof LatLon)) throw new TypeError('point is not LatLon object');
if (this.lat != point.lat) return false;
if (this.lon != point.lon) return false;
return true;
};
/**
 * Returns a string representation of ‘this’ point, formatted as degrees, degrees+minutes, or
 * degrees+minutes+seconds.
 *
 * @param   {string} [format=dms] - Format point as 'd', 'dm', 'dms'.
 * @param   {number} [dp=0|2|4] - Number of decimal places to use - default 0 for dms, 2 for dm, 4 for d.
 * @returns {string} Comma-separated latitude/longitude.
 */
LatLon.prototype.toString = function(format, dp) {
return Dms.toLat(this.lat, format, dp) + ', ' + Dms.toLon(this.lon, format, dp);
};
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/** Extend Number object with method to convert numeric degrees to radians */
if (Number.prototype.toRadians === undefined) {
Number.prototype.toRadians = function() { return this * Math.PI / 180; };
}
/** Extend Number object with method to convert radians to numeric (signed) degrees */
if (Number.prototype.toDegrees === undefined) {
Number.prototype.toDegrees = function() { return this * 180 / Math.PI; };
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
if (typeof module != 'undefined' && module.exports) module.exports = LatLon; // ≡ export default LatLon    </script>


<div id="map-canvas"></div>
Alessandro Jacopson
  • 18,047
  • 15
  • 98
  • 153