Last active
February 6, 2025 23:43
-
-
Save richard-flosi/8c4ade400188d1c91e8964ff3211d091 to your computer and use it in GitHub Desktop.
JavaScript Trilateration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Created by Derrick Cohodas (dav-) | |
// Based on the Python example by StackExchange user wwnick from http://gis.stackexchange.com/a/415/41129 | |
// Requires the Mathjs library - http://mathjs.org/ | |
var math = require('mathjs') | |
/** | |
* Represents a coordinate with a distance | |
* @param {Number} lat Latitude | |
* @param {Number} lon Longitude | |
* @param {Number} dist Distance from [lat, lon] to some point in kilometers | |
*/ | |
var Beacon = function(lat, lon, dist) { | |
this.lat = lat | |
this.lon = lon | |
this.dist = dist | |
return this | |
} | |
/** | |
* Perform a trilateration calculation to determine a location | |
* based on 3 beacons and their respective distances (in kilometers) to the desired point. | |
* | |
* @param {Array} beacons Array of 3 Beacon objects | |
* @return {Array} Array of the format [latitude, longitude] | |
*/ | |
var trilaterate = function(beacons) { | |
// #assuming elevation = 0 | |
var earthR = 6371e3 | |
, rad = function(deg) { | |
return deg * (math.pi/180) | |
} | |
, deg = function(rad) { | |
return rad * (180/math.pi) | |
} | |
// #using authalic sphere | |
// #if using an ellipsoid this step is slightly different | |
// #Convert geodetic Lat/Long to ECEF xyz | |
// # 1. Convert Lat/Long to radians | |
// # 2. Convert Lat/Long(radians) to ECEF | |
var P1 = [ earthR *(math.cos(rad(beacons[0].lat)) * math.cos(rad(beacons[0].lon))) | |
, earthR *(math.cos(rad(beacons[0].lat)) * math.sin(rad(beacons[0].lon))) | |
, earthR *(math.sin(rad(beacons[0].lat))) | |
] | |
var P2 = [ earthR *(math.cos(rad(beacons[1].lat)) * math.cos(rad(beacons[1].lon))) | |
, earthR *(math.cos(rad(beacons[1].lat)) * math.sin(rad(beacons[1].lon))) | |
, earthR *(math.sin(rad(beacons[1].lat))) | |
] | |
var P3 = [ earthR *(math.cos(rad(beacons[2].lat)) * math.cos(rad(beacons[2].lon))) | |
, earthR *(math.cos(rad(beacons[2].lat)) * math.sin(rad(beacons[2].lon))) | |
, earthR *(math.sin(rad(beacons[2].lat))) | |
] | |
// #from wikipedia | |
// #transform to get circle 1 at origin | |
// #transform to get circle 2 on x axis | |
var ex = math.divide(math.subtract(P2, P1), math.norm( math.subtract(P2, P1) )) | |
var i = math.dot(ex, math.subtract(P3, P1) ) | |
var ey = math.divide( | |
math.subtract( | |
math.subtract(P3, P1), | |
math.multiply(i, ex) | |
), | |
math.norm( | |
math.subtract( | |
math.subtract(P3, P1), | |
math.multiply(i, ex) | |
) | |
) | |
) | |
var ez = math.cross(ex, ey) | |
var d = math.norm(math.subtract(P2, P1)) | |
var j = math.dot(ey, math.subtract(P3, P1)) | |
// #from wikipedia | |
// #plug and chug using above values | |
var x = (math.pow(beacons[0].dist, 2) - math.pow(beacons[1].dist,2) + math.pow(d,2))/(2*d) | |
var y = ((math.pow(beacons[0].dist, 2) - math.pow(beacons[2].dist,2) + math.pow(i,2) + math.pow(j,2))/(2*j)) - ((i/j)*x) | |
// # only one case shown here | |
// | |
// I was having problems with the number in the radical being negative, | |
// so I took the absolute value. Not sure if this is always going to work | |
var z = math.sqrt( math.abs(math.pow(beacons[0].dist, 2) - math.pow(x, 2) - math.pow(y, 2)) ) | |
// #triPt is an array with ECEF x,y,z of trilateration point | |
var triPt = math.add( | |
math.add( | |
math.add(P1, | |
math.multiply(x, ex) | |
), | |
math.multiply(y, ey) | |
), | |
math.multiply(z, ez) | |
) | |
// #convert back to lat/long from ECEF | |
// #convert to degrees | |
var lat = deg(math.asin(math.divide(triPt[2], earthR))) | |
var lon = deg(math.atan2(triPt[1], triPt[0])) | |
return [lat, lon] | |
} | |
var beacons = [ new Beacon(35.000000, -120.000000, 189.419265289145) | |
, new Beacon(35.000005, -120.000010, 189.420325082156) | |
, new Beacon(35.000000, -120.000020, 189.420689733286) | |
] | |
console.log(trilaterate(beacons)) |
We'd used this one, works perfect. You've an error with the earth radius though. It's in kilometers, it should be in meters. Instead of using var earthR = 6371 on line 32 use -> var earthR = 6371e3
and it works like charm!
Thank you so much for sharing this. It saved us a lot of work :)
@seco35 Thanks! I've updated line 32 accordingly.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Did you have much luck finding a solution that works well to calculate trilateration in JS