Reputation: 111
I'm trying to make a website that asks for the user's location, then finds the closest location (100m radius) from their position using GeoLocation and displays the result in HTML.
What I have tried:
$.getJSON("places.json", function (data) {
for (var i = 0; i < data.length; i++) {
if ((data[i].lat - poslat) > 0.00200 || (data[i].lng - poslng) > 0.00200) {
return data[i];
}
html += '<p>' + data[i].location + ' - ' + data[i].code + '</p>';
$('#nearbystops').append(html);
}
});
places.json
[
{
"code": "0001",
"lat": "1.28210155945393",
"lng": "103.81722480263163",
"location": "Stop 1"
},
{
"code": "0003",
"lat": "1.2777380589964",
"lng": "103.83749709165197",
"location": "Stop 2"
},
{
"code": "0002",
"lat": "1.27832046633393",
"lng": "103.83762574759974",
"location": "Stop 3"
}
]
Thank you in advance! :)
Upvotes: 11
Views: 28813
Reputation: 1303
there are a few algorithms you can use:
Haversine Formula Wikipedia
Vincenty’s Formula Wikipedia
Great-circle Distance with Equirectangular Approximation Wikipedia
Geodesic Calculations Wikipedia
Spherical Law of Cosines Wikipedia
Run all scripts below, and pay attention to how they compare to each other, short distance (Anna Bay to Sydney) and long distance (Anna Bay to São Paulo - real distance is 13,435 km)
interface City {
city: string;
lat: number;
lng: number;
}
const cityCoordinates: City[] = [
{ city: 'Sydney', lat: -33.8688, lng: 151.2093 },
{ city: 'Melbourne', lat: -37.8136, lng: 144.9631 },
{ city: 'Wollongong', lat: -34.4278, lng: 150.8931 },
{ city: 'Canberra', lat: -35.2802, lng: 149.1310 },
{ city: 'Brisbane', lat: -27.4698, lng: 153.0251 },
{ city: 'Perth', lat: -31.9505, lng: 115.8605 },
{ city: 'Sao Paulo', lat: -23.5558, lng: 46.6396 }
];
const userLocation: { city: string; lat: number; lng: number } = {
city: 'Anna Bay',
lat: -32.7790,
lng: 152.0858
};
// Function to calculate the distance between two coordinates using Haversine formula
const calculateDistance = (lat1: number, lng1: number, lat2: number, lng2: number): number => {
const toRadians = (degree: number) => degree * (Math.PI / 180);
const R = 6371; // Radius of the Earth in km
const dLat = toRadians(lat2 - lat1);
const dLng = toRadians(lng2 - lng1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
Math.sin(dLng / 2) * Math.sin(dLng / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // Distance in km
};
// Find the closest city to the user's location
const findClosestCity = (userLat: number, userLng: number, cities: City[]): City => {
let closest: City & { distance: number } = { city: '', lat: 0, lng: 0, distance: Infinity };
cities.forEach(city => {
const distance = calculateDistance(userLat, userLng, city.lat, city.lng);
console.log(`Distance from ${userLocation.city} to ${city.city}: ${distance.toFixed(2)} km`);
if (distance < closest.distance) {
closest = { ...city, distance };
}
});
return closest;
};
const closestCity = findClosestCity(userLocation.lat, userLocation.lng, cityCoordinates);
console.log(closestCity)
// Equirectangular Approximation algorithm
interface City {
city: string;
lat: number;
lng: number;
}
const cityCoordinates: City[] = [
{ city: 'Sydney', lat: -33.8688, lng: 151.2093 },
{ city: 'Melbourne', lat: -37.8136, lng: 144.9631 },
{ city: 'Wollongong', lat: -34.4278, lng: 150.8931 },
{ city: 'Canberra', lat: -35.2802, lng: 149.1310 },
{ city: 'Brisbane', lat: -27.4698, lng: 153.0251 },
{ city: 'Perth', lat: -31.9505, lng: 115.8605 },
{ city: 'Sao Paulo', lat: -23.5558, lng: 46.6396 }
];
const userLocation: { city: string; lat: number; lng: number } = {
city: 'Anna Bay',
lat: -32.7790,
lng: 152.0858
};
// Equirectangular approximation for distance calculation
const equirectangularDistance = (lat1: number, lng1: number, lat2: number, lng2: number): number => {
const R = 6371; // Radius of the Earth in km
const dLat = (lat2 - lat1) * (Math.PI / 180);
const dLng = (lng2 - lng1) * (Math.PI / 180);
const a = Math.cos(lat1 * (Math.PI / 180)) * Math.sin(dLng);
const b = Math.sin(dLat);
return Math.sqrt(a * a + b * b) * R;
};
// Find the closest city to the user's location
const findClosestCity = (userLat: number, userLng: number, cities: City[]): City & { distance: number } => {
let closest: City & { distance: number } = { city: '', lat: 0, lng: 0, distance: Infinity };
cities.forEach(city => {
const distance = equirectangularDistance(userLat, userLng, city.lat, city.lng);
console.log(`Distance from ${userLocation.city} to ${city.city}: ${distance.toFixed(2)} km`);
if (distance < closest.distance) {
closest = { ...city, distance };
}
});
return closest;
};
const closestCity = findClosestCity(userLocation.lat, userLocation.lng, cityCoordinates);
console.log('Closest City:', closestCity);
interface City {
city: string;
lat: number;
lng: number;
}
const cityCoordinates: City[] = [
{ city: 'Sydney', lat: -33.8688, lng: 151.2093 },
{ city: 'Melbourne', lat: -37.8136, lng: 144.9631 },
{ city: 'Wollongong', lat: -34.4278, lng: 150.8931 },
{ city: 'Canberra', lat: -35.2802, lng: 149.1310 },
{ city: 'Brisbane', lat: -27.4698, lng: 153.0251 },
{ city: 'Perth', lat: -31.9505, lng: 115.8605 },
{ city: 'Sao Paulo', lat: -23.5558, lng: 46.6396 }
];
const userLocation: { city: string; lat: number; lng: number } = {
city: 'Anna Bay',
lat: -32.7790,
lng: 152.0858
};
// Vincenty formula for distance calculation
const vincentyDistance = (lat1: number, lng1: number, lat2: number, lng2: number): number => {
const a = 6378137; // Major radius (meters)
const f = 1 / 298.257223563; // Flattening
const b = (1 - f) * a; // Minor radius
const toRadians = (degree: number) => degree * (Math.PI / 180);
const phi1 = toRadians(lat1);
const phi2 = toRadians(lat2);
const L = toRadians(lng2 - lng1);
const U1 = Math.atan((1 - f) * Math.tan(phi1));
const U2 = Math.atan((1 - f) * Math.tan(phi2));
const sinU1 = Math.sin(U1);
const cosU1 = Math.cos(U1);
const sinU2 = Math.sin(U2);
const cosU2 = Math.cos(U2);
let lambda = L;
let lambdaPrev: number;
let iterationLimit = 100;
let sinSigma: number, cosSigma: number, sigma: number;
let cos2SigmaM: number, sinAlpha: number, cosSqAlpha: number;
do {
lambdaPrev = lambda;
const sinLambda = Math.sin(lambda);
const cosLambda = Math.cos(lambda);
sinSigma = Math.sqrt((cosU2 * sinLambda) ** 2 + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) ** 2);
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
sigma = Math.atan2(sinSigma, cosSigma);
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
cosSqAlpha = 1 - sinAlpha ** 2;
cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
const C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
lambda = L + (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM ** 2)));
} while (Math.abs(lambda - lambdaPrev) > 1e-12 && --iterationLimit > 0);
const uSq = cosSqAlpha * (a ** 2 - b ** 2) / (b ** 2);
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
const deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM ** 2) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma ** 2) * (-3 + 4 * cos2SigmaM ** 2)));
const s = b * A * (sigma - deltaSigma);
return s / 1000; // Convert meters to kilometers
};
// Find the closest city to the user's location
const findClosestCity = (userLat: number, userLng: number, cities: City[]): City & { distance: number } => {
let closest: City & { distance: number } = { city: '', lat: 0, lng: 0, distance: Infinity };
cities.forEach(city => {
const distance = vincentyDistance(userLat, userLng, city.lat, city.lng);
console.log(`Distance from ${userLocation.city} to ${city.city}: ${distance.toFixed(2)} km`);
if (distance < closest.distance) {
closest = { ...city, distance };
}
});
return closest;
};
const closestCity = findClosestCity(userLocation.lat, userLocation.lng, cityCoordinates);
console.log('Closest City:', closestCity);
interface City {
city: string;
lat: number;
lng: number;
}
const cityCoordinates: City[] = [
{ city: 'Sydney', lat: -33.8688, lng: 151.2093 },
{ city: 'Melbourne', lat: -37.8136, lng: 144.9631 },
{ city: 'Wollongong', lat: -34.4278, lng: 150.8931 },
{ city: 'Canberra', lat: -35.2802, lng: 149.1310 },
{ city: 'Brisbane', lat: -27.4698, lng: 153.0251 },
{ city: 'Perth', lat: -31.9505, lng: 115.8605 },
{city: 'Sao Paulo', lat: -23.5558, lng: 46.6396}
];
const userLocation: { city: string; lat: number; lng: number } = {
city: 'Anna Bay',
lat: -32.7790,
lng: 152.0858
};
// Function to calculate the geodesic distance using WGS84 ellipsoid
const geodesicDistance = (lat1: number, lng1: number, lat2: number, lng2: number): number => {
const a = 6378137; // WGS84 major axis (meters)
const f = 1 / 298.257223563; // Flattening
const b = (1 - f) * a; // WGS84 minor axis
const toRadians = (degree: number) => degree * (Math.PI / 180);
const phi1 = toRadians(lat1);
const phi2 = toRadians(lat2);
const lambda1 = toRadians(lng1);
const lambda2 = toRadians(lng2);
const L = lambda2 - lambda1;
const U1 = Math.atan((1 - f) * Math.tan(phi1));
const U2 = Math.atan((1 - f) * Math.tan(phi2));
let lambda = L;
let lambdaPrev;
let iterationLimit = 100;
let sinSigma, cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM, C;
do {
lambdaPrev = lambda;
const sinLambda = Math.sin(lambda);
const cosLambda = Math.cos(lambda);
sinSigma = Math.sqrt(
(Math.cos(U2) * sinLambda) ** 2 +
(Math.cos(U1) * Math.sin(U2) - Math.sin(U1) * Math.cos(U2) * cosLambda) ** 2
);
cosSigma = Math.sin(U1) * Math.sin(U2) + Math.cos(U1) * Math.cos(U2) * cosLambda;
sigma = Math.atan2(sinSigma, cosSigma);
sinAlpha = Math.cos(U1) * Math.cos(U2) * sinLambda / sinSigma;
cosSqAlpha = 1 - sinAlpha ** 2;
cos2SigmaM = cosSigma - 2 * Math.sin(U1) * Math.sin(U2) / cosSqAlpha;
C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
lambda = L + (1 - C) * f * sinAlpha * (
sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM ** 2))
);
} while (Math.abs(lambda - lambdaPrev) > 1e-12 && --iterationLimit > 0);
const uSq = cosSqAlpha * (a ** 2 - b ** 2) / (b ** 2);
const A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
const B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
const deltaSigma = B * sinSigma * (
cos2SigmaM + B / 4 * (
cosSigma * (-1 + 2 * cos2SigmaM ** 2) -
B / 6 * cos2SigmaM * (-3 + 4 * sinSigma ** 2) * (-3 + 4 * cos2SigmaM ** 2)
)
);
const s = b * A * (sigma - deltaSigma);
return s / 1000; // Convert meters to kilometers
};
// Find the closest city to the user's location
const findClosestCity = (userLat: number, userLng: number, cities: City[]): City & { distance: number } => {
let closest: City & { distance: number } = { city: '', lat: 0, lng: 0, distance: Infinity };
cities.forEach(city => {
const distance = geodesicDistance(userLat, userLng, city.lat, city.lng);
console.log(`Distance from ${userLocation.city} to ${city.city}: ${distance.toFixed(2)} km\n`);
if (distance < closest.distance) {
closest = { ...city, distance };
}
});
return closest;
};
const closestCity = findClosestCity(userLocation.lat, userLocation.lng, cityCoordinates);
console.log('Closest City:', closestCity);
interface City {
city: string;
lat: number;
lng: number;
}
const cityCoordinates: City[] = [
{ city: 'Sydney', lat: -33.8688, lng: 151.2093 },
{ city: 'Melbourne', lat: -37.8136, lng: 144.9631 },
{ city: 'Wollongong', lat: -34.4278, lng: 150.8931 },
{ city: 'Canberra', lat: -35.2802, lng: 149.1310 },
{ city: 'Brisbane', lat: -27.4698, lng: 153.0251 },
{ city: 'Perth', lat: -31.9505, lng: 115.8605 },
{city: 'Sao Paulo', lat: -23.5558, lng: 46.6396}
];
const userLocation: { city: string; lat: number; lng: number } = {
city: 'Anna Bay',
lat: -32.7790,
lng: 152.0858
};
// Spherical Law of Cosines for distance calculation
const sphericalLawOfCosinesDistance = (lat1: number, lng1: number, lat2: number, lng2: number): number => {
const toRadians = (degree: number) => degree * (Math.PI / 180);
const R = 6371; // Radius of the Earth in km
const lat1Rad = toRadians(lat1);
const lat2Rad = toRadians(lat2);
const dLngRad = toRadians(lng2 - lng1);
const distance = Math.acos(
Math.sin(lat1Rad) * Math.sin(lat2Rad) +
Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.cos(dLngRad)
) * R;
return distance;
};
// Find the closest city to the user's location
const findClosestCity = (userLat: number, userLng: number, cities: City[]): City & { distance: number } => {
let closest: City & { distance: number } = { city: '', lat: 0, lng: 0, distance: Infinity };
cities.forEach(city => {
const distance = sphericalLawOfCosinesDistance(userLat, userLng, city.lat, city.lng);
console.log(`Distance from ${userLocation.city} to ${city.city}: ${distance.toFixed(2)} km`);
if (distance < closest.distance) {
closest = { ...city, distance };
}
});
return closest;
};
const closestCity = findClosestCity(userLocation.lat, userLocation.lng, cityCoordinates);
console.log('Closest City:', closestCity);
Upvotes: 0
Reputation: 26143
To calculate distance between two co-ordinates, you can't just subtract the values. That's fine but it gives you the co-ordinates that are within a square. This may be suitable but mostly people do tend to want to search locations by radius. This function will do that...
function distance(lat1, lon1, lat2, lon2, unit) {
var radlat1 = Math.PI * lat1/180
var radlat2 = Math.PI * lat2/180
var theta = lon1-lon2
var radtheta = Math.PI * theta/180
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
if (dist > 1) {
dist = 1;
}
dist = Math.acos(dist)
dist = dist * 180/Math.PI
dist = dist * 60 * 1.1515
if (unit=="K") { dist = dist * 1.609344 }
if (unit=="N") { dist = dist * 0.8684 }
return dist
}
It's a common piece of code which I copied from here...
https://www.geodatasource.com/developers/javascript
And here it is, used in your example...
function distance(lat1, lon1, lat2, lon2, unit) {
var radlat1 = Math.PI * lat1/180
var radlat2 = Math.PI * lat2/180
var theta = lon1-lon2
var radtheta = Math.PI * theta/180
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
if (dist > 1) {
dist = 1;
}
dist = Math.acos(dist)
dist = dist * 180/Math.PI
dist = dist * 60 * 1.1515
if (unit=="K") { dist = dist * 1.609344 }
if (unit=="N") { dist = dist * 0.8684 }
return dist
}
var data = [{
"code": "0001",
"lat": "1.28210155945393",
"lng": "103.81722480263163",
"location": "Stop 1"
}, {
"code": "0003",
"lat": "1.2777380589964",
"lng": "103.83749709165197",
"location": "Stop 2"
}, {
"code": "0002",
"lat": "1.27832046633393",
"lng": "103.83762574759974",
"location": "Stop 3"
}];
var html = "";
var poslat = 1.28210155945393;
var poslng = 103.81722480263163;
for (var i = 0; i < data.length; i++) {
// if this location is within 0.1KM of the user, add it to the list
if (distance(poslat, poslng, data[i].lat, data[i].lng, "K") <= 0.1) {
html += '<p>' + data[i].location + ' - ' + data[i].code + '</p>';
}
}
$('#nearbystops').append(html);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="nearbystops"></div>
Upvotes: 29
Reputation: 2480
get current user's location using HTML5 geolocation and find nearest location within 100 meters.
include and use below google maps libs
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry"> </script>
Snippet
//calculates distance between two points in km's
function calcDistance(p1, p2) {
return (google.maps.geometry.spherical.computeDistanceBetween(p1, p2) / 1000).toFixed(2);
}
function getPosition(position) {
var userPosition = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
$.getJSON("places.json", function(data) {
for (var i = 0; i < data.length; i++) {
var p1 = new google.maps.LatLng(userPosition.lat, userPosition.lng);
var p2 = new google.maps.LatLng(data[i].lat, data[i].lng);
var distance = calcDistance(p1, p2) * 1000;
if ((distance * 1000) <= 100) {
html += '<p>' + data[i].location + ' - ' + data[i].code + '</p>';
$('#nearbystops').append(html);
}
}
})
}
// get user's current latitude & longitude
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getPosition);
} else {
alert("Geolocation is not supported by this browser.");
}
}
getLocation();
Upvotes: 4
Reputation: 1813
To calculate the distance between two points (latitude, longitude), implemented a function of haversine formula in typescript.
//There are 6200 points in the JSON file
import data from './json/test.json';
let radians = function (degree: number) {
// degrees to radians
let rad: number = degree * Math.PI / 180;
return rad;
}
const haversine = (lat1: number, lon1: number, lat2: number, lon2: number) => {
let dlat, dlon, a, c, R: number;
R = 6372.8; // km
dlat = radians(lat2 - lat1);
dlon = radians(lon2 - lon1);
lat1 = radians(lat1);
lat2 = radians(lat2);
a = Math.sin(dlat / 2) * Math.sin(dlat / 2) + Math.sin(dlon / 2) * Math.sin(dlon / 2) * Math.cos(lat1) * Math.cos(lat2)
c = 2 * Math.asin(Math.sqrt(a));
return R * c;
}
let test = function () {
const keys = Object.keys(data);
let count: number = keys.length;
for (var _i = 0; _i < count; _i++) {
var _dummy: number = haversine(
36.12, -86.67, data[_i].position.lat, data[_i].position.lng);
}
}
Upvotes: 3