gis-utils
Version:
Some GIS utility functions I have used in multiple projects.
191 lines (154 loc) • 5.22 kB
JavaScript
;
/**
* Helper function - converts degrees to radians
*
* @param {number} degrees
*
* @return {number}
*/
function deg2rad(degrees) {
return degrees * Math.PI / 180;
}
/**
* Helper function - inverse of above
*
* @param {number} angle
*
* @return {number}
*/
function rad2deg(angle) {
return angle * 57.29577951308232; // angle / Math.PI * 180
}
/**
* Returns the distance in km on a sphere between fromLocation and toLocation
* Expects Objects like: { lat: 1.100, lng: 59.00 }
*
* @param {Object} fromLocation
* @param {Object} toLocation
*
* @return {number}
*/
function haversineKm(fromLocation, toLocation) {
var radius_of_earth_kms = 6371;
var dLat = deg2rad(fromLocation.lat - toLocation.lat);
var dLng = deg2rad(fromLocation.lng - toLocation.lng);
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLng / 2) * Math.sin(dLng / 2) * Math.cos(deg2rad(fromLocation.lat)) * Math.cos(deg2rad(toLocation.lat));
var c = 2 * Math.asin(Math.sqrt(a));
return radius_of_earth_kms * c;
}
/**
* Finds the radius of the circle which would fully enclose the current map
*
* @param {google.maps.LatLngBounds} bounds The google maps bounds object
*
* @return {number}
*/
function calculateRadiusFromMapBounds(bounds) {
// get the corners
var pointNE = bounds.getNorthEast();
var pointNELatLng = {
lat: pointNE.lat(),
lng: pointNE.lng()
};
var pointSW = bounds.getSouthWest();
var pointSWLatLng = {
lat: pointSW.lat(),
lng: pointSW.lng()
};
// calculate the diagonal
var diagonalDistanceKm = haversineKm(pointNELatLng, pointSWLatLng);
// radius is half the diagonal
return diagonalDistanceKm / 2;
}
/**
* Returns a an object for the bounds NE/SW bounds which fits inside a circle
* at point center with radius radius (in m)
*
* @param {number} radius The radius in m
* @param {Object} center object representing the center
*
* @return {Object}
*/
function getNESWBoundsFromRadiusAndCenter(radius, center) {
var earthRadiusM = 6378137;
var pointNE = computeDestinationPoint(center.lat, center.lng, radius, 45, earthRadiusM);
var pointSW = computeDestinationPoint(center.lat, center.lng, radius, 225, earthRadiusM);
return {
'ne': pointNE,
'sw': pointSW
};
}
/**
* As above, but for NW and SE
*
* @param {number} radius The radius in m
* @param {Object} center object representing the center
*
* @return {Object}
*/
function getNWSEBoundsFromRadiusAndCenter(radius, center) {
var earthRadiusM = 6378137;
var pointNW = computeDestinationPoint(center.lat, center.lng, radius, 315, earthRadiusM);
var pointSE = computeDestinationPoint(center.lat, center.lng, radius, 135, earthRadiusM);
return {
'nw': pointNW,
'se': pointSE
};
}
/**
* Computes the destination point given an initial point, a distance
* and a bearing
*
* See http://www.movable-type.co.uk/scripts/latlong.html for the original code
*
* @param {number} lat latitude of the initial point in degree
* @param {number} lon longitude of the initial point in degree
* @param {number} distance distance to go from the initial point in meter
* @param {number} bearing bearing in degree of the direction to go, e.g.
* 0 = north, 180 = south
* @param {number} radius mean radius of the earth (in meters)
*
* @return object { latitude: destLat longitude: destLng }
*/
function computeDestinationPoint(lat, lon, distance, bearing, radius) {
var delta = Number(distance) / radius; // angular distance in radians
var theta = deg2rad(Number(bearing));
var phi1 = deg2rad(Number(lat));
var lambda1 = deg2rad(Number(lon));
var phi2 = Math.asin(Math.sin(phi1) * Math.cos(delta) + Math.cos(phi1) * Math.sin(delta) * Math.cos(theta));
var lambda2 = lambda1 + Math.atan2(Math.sin(theta) * Math.sin(delta) * Math.cos(phi1), Math.cos(delta) - Math.sin(phi1) * Math.sin(phi2));
// normalise to -180..+180°
lambda2 = (lambda2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
return { lat: rad2deg(phi2), lng: rad2deg(lambda2) };
}
/**
* Determines if a point is within a NE/SW boundary
* @param {Object} point
* @param {Object} boundary
*
* @return {Boolean}
*/
function isLatAndLngInRectBoundary(point, boundary) {
// handling for the rare occasion where we might go from 359deg to 0deg on
// international date line
var eastBound = point.lng < boundary.NE.lng;
var westBound = point.lng > boundary.SW.lng;
var inLng;
if (boundary.NE.lng < boundary.SW.lng) {
inLng = eastBound || westBound;
} else {
inLng = eastBound && westBound;
}
var inLat = point.lat > boundary.SW.lat && point.lat < boundary.NE.lat;
return inLat && inLng;
}
module.exports = {
deg2rad: deg2rad,
rad2deg: rad2deg,
haversineKm: haversineKm,
calculateRadiusFromMapBounds: calculateRadiusFromMapBounds,
getNESWBoundsFromRadiusAndCenter: getNESWBoundsFromRadiusAndCenter,
getNWSEBoundsFromRadiusAndCenter: getNWSEBoundsFromRadiusAndCenter,
computeDestinationPoint: computeDestinationPoint,
isLatAndLngInRectBoundary: isLatAndLngInRectBoundary
};