All files / geonet-geohash/src encode.js

100% Statements 42/42
100% Branches 15/15
100% Functions 1/1
100% Lines 42/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106                              1x           1x                                 44x 44x 44x   44x 1x   43x 1x   42x 1x     41x     41x 41x 41x 41x 41x 41x 41x 41x 41x   41x 1175x   602x 602x 276x 276x     326x 326x         573x 573x 309x 309x     264x 264x       1175x   1175x   235x 235x 235x       41x     1x  
/** ****************************************************************************************************
 * 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;