import { geoAlbers , geoConicEqualArea , GeoStream , geoStream } from "d3-geo" ; function noop ( ) { } let x0 = Infinity ; let y0 = x0 ; let x1 = - x0 ; let y1 = x1 ; function boundsPoint ( x , y ) { if ( x < x0 ) x0 = x ; if ( x > x1 ) x1 = x ; if ( y < y0 ) y0 = y ; if ( y > y1 ) y1 = y ; } const boundsStream = { point : boundsPoint , lineStart : noop , lineEnd : noop , polygonStart : noop , polygonEnd : noop , result ( ) { const bounds = [ [ x0 , y0 ] , [ x1 , y1 ] ] ; x1 = y1 = - ( y0 = x0 = Infinity ) ; return bounds ; } } ; function fitExtent ( projection , extent , object ) { const w = extent [ 1 ] [ 0 ] - extent [ 0 ] [ 0 ] ; const h = extent [ 1 ] [ 1 ] - extent [ 0 ] [ 1 ] ; const clip = projection . clipExtent && projection . clipExtent ( ) ; projection . scale ( 150 ) . translate ( [ 0 , 0 ] ) ; if ( clip != null ) projection . clipExtent ( null ) ; geoStream ( object , projection . stream ( boundsStream ) ) ; const b = boundsStream . result ( ) ; const k = Math . min ( w / ( b [ 1 ] [ 0 ] - b [ 0 ] [ 0 ] ) , h / ( b [ 1 ] [ 1 ] - b [ 0 ] [ 1 ] ) ) ; const x = + extent [ 0 ] [ 0 ] + ( w - k * ( b [ 1 ] [ 0 ] + b [ 0 ] [ 0 ] ) ) / 2 ; const y = + extent [ 0 ] [ 1 ] + ( h - k * ( b [ 1 ] [ 1 ] + b [ 0 ] [ 1 ] ) ) / 2 ; if ( clip != null ) projection . clipExtent ( clip ) ; return projection . scale ( k * 150 ) . translate ( [ x , y ] ) ; } function fitSize ( projection , size , object ) { return fitExtent ( projection , [ [ 0 , 0 ] , size ] , object ) ; } export class Geohash { private static base32 = "0123456789bcdefghjkmnpqrstuvwxyz" ; param { number } lat param { number } lon param { number } precision returns { string } throws example encode ( lat , lon , precision ) { if ( typeof precision === "undefined" ) { for ( let p = 1 ; p <= 12 ; p ++ ) { const hash = this . encode ( lat , lon , p ) ; const posn = this . decode ( hash ) ; if ( posn . lat === lat && posn . lon === lon ) return hash ; } precision = 12 ; } lat = Number ( lat ) ; lon = Number ( lon ) ; precision = Number ( precision ) ; if ( isNaN ( lat ) || isNaN ( lon ) || isNaN ( precision ) ) throw new Error ( "Invalid geohash" ) ; let idx = 0 ; let bit = 0 ; let evenBit = true ; let geohash = "" ; let latMin = - 90 ; let latMax = 90 ; let lonMin = - 180 ; let lonMax = 180 ; while ( geohash . length < precision ) { if ( evenBit ) { const lonMid = ( lonMin + lonMax ) / 2 ; if ( lon > lonMid ) { idx = idx * 2 + 1 ; lonMin = lonMid ; } else { idx = idx * 2 ; lonMax = lonMid ; } } else { const latMid = ( latMin + latMax ) / 2 ; if ( lat > latMid ) { idx = idx * 2 + 1 ; latMin = latMid ; } else { idx = idx * 2 ; latMax = latMid ; } } evenBit = ! evenBit ; if ( ++ bit === 5 ) { geohash += Geohash . base32 . charAt ( idx ) ; bit = 0 ; idx = 0 ; } } return geohash ; } param { string } geohash returns { { lat : number , lon : number } } throws example decode ( geohash ) { const bounds = this . bounds ( geohash ) ; const latMin = bounds . sw . lat ; const lonMin = bounds . sw . lon ; const latMax = bounds . ne . lat ; const lonMax = bounds . ne . lon ; let lat = ( latMin + latMax ) / 2 ; let lon = ( lonMin + lonMax ) / 2 ; lat = Number ( lat . toFixed ( Math . floor ( 2 - Math . log ( latMax - latMin ) / Math . LN10 ) ) ) ; lon = Number ( lon . toFixed ( Math . floor ( 2 - Math . log ( lonMax - lonMin ) / Math . LN10 ) ) ) ; return { lat , lon } ; } param { string } geohash returns { { sw : { lat : number , lon : number } , ne : { lat : number , lon : number } } } throws bounds ( geohash ) { if ( geohash . length === 0 ) throw new Error ( "Invalid geohash" ) ; geohash = geohash . toLowerCase ( ) ; let evenBit = true ; let latMin = - 90 ; let latMax = 90 ; let lonMin = - 180 ; let lonMax = 180 ; for ( let i = 0 ; i < geohash . length ; i ++ ) { const chr = geohash . charAt ( i ) ; const idx = Geohash . base32 . indexOf ( chr ) ; if ( idx === - 1 ) throw new Error ( "Invalid geohash" ) ; for ( let n = 4 ; n >= 0 ; n -- ) { const bitN = idx >> n & 1 ; if ( evenBit ) { const lonMid = ( lonMin + lonMax ) / 2 ; if ( bitN === 1 ) { lonMin = lonMid ; } else { lonMax = lonMid ; } } else { const latMid = ( latMin + latMax ) / 2 ; if ( bitN === 1 ) { latMin = latMid ; } else { latMax = latMid ; } } evenBit = ! evenBit ; } } const bounds = { sw : { lat : latMin , lon : lonMin } , ne : { lat : latMax , lon : lonMax } } ; return bounds ; } param geohash param direction returns { string } throws adjacent ( geohash , direction ) { geohash = geohash . toLowerCase ( ) ; direction = direction . toLowerCase ( ) ; if ( geohash . length === 0 ) throw new Error ( "Invalid geohash" ) ; if ( "nsew" . indexOf ( direction ) === - 1 ) throw new Error ( "Invalid direction" ) ; const neighbour = { n : [ "p0r21436x8zb9dcf5h7kjnmqesgutwvy" , "bc01fg45238967deuvhjyznpkmstqrwx" ] , s : [ "14365h7k9dcfesgujnmqp0r2twvyx8zb" , "238967debc01fg45kmstqrwxuvhjyznp" ] , e : [ "bc01fg45238967deuvhjyznpkmstqrwx" , "p0r21436x8zb9dcf5h7kjnmqesgutwvy" ] , w : [ "238967debc01fg45kmstqrwxuvhjyznp" , "14365h7k9dcfesgujnmqp0r2twvyx8zb" ] } ; const border = { n : [ "prxz" , "bcfguvyz" ] , s : [ "028b" , "0145hjnp" ] , e : [ "bcfguvyz" , "prxz" ] , w : [ "0145hjnp" , "028b" ] } ; const lastCh = geohash . slice ( - 1 ) ; let parent = geohash . slice ( 0 , - 1 ) ; const type = geohash . length % 2 ; if ( border [ direction ] [ type ] . indexOf ( lastCh ) !== - 1 && parent !== "" ) { parent = this . adjacent ( parent , direction ) ; } return parent + Geohash . base32 . charAt ( neighbour [ direction ] [ type ] . indexOf ( lastCh ) ) ; } param { string } geohash returns { { n , ne , e , se , s , sw , w , nw : string } } throws neighbours ( geohash ) { return { n : this . adjacent ( geohash , "n" ) , ne : this . adjacent ( this . adjacent ( geohash , "n" ) , "e" ) , e : this . adjacent ( geohash , "e" ) , se : this . adjacent ( this . adjacent ( geohash , "s" ) , "e" ) , s : this . adjacent ( geohash , "s" ) , sw : this . adjacent ( this . adjacent ( geohash , "s" ) , "w" ) , w : this . adjacent ( geohash , "w" ) , nw : this . adjacent ( this . adjacent ( geohash , "n" ) , "w" ) } ; } contained ( w , n , e , s , precision ) { if ( isNaN ( n ) || n >= 90 ) n = 89 ; if ( isNaN ( e ) || e > 180 ) e = 180 ; if ( isNaN ( s ) || s <= - 90 ) s = - 89 ; if ( isNaN ( w ) || w < - 180 ) w = - 180 ; precision = precision || 1 ; const geoHashNW = this . encode ( n , w , precision ) ; const geoHashNE = this . encode ( n , e , precision ) ; const geoHashSE = this . encode ( s , e , precision ) ; let currRowHash = geoHashNW ; let col = 0 ; let maxCol = - 1 ; const geoHashes = [ geoHashNW , geoHashSE ] ; let currHash = this . adjacent ( geoHashNW , "e" ) ; while ( currHash !== geoHashSE ) { geoHashes . push ( currHash ) ; ++ col ; if ( currHash === geoHashNE || maxCol === col ) { maxCol = col + 1 ; col = 0 ; currHash = this . adjacent ( currRowHash , "s" ) ; currRowHash = currHash ; } else { currHash = this . adjacent ( currHash , "e" ) ; } } return geoHashes ; } calculateWidthDegrees ( n ) { let a ; if ( n % 2 === 0 ) a = - 1 ; else a = - 0.5 ; const result = 180 / Math . pow ( 2 , 2.5 * n + a ) ; return result ; } width ( n ) { const parity = n % 2 ; return 180 / ( 2 ^ ( ( ( 5 * n + parity ) / 2 ) - 1 ) ) ; } } function multiplex ( streams ) { const n = streams . length ; return { point ( x , y ) { let i = - 1 ; while ( ++ i < n ) streams [ i ] . point ( x , y ) ; } , sphere ( ) { let i = - 1 ; while ( ++ i < n ) streams [ i ] . sphere ( ) ; } , lineStart ( ) { let i = - 1 ; while ( ++ i < n ) streams [ i ] . lineStart ( ) ; } , lineEnd ( ) { let i = - 1 ; while ( ++ i < n ) streams [ i ] . lineEnd ( ) ; } , polygonStart ( ) { let i = - 1 ; while ( ++ i < n ) streams [ i ] . polygonStart ( ) ; } , polygonEnd ( ) { let i = - 1 ; while ( ++ i < n ) streams [ i ] . polygonEnd ( ) ; } } ; } export function albersUsaPr ( ) { let cache ; let cacheStream ; const ε = 1e-6 ; const lower48 = geoAlbers ( ) ; let lower48Point ; const alaska = geoConicEqualArea ( ) . rotate ( [ 154 , 0 ] ) . center ( [ - 2 , 58.5 ] ) . parallels ( [ 55 , 65 ] ) ; let alaskaPoint ; const hawaii = geoConicEqualArea ( ) . rotate ( [ 157 , 0 ] ) . center ( [ - 3 , 19.9 ] ) . parallels ( [ 8 , 18 ] ) ; let hawaiiPoint ; const puertoRico = geoConicEqualArea ( ) . rotate ( [ 66 , 0 ] ) . center ( [ 0 , 18 ] ) . parallels ( [ 8 , 18 ] ) ; let puertoRicoPoint ; let point ; const pointStream = { point : ( x , y ) => { point = [ x , y ] ; } } as GeoStream ; const albersUsa = function ( coordinates ) { const x = coordinates [ 0 ] ; const y = coordinates [ 1 ] ; point = null ; ( lower48Point . point ( x , y ) , point ) || ( alaskaPoint . point ( x , y ) , point ) || ( hawaiiPoint . point ( x , y ) , point ) || ( puertoRicoPoint . point ( x , y ) , point ) ; return point ; } ; albersUsa . invert = function ( coordinates ) { const k = lower48 . scale ( ) ; const t = lower48 . translate ( ) ; const x = ( coordinates [ 0 ] - t [ 0 ] ) / k ; const y = ( coordinates [ 1 ] - t [ 1 ] ) / k ; return ( y >= 0.120 && y < 0.234 && x >= - 0.425 && x < - 0.214 ? alaska : y >= 0.166 && y < 0.234 && x >= - 0.214 && x < - 0.115 ? hawaii : y >= 0.204 && y < 0.234 && x >= 0.320 && x < 0.380 ? puertoRico : lower48 ) . invert ( coordinates ) ; } ; albersUsa . stream = function ( stream ) { return cache && cacheStream === stream ? cache : cache = multiplex ( [ lower48 . stream ( cacheStream = stream ) , alaska . stream ( stream ) , hawaii . stream ( stream ) , puertoRico . stream ( stream ) ] ) ; } ; albersUsa . precision = function ( _ ) { if ( ! arguments . length ) return lower48 . precision ( ) ; lower48 . precision ( _ ) ; alaska . precision ( _ ) ; hawaii . precision ( _ ) ; puertoRico . precision ( _ ) ; return reset ( ) ; } ; albersUsa . scale = function ( _ ) { if ( ! arguments . length ) return lower48 . scale ( ) ; lower48 . scale ( _ ) ; alaska . scale ( _ * 0.35 ) ; hawaii . scale ( _ ) ; puertoRico . scale ( _ ) ; return albersUsa . translate ( lower48 . translate ( ) ) ; } ; albersUsa . translate = function ( _ ) { if ( ! arguments . length ) return lower48 . translate ( ) ; const k = lower48 . scale ( ) ; const x = + _ [ 0 ] ; const y = + _ [ 1 ] ; lower48Point = lower48 . translate ( _ ) . clipExtent ( [ [ x - 0.455 * k , y - 0.238 * k ] , [ x + 0.455 * k , y + 0.238 * k ] ] ) . stream ( pointStream ) ; alaskaPoint = alaska . translate ( [ x - 0.307 * k , y + 0.201 * k ] ) . clipExtent ( [ [ x - 0.425 * k + ε , y + 0.120 * k + ε ] , [ x - 0.214 * k - ε , y + 0.234 * k - ε ] ] ) . stream ( pointStream ) ; hawaiiPoint = hawaii . translate ( [ x - 0.205 * k , y + 0.212 * k ] ) . clipExtent ( [ [ x - 0.214 * k + ε , y + 0.166 * k + ε ] , [ x - 0.115 * k - ε , y + 0.234 * k - ε ] ] ) . stream ( pointStream ) ; puertoRicoPoint = puertoRico . translate ( [ x + 0.350 * k , y + 0.224 * k ] ) . clipExtent ( [ [ x + 0.320 * k , y + 0.204 * k ] , [ x + 0.380 * k , y + 0.234 * k ] ] ) . stream ( pointStream ) ; return reset ( ) ; } ; albersUsa . fitExtent = function ( extent , object ) { return fitExtent ( albersUsa , extent , object ) ; } ; albersUsa . fitSize = function ( size , object ) { return fitSize ( albersUsa , size , object ) ; } ; function reset ( ) { cache = cacheStream = null ; return albersUsa ; } return albersUsa . scale ( 1070 ) ; }