5

Objective

To know if a coordinate point is between two other coordinates.

Background

I am making a google-maps application, and I need to know if a certain Point is between two LatLng points (start and end).

I am looking for a function like the following:

var currentCoord = {lat: 51.8732, lng: -118.6346};
var startCoord = {lat: 61.3434, lng: -118.0046};
var endCoord = {lat: 50.5468, lng: -118.5435};

function isBetween(startCoord, endCoord, currentCoord){
    //calculations here
    return "true if currentCoord is between startCoord and endCoord, or false otherwise";
}

What I tried

To achieve this I read several questions and threads:

Code

No matter what i try, I cannot make it work, but I do have a minimal example with my failed experiments:

"use strict";

/*global google*/

function initialize() {

  let mapOptions = {
    zoom: 3,
    center: new google.maps.LatLng(0, -180),
    mapTypeId: google.maps.MapTypeId.TERRAIN
  };

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

  let flightPlanCoordinates = [
    new google.maps.LatLng(37.772323, -122.214897),
    new google.maps.LatLng(21.291982, -157.821856),
    new google.maps.LatLng(-18.142599, 178.431),
    new google.maps.LatLng(-27.46758, 153.027892)
  ];

  let flightPath = new google.maps.Polyline({
    path: flightPlanCoordinates,
    geodesic: true,
    strokeColor: '#FF0000',
    strokeOpacity: 1.0,
    strokeWeight: 2
  });

  flightPath.setMap(map);

  google.maps.event.addListener(flightPath, 'mouseover', function(event) {
    console.log("Marker is over the polyline");
  });

  let marker = new google.maps.Marker({
    position: new google.maps.LatLng(37.772323, -122.214897),
    draggable: true,
    map: map,
    title: 'Drag me!'
  });

  marker.addListener('drag', function(event) {

    let startPoint = {
      lat: 37.772323,
      lng: -122.214897
    };
    let endPoint = {
      lat: 21.291982,
      lng: -157.821856
    };
    let currentPoint = {
      lat: marker.getPosition().lat(),
      lng: marker.getPosition().lng()
    };

    if (checkCoordinate(startPoint, endPoint, currentPoint))
      console.log("in line !");
  });
}

google.maps.event.addDomListener(window, 'load', initialize);

function checkCoordinate(start, end, point) {
  var slope = (end.lng - start.lng) / (end.lat - start.lat);
  var newSlope = (end.lng - point.lng) / (end.lat - point.lat);
  return (point.lat > start.lat && point.lat < end.lat && point.lng > start.lng && point.lng < end.lng && slope == newSlope);
}
html,
body,
#map-canvas {
  height: 100%;
  margin: 0px;
  padding: 0px
}
<!DOCTYPE html>
<html>

<head>
  <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
  <meta charset="utf-8">
  <title>Simple Polylines</title>
  <link rel="stylesheet" type="text/css" href="style.css">
  <script src="https://maps.googleapis.com/maps/api/js?v=3"></script>
  <script src="script.js" type="text/javascript"></script>
</head>

<body>
  <div id="map-canvas"></div>
</body>

</html>

Question

  • How can I find out if a given coordinate is between two coordinates using a mathematical approach?
Community
  • 1
  • 1
Flame_Phoenix
  • 16,489
  • 37
  • 131
  • 266
  • Although it's Java code, you may want to take a look at the `PolyUtil.isLocationOnPath` method from the Google Maps API Utility Library https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/PolyUtil.java – antonio Jul 13 '16 at 14:20

3 Answers3

3

One option would be to use the google.maps.geometry.poly.isLocationOnEdge method.

code snippet:

var map;

function initialize() {

  var mapOptions = {
    zoom: 2,
    center: new google.maps.LatLng(0, -180),
    mapTypeId: google.maps.MapTypeId.TERRAIN
  };

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

  var flightPlanCoordinates = [
    new google.maps.LatLng(37.772323, -122.214897),
    new google.maps.LatLng(21.291982, -157.821856),
    new google.maps.LatLng(-18.142599, 178.431),
    new google.maps.LatLng(-27.46758, 153.027892)
  ];

  var flightPath = new google.maps.Polyline({
    path: flightPlanCoordinates,
    geodesic: false,
    strokeColor: '#FF0000',
    strokeOpacity: 1.0,
    strokeWeight: 2
  });

  flightPath.setMap(map);

  var marker = new google.maps.Marker({
    position: new google.maps.LatLng(37.772323, -122.214897),
    draggable: true,
    map: map,
    title: 'Drag me!'
  });

  marker.addListener('dragend', function(event) {

    var startPoint = {
      lat: 37.772323,
      lng: -122.214897
    };
    var startMarker = new google.maps.Marker({
      position: startPoint,
      map: map
    });
    var endPoint = {
      lat: 21.291982,
      lng: -157.821856
    };
    var endMarker = new google.maps.Marker({
      position: endPoint,
      map: map
    });
    var currentPoint = {
      lat: marker.getPosition().lat(),
      lng: marker.getPosition().lng()
    };

    if (checkCoordinate(startPoint, endPoint, marker.getPosition()))
      console.log("in line !");
  });
}

google.maps.event.addDomListener(window, 'load', initialize);

function checkCoordinate(start, end, point) {
  return google.maps.geometry.poly.isLocationOnEdge(point, new google.maps.Polyline({
    map: map,
    path: [start, end]
  }), 10e-1);
}
html,
body,
#map-canvas {
  height: 100%;
  margin: 0px;
  padding: 0px
}
<!DOCTYPE html>
<html>

<head>
  <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
  <meta charset="utf-8">
  <title>Simple Polylines</title>
  <link rel="stylesheet" type="text/css" href="style.css">
  <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
  <script src="script.js" type="text/javascript"></script>
</head>

<body>
  <div id="map-canvas"></div>
</body>

</html>
geocodezip
  • 158,664
  • 13
  • 220
  • 245
0

I have arrived to a solution, by calculating if a point belongs to a line or not.

After researching many mathematical principles, I decided to calculate the determinant of the matrix between two points, and check (with a certain level of precision) if my given point is between the line of points A and B.

/**
 * @const
 * @type        {Number}
 * @description The precision to calculate if a given point is between two other points. Low precisions get precise results but are less forgiving against errors.
 */
const PRECISION = 1;

/**
 * @function    isBetween
 * @description Determines if a point P = (p.x, p.y) lies on the line connecting points S = (S.x, S.y) and E = (E.x, E.y) by calculating the determinant of the matrix. A point is considered to belong to the line if the precision of the calculation is small enough (tests for errors and loss of precision).
 * @param       {Point} start   The start point
 * @param       {Point} end     The end point
 * @param       {Point} point   The point we which to test.
 * @returns     <code>true</code> if the given point belongs to the line, <code>false</code> otherwise.
 * @see         {@link http://stackoverflow.com/a/907491/1337392|Distance Matrix Calculation}
 */
function isBetween(start, end, point) {
    return Math.abs((end.lat - start.lat) * (point.lng - start.lng) - (end.lng - start.lng) * (point.lat - start.lat)) < PRECISION;
}

However, this solution has a pitfall worth mentioning. The problem with this solution is that it does not take into account the curvature of the earth. It is for straight lines only.

So, if you are checking the distances in your town, this is unlikely to be important at all. But if you are checking on a flight across the pacific, you should probably use another mathematical method.

For this reason, and because the polylines already take into account the curvature of the earth, I decided use the answer from geocodezip.


PS: I have to say it is funny. A few hours ago I replied to an old question where I also thanked geocodezip for the insightful comment he gave and now he is here, answering my question. Sometimes, I do believe the world is a small place. Thanks man!

Flame_Phoenix
  • 16,489
  • 37
  • 131
  • 266
0

Using Bearing section from mentioned http://www.movable-type.co.uk/scripts/latlong.html page, you can just check that

bearing(from currentCoord to startCoord) = 
  bearing(from currentCoord to endCoord) +/- 180 (with some tolerance)

This equation says that all the three points lie on the same great circle arc

(I assume that yours "is between two other coordinates" has the same meaning)

MBo
  • 77,366
  • 5
  • 53
  • 86