/** ****************************************************************************************************
* File: encode.js
* Project: geohash
* @author Nick Soggin <iSkore@users.noreply.github.com> on 19-Feb-2019
*******************************************************************************************************/
'use strict';
const
{
BASE32,
ENCODE_AUTO,
MIN_LNG,
MIN_LAT,
MAX_LNG,
MAX_LAT
} = require( './variables' );
const
{
determinePrecision,
isNumber
} = require( './utils' );
/**
* encode
* Encodes latitude/longitude to geohash, either to specified precision or to automatically
* evaluated precision.
*
* @param {number} lng - Longitude in degrees.
* @param {number} lat - Latitude in degrees.
* @param {number} [precision=ENCODE_AUTO] - Number of characters in resulting geohash.
* @returns {string} Geohash of supplied latitude/longitude.
* @throws Invalid geohash.
*
* @example
* const geohash = Geohash.encode(52.205, 0.119, 7); // geohash: 'u120fxw'
*/
function encode( lng, lat, precision = ENCODE_AUTO ) {
lng = +lng;
lat = +lat;
precision = +precision;
if ( !isNumber( lng ) ) {
throw new Error( 'Invalid value for `lng`' );
}
else if ( !isNumber( lat ) ) {
throw new Error( 'Invalid value for `lat`' );
}
else if ( !isNumber( precision ) ) {
throw new Error( 'Invalid value for `precision`' );
}
precision = determinePrecision( lng, lat, precision );
let
geohash = '',
hash = 0, // index into BASE32 map
bit = 0, // each char holds 5 bits
evenBit = true,
lngMin = MIN_LNG,
latMin = MIN_LAT,
lngMax = MAX_LNG,
latMax = MAX_LAT,
mid = 0;
while ( geohash.length < precision ) {
if ( evenBit ) {
// bisect E-W longitude
mid = ( lngMin + lngMax ) / 2;
if ( lng >= mid ) {
hash = ( hash << 1 ) + 1;
lngMin = mid;
}
else {
hash = hash << 1;
lngMax = mid;
}
}
else {
// bisect N-S latitude
mid = ( latMin + latMax ) / 2;
if ( lat >= mid ) {
hash = ( hash << 1 ) + 1;
latMin = mid;
}
else {
hash = hash << 1;
latMax = mid;
}
}
evenBit = !evenBit;
if ( ++bit === 5 ) {
// 5 bits gives us a character: append it and start over
geohash += BASE32[ hash ];
bit = 0;
hash = 0;
}
}
return geohash;
}
module.exports = encode;