geographic-math
Version:
A set of useful geo functions
51 lines (40 loc) • 2.09 kB
JavaScript
const getAngle = (center, { lat, lon }) => Math.atan2(lon - center.lon, lat - center.lat);
const getCenter = (polygon) => {
const sum = polygon.reduce((acc, { lat, lon }) => ({ lat: acc.lat + lat, lon: acc.lon + lon }), { lat: 0, lon: 0 });
return { lat: sum.lat / polygon.length, lon: sum.lon / polygon.length };
};
const getCartesianDistance = ({ lat: latTarget, lon: lonTarget }, { lat, lon }) => Math.sqrt(
(lat - latTarget) * (lat - latTarget) + (lon - lonTarget) * (lon - lonTarget)
);
const getLine = ({ lat: lat1, lon: lon1 }, { lat: lat2, lon: lon2 }) => ([
lon1 - lon2,
lat2 - lat1,
lat1 * lon2 - lat2 * lon1,
]);
const getLinesIntersection = ([a1, b1, c1], [a2, b2, c2]) => ({
lat: -1 * (c1 * b2 - c2 * b1) / (a1 * b2 - a2 * b1),
lon: -1 * (a1 * c2 - a2 * c1) / (a1 * b2 - a2 * b1),
});
const getNeighbours = (targetAngle, coordinates) => {
const biggerIndex = coordinates.findIndex(({ angle }) => angle > targetAngle);
return [coordinates[(biggerIndex - 1 + coordinates.length) % coordinates.length], coordinates[biggerIndex]];
};
const getDistance = ({ lat: lat1, lon: lon1 }, { lat: lat2, lon: lon2 }) => {
const denominator = 57.2958;
const x = Math.sin(lat1 / denominator) * Math.sin(lat2 / denominator)
+ Math.cos(lat1 / denominator) * Math.cos(lat2 / denominator) * Math.cos((lon2 - lon1)
/ denominator);
return 3958.75 * Math.atan(Math.sqrt(1 - x * x) / x) * 1.609;
};
const isInside = (position, polygon) => {
const center = getCenter(polygon);
const coordinates = polygon.map(position => Object.assign({}, position, { angle: getAngle(center, position)}));
coordinates.sort(({ angle: angleA }, { angle: angleB }) => angleA - angleB);
const neighbours = getNeighbours(getAngle(center, position), coordinates);
const intersection = getLinesIntersection(getLine(center, position), getLine(neighbours[0], neighbours[1]));
return getCartesianDistance(center, intersection) >= getCartesianDistance(center, position);
};
module.exports = {
getDistance,
isInside,
};