Source: GeohashStream.js

/** ****************************************************************************************************
 * File: GeohashStream.js
 * Project: geohash
 * @author Nick Soggin <iSkore@users.noreply.github.com> on 11-Feb-2019
 *******************************************************************************************************/
'use strict';

const
	{ Readable } = require( 'stream' );

const
	getBBoxStartingPoint = require( './getBBoxStartingPoint' ),
	neighbor             = require( './neighbor' ),
	{
		isNumber
	}                    = require( './utils' );

class GeohashStream extends Readable
{
	/**
	 * GeohashStream
	 *
	 * extends Readable stream
	 *
	 * @param {object} opts - configuration object
	 * @param {number} opts.minLng - bbox min longitude
	 * @param {number} opts.minLat - bbox min latitude
	 * @param {number} opts.maxLng - bbox max longitude
	 * @param {number} opts.maxLat - bbox max latitude
	 * @param {number} opts.precision - geohash precision
	 */
	constructor( opts )
	{
		super();

		if ( !opts ) {
			throw new Error(
				'[GeohashStream] requires options defining bbox (minLng, minLat, maxLng, maxLat) and precision'
			);
		}
		else if (
			!isNumber( opts.minLng ) || !isNumber( opts.minLat ) ||
			!isNumber( opts.maxLng ) || !isNumber( opts.maxLat )
		) {
			throw new Error( '[GeohashStream] minLng, minLat, maxLng, and maxLat must be numbers' );
		}
		else if ( !isNumber( opts.precision ) ) {
			throw new Error( '[GeohashStream] precision must be a number' );
		}

		this.startingBBox = getBBoxStartingPoint(
			opts.minLng,
			opts.minLat,
			opts.maxLng,
			opts.maxLat,
			opts.precision
		);

		this._x = -1;
		this._y = -1;
		this.x  = this.startingBBox.lngStep;
		this.y  = this.startingBBox.latStep;

		this._nextChunk = null;
	}

	nextChunk()
	{
		if ( this._x === -1 && this._y === -1 ) {
			this._nextChunk = this.startingBBox.hashSouthWest;
			this.lastNorth  = this._nextChunk;
			this.lastEast   = this._nextChunk;

			this._x++;
			this._y++;

			return this._nextChunk;
		}
		else if ( this._x < this.x ) {
			this._nextChunk = neighbor( this.lastEast, 'e' );
			this.lastEast   = this._nextChunk;
			this._x++;

			return this._nextChunk;
		}
		else if ( this._y < this.y ) {
			this._x = 0;

			this._nextChunk = neighbor( this.lastNorth, 'n' );
			this.lastNorth  = this._nextChunk;
			this.lastEast   = this._nextChunk;
			this._y++;

			return this._nextChunk;
		}

		return null;
	}

	_read()
	{
		let chunk;
		while ( ( chunk = this.nextChunk() ) !== null ) {
			this.push( chunk );
		}

		this.push( null );
	}
}

module.exports = GeohashStream;