I need to work with geohashs. I have to check if a target position as geohash is in the range (distance) of my current position as geohash. Its actually working but I have noticed large range differences. So I have written a Tester where I don't need my database to show you the problem.
Start reading in the GeoTest() function I have defined a start position as lat, lon coordinates and also encoded as geohash.
Then I have put a distance/range value in miles.
With both I can compute the lower and upper geohashs.
With the lower and upper geohashs I could compare geohashs of other places (in firestore) to check if they are in the the range or not. At this point there are wrong results. For example a place is in 600 mile distance but at 320 mile the result is already its in range. Thats wrong.
I decode the upper and lower again to the lat, lon values and compute the distance just on the coordinates (not geohashs) to show you the difference and that the distance is nearly doubled up. Sometimes its higher as factor 2, sometimes lower. Its different if I chose different distance values.
In my opinion if I put in 10 miles distance, encode the ranges and decode them to recalculate the distance it should stay 10 miles. Maybe some small difference because of roundings but no 20.37, thats twice as high as expected.
The question is now, why is the distance after decoding higher as before? Did I miss some calculation anywhere?
So I can't check exactly if positions are in the given range of or not.
I know in client code I could work on coordinates only but the places are given as geohashs in firestore and I have to fetch the places which are in the range directly from there without filtering on client side. So thats not an option and I think it could work if I had the right Math. Thank you.
GeoTester.js:
import geohash from "ngeohash";
export const GeoTester = () => {
const getDistance = (startLat, startLon, targetLat, targetLon) => {
const dLat = (startLat-targetLat) * Math.PI / 180;
const dLon = (startLon-targetLon) * Math.PI / 180;
const lat1 = targetLat * Math.PI / 180;
const lat2 = startLat * Math.PI / 180;
const a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
const d = 6371 * c;
return d;
}
const getGeohashRange = (latitude, longitude, distance) => {
const latDeg = 0.0144927536231884; // degrees latitude per mile
const lonDeg = 0.0181818181818182; // degrees longitude per mile
const lowerLat = latitude - latDeg * distance;
const lowerLon = longitude - lonDeg * distance;
const upperLat = latitude + latDeg * distance;
const upperLon = longitude + lonDeg * distance;
const lowerGeohash = geohash.encode(lowerLat, lowerLon);
const upperGeohash = geohash.encode(upperLat, upperLon);
return { lowerGeohash, upperGeohash };
}
const geoTest = () => {
const startLat = 52.00991050587265;
const startLon = 4.708180705155277;
const startGeohash = geohash.encode(startLat, startLon);
const distance = 10; // miles
const { lowerGeohash, upperGeohash } = getGeohashRange(startLat, startLon, distance);
const lowerDecoded = geohash.decode(lowerGeohash);
const upperDecoded = geohash.decode(upperGeohash);
const distLower = getDistance(startLat, startLon, lowerDecoded.latitude, lowerDecoded.longitude);
const distUpper = getDistance(startLat, startLon, upperDecoded.latitude, upperDecoded.longitude);
const diffLower = (distLower / distance);
const diffUpper = (distUpper / distance);
console.log('distance and diff lower', distLower, diffLower);
console.log('distance and diff upper', distUpper, diffUpper);
}
return {
geoTest
};
}
App.js:
import React from 'react';
import {View, Button} from 'react-native';
import {GeoTester} from '../helpers/GeoTester';
const App = () => {
const { geoTest } = GeoTester();
return (
<View>
<Button title="Test" onPress={() => geoTest()} />
</View>
);
}
export default App;
console.log output:
distance and diff lower 20.374469782722997 2.0374469782722997
distance and diff upper 20.347694449540903 2.0347694449540903