geo-box
Version:
Create, manage and analyze geographical boxes (including geohashes) to get positional, size and distortion information.
302 lines (249 loc) • 7.43 kB
JavaScript
var ngeohash = require('ngeohash');
var radiusOfTheEarth = 6378100;
var aDegreeInRadian = (2*Math.PI/360);
// how many meters is 1 (base) degree of the earth
var aDegreeOfTheEarth = aDegreeInRadian * radiusOfTheEarth;
// var intRadiiMap = generateIntRadiusMap();
// function generateIntRadiusMapForLat(lat){
// var latAttributes = {},
// geoHashBox = [],
// map = [],
// fullMap = [];
// for(var i=2; i<54; i+=2){
// geoHashBox = geohashBoxDegreeSize(lat, 50, i);
// latAttributes = boxAttributesAtLat(geoHashBox.degWidth, geoHashBox.degHeight, lat);
// fullMap.push(latAttributes);
// map.push(latAttributes.boxGeneralSize);
// }
// return map;
// }
// function bitDepthForRadiusAtLat(radius, lat){
// var mapForLat = intRadiiMap[Math.round(lat) + 90];
// // console.log("Length of Map", mapForLat.length);
// for(var i=mapForLat.length-1; i > -1; i--){
// // console.log("Position:", i, "| Avg Box Size:", mapForLat[i], radius - mapForLat[i], mapForLat[i-1] - radius);
// if(radius - mapForLat[i] < mapForLat[i-1] - radius){
// return ((i*2)+2);
// }
// }
// return 2;
// }
// function generateIntRadiusMap(){
// var map = [];
// for(var i=-90; i<91; i++){
// map.push(generateIntRadiusMapForLat(i));
// }
// return map;
// }
function getMeterWidthAtLat(degDelta, lat){
return aDegreeOfTheEarth * Math.cos(lat*aDegreeInRadian) * degDelta;
}
function getMeterHeightAtLon(degDelta){
return aDegreeOfTheEarth * degDelta;
}
// how many degrees is latWidth meters at a certain latitude
function getDegreeWidthOfWidthAtLat(latWidth, lat){
return latWidth/getMeterWidthAtLat(1, lat);
}
function getDegreeHeightOfHeightAtLon(lonHeight){
return lonHeight/aDegreeOfTheEarth;
}
function getBoxDistortionAtLat(lat){
return getDegreeWidthOfWidthAtLat(50, lat)/getDegreeHeightOfHeightAtLon(50);
}
function convertBoxFromDegreesToMeters(degWidth, degHeight, lat){
return {
width: getMeterWidthAtLat(degWidth, lat),
height: getMeterHeightAtLon(degHeight)
};
}
function convertBoxFromMetersToDegrees(width, height, lat){
return {
degWidth: getDegreeWidthOfWidthAtLat(width, lat),
degHeight: getDegreeHeightOfHeightAtLon(height)
};
}
function geohashPhysicalDistortionAtLat(lat, lon, bitDepth){
var geoHashBox = geohashBoxDegreeSize(lat, lon, bitDepth);
var latAttributes = boxAttributesAtLat(geoHashBox.degWidth, geoHashBox.degHeight, lat);
return latAttributes.boxDistortion;
}
function geohashBoxDegreeSize(lat, lon, bitDepth){
var hash = ngeohash.encode_int(lat, lon || 45, bitDepth || 52);
var decode = ngeohash.decode_bbox_int(hash, bitDepth || 52);
return {
degHeight: decode[2] - decode[0],
degWidth: decode[3] - decode[1]
};
}
function boxAttributesAtLat(degreeWidth, degreeHeight, lat){
var latHeightInMeters = getMeterHeightAtLon(degreeHeight);
var lonWidthInMeters = getMeterWidthAtLat(degreeWidth, lat);
var distortion = lonWidthInMeters/latHeightInMeters;
return {
height: latHeightInMeters,
width: lonWidthInMeters,
boxGeneralSize: (latHeightInMeters+lonWidthInMeters)/2,
boxDistortion: distortion
};
}
function box(lat, lon, width, height, units){
return new Box(lat, lon, width, height, units);
}
var Box = function(lat, lon, width, height, units){
units = units || "degrees";
var degWidth = 0,
degHeight = 0;
if(units === "degrees"){
degWidth = width;
degHeight = height;
width = getMeterWidthAtLat(degWidth, lat);
height = getMeterHeightAtLon(degHeight);
}
else{
degWidth = getDegreeWidthOfWidthAtLat(width, lat);
degHeight = getDegreeHeightOfHeightAtLon(height);
}
Object.defineProperty(this, "baseUnit", {
enumerable: true,
get: function () {
return units;
},
set: function(newBaseUnit){
units = newBaseUnit;
}
});
Object.defineProperty(this, "lat", {
enumerable: true,
get: function () {
return lat;
},
set: function(newLat){
if (this.baseUnit === 'degrees'){
lat = newLat;
width = getMeterWidthAtLat(degWidth, lat);
}
else{
lat = newLat;
degWidth = getDegreeWidthOfWidthAtLat(width, lat);
}
}
});
Object.defineProperty(this, "lon", {
enumerable: true,
get: function () {
return lon;
},
set: function(newLon){
lon = newLon;
}
});
Object.defineProperty(this, "center", {
enumerable: true,
get: function () {
return [lat + (degHeight/2), lon + (degWidth/2)];
},
set: function(center){
lat = center[0] - (degHeight/2);
lon = center[1] - (degWidth/2);
}
});
Object.defineProperty(this, "width", {
enumerable: true,
get: function () {
return width;
},
set: function(meterWidth){
width = meterWidth;
degWidth = getDegreeWidthOfWidthAtLat(meterWidth, lat);
}
});
Object.defineProperty(this, "height", {
enumerable: true,
get: function () {
return height;
},
set: function(meterHeight){
height = meterHeight;
degHeight = getDegreeHeightOfHeightAtLon(meterHeight);
}
});
Object.defineProperty(this, "diagonal", {
enumerable: true,
get: function () {
return Math.sqrt(width*width + height*height);
}
});
Object.defineProperty(this, "distortion", {
enumerable: true,
get: function () {
return width/height;
}
});
Object.defineProperty(this, "latitudinalDegreeDistortion", {
enumerable: true,
get: function () {
return (height/degHeight)/(width/degWidth);
}
});
Object.defineProperty(this, "degWidth", {
enumerable: true,
get: function () {
return degWidth;
},
set: function(newDegWidth){
degWidth = newDegWidth;
width = getMeterWidthAtLat(newDegWidth, lat);
}
});
Object.defineProperty(this, "degHeight", {
enumerable: true,
get: function () {
return degHeight;
},
set: function(newDegHeight){
degHeight = newDegHeight;
height = getMeterHeightAtLon(newDegHeight);
}
});
};
Object.defineProperty(Box.prototype, "fromGeohash", {
enumerable: false,
value: function(geohash, bitDepth){
var decode = [];
if(typeof geohash === 'number'){
decode = ngeohash.decode_bbox_int(geohash, bitDepth || 52);
}
else if(typeof geohash === 'string'){
decode = ngeohash.decode_bbox(geohash);
}
return new Box(decode[0], decode[1], decode[3] - decode[1], decode[2] - decode[0], "degrees");
}
});
Object.defineProperty(Box.prototype, "setBaseUnit", {
enumerable: false,
value: function(units){
console.log(units);
if(units === 'degrees'){
this.baseUnit = 'degrees';
console.log("is degrees");
console.log(this.baseUnit);
}
else{
this.baseUnit = 'meters';
}
return this;
}
});
var geoBoxInfo = {
// 'bitDepthForRadiusAtLat': bitDepthForRadiusAtLat,
'geohashPhysicalDistortionAtLat': geohashPhysicalDistortionAtLat,
'geohashBoxInDegrees': geohashBoxDegreeSize,
'geohashBoxInMeters': boxAttributesAtLat,
'convertBoxFromDegreesToMeters': convertBoxFromDegreesToMeters,
'convertBoxFromMetersToDegrees': convertBoxFromMetersToDegrees,
'physicalDistortionAtLat': getBoxDistortionAtLat,
'box': box,
'Box': Box
};
module.exports = geoBoxInfo;