UNPKG

gis-utils

Version:

Some GIS utility functions I have used in multiple projects.

191 lines (154 loc) 5.22 kB
'use strict'; /** * 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 };