Comparing version 1.0.1 to 1.1.0
@@ -0,1 +1,11 @@ | ||
### 1.1.0 (2020-06-22) | ||
##### New Features | ||
* **validateCoordinates:** add function to validate `LatLngLiteral` ([43039bfb](https://github.com/MichaelSolati/geokit/commit/43039bfba5828f8c4bfe66e922e126df22c7e17c)) | ||
##### Bug Fixes | ||
* **hash:** rewrite `hash` function to address coordinates of 0, 0 ([52ab31ad](https://github.com/MichaelSolati/geokit/commit/52ab31ad8272c56af3c4e88c39b522d39b9c2af7)) | ||
#### 1.0.1 (2020-06-21) | ||
@@ -2,0 +12,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t=t||self).Geokit={})}(this,(function(t){"use strict";const n="0123456789bcdefghjkmnpqrstuvwxyz";function e(t,n){return(n[0]+n[1])/2>t?0:1}function o(t){return t*Math.PI/180}function r(t){const n=[];if(t.lat>90&&n.push("Your latitude is greater than 90°"),t.lat<-90&&n.push("Your latitude is less than -90°"),t.lng>180&&n.push("Your longitude is greater than 180°"),t.lng<-180&&n.push("Your longitude is less than -180°"),0!==n.length)return new Error(n.join(" "))}function s(t,e=!1){let o;if("string"!=typeof t)o="geohash must be a string";else if(0===t.length)o="geohash cannot be the empty string";else for(const e of t)-1===n.indexOf(e)&&(o="geohash cannot contain '"+e+"'");if(void 0===o||e)return!o;throw new Error("Invalid geohash '"+t+"': "+o)}t.decodeHash=function(t){s(t);let n=!0;const e=[-90,90],o=[-180,180],r=t.split("");for(;r.length;){const t=(a=r.shift(),"0123456789bcdefghjkmnpqrstuvwxyz".indexOf(a.toLowerCase()));for(let r=0;r<5;r++){const s=[16,8,4,2,1][r],a=n?o:e,i=(a[0]+a[1])/2;a[t&s?0:1]=i,n=!n}}var a;return{lat:(e[0]+e[1])/2,lng:(o[0]+o[1])/2}},t.distance=function(t,n,e="km"){const s=r(t);if(s instanceof Error)throw new Error("Start coordinates: "+s.message);const a=r(n);if(a instanceof Error)throw new Error("End coordinates: "+a.message);const i="miles"===e.toLowerCase()?3963:6371,h=o(n.lat-t.lat),f=o(n.lng-t.lng),l=o(t.lat),c=o(n.lat),u=Math.sin(h/2)*Math.sin(h/2)+Math.sin(f/2)*Math.sin(f/2)*Math.cos(l)*Math.cos(c);return i*(2*Math.atan2(Math.sqrt(u),Math.sqrt(1-u)))},t.hash=function(t,o=10){const s=r(t);if(s instanceof Error)throw s;let a="";const i=[-90,90],h=[-180,180];for(;a.length<o;){let o=0;for(let n=0;n<5;n++){const r=(5*a.length+n)%2==0,s=r?t.lng:t.lat,f=r?h:i,l=(f[0]+f[1])/2;o=(o<<1)+e(s,f),s>l?f[0]=l:f[1]=l}a+=n.charAt(o)}return a},t.validateHash=s,Object.defineProperty(t,"__esModule",{value:!0})})); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).Geokit={})}(this,(function(t){"use strict";const e="0123456789bcdefghjkmnpqrstuvwxyz";function n(t){return t*Math.PI/180}function o(t,n=!1){let o;if("string"!=typeof t)o="geohash must be a string";else if(0===t.length)o="geohash cannot be the empty string";else for(const n of t)-1===e.indexOf(n)&&(o="geohash cannot contain '"+n+"'");if(void 0===o||n)return!o;throw new Error("Invalid geohash '"+t+"': "+o)}function s(t,e=!1){const n=[];if(t)if(void 0===t.lat)n.push("Latitude must exist on coordinates");else if(void 0===t.lng)n.push("Longitude must exist on coordinates");else{const e=t.lat,o=t.lng;"number"!=typeof e||isNaN(e)?n.push("Latitude must be a number."):e<-90||e>90?n.push("Latitude must be within the range [-90, 90]."):"number"!=typeof o||isNaN(o)?n.push("Longitude must be a number"):(o<-180||o>180)&&n.push("Longitude must be within the range [-180, 180]")}else n.push("Coordinates must exist.");if(n.length>0&&!e)throw new Error("Invalid coordinates: "+n.join(" "));return!n}t.decodeHash=function(t){o(t);let e=!0;const n=[-90,90],s=[-180,180],i=t.split("");for(;i.length;){const t=(r=i.shift(),"0123456789bcdefghjkmnpqrstuvwxyz".indexOf(r.toLowerCase()));for(let o=0;o<5;o++){const i=[16,8,4,2,1][o],r=e?s:n,a=(r[0]+r[1])/2;r[t&i?0:1]=a,e=!e}}var r;return{lat:(n[0]+n[1])/2,lng:(s[0]+s[1])/2}},t.distance=function(t,e,o="km"){s(t),s(e);const i="miles"===o.toLowerCase()?3963:6371,r=n(e.lat-t.lat),a=n(e.lng-t.lng),u=n(t.lat),h=n(e.lat),f=Math.sin(r/2)*Math.sin(r/2)+Math.sin(a/2)*Math.sin(a/2)*Math.cos(u)*Math.cos(h);return i*(2*Math.atan2(Math.sqrt(f),Math.sqrt(1-f)))},t.hash=function(t,n=10){if(s(t),"number"!=typeof n||isNaN(n))throw new Error("Precision must be a number");if(n<=0)throw new Error("Precision must be greater than 0");if(n>22)throw new Error("Precision cannot be greater than 22");if(Math.round(n)!==n)throw new Error("Precision must be an integer");const o=[-90,90],i=[-180,180];let r="",a=0,u=0,h=1;for(;r.length<n;){const n=h?t.lng:t.lat,s=h?i:o,f=(s[0]+s[1])/2;n>f?(a=1+(a<<1),s[0]=f):(a=0+(a<<1),s[1]=f),h=!h,u<4?u++:(u=0,r+=e.charAt(a),a=0)}return r},t.validateCoordinates=s,t.validateHash=o,Object.defineProperty(t,"__esModule",{value:!0})})); |
@@ -12,27 +12,5 @@ 'use strict'; | ||
} | ||
function getBit(point, range) { | ||
const middle = (range[0] + range[1]) / 2; | ||
return middle > point ? 0 : 1; | ||
} | ||
function toRad(degrees) { | ||
return (degrees * Math.PI) / 180; | ||
} | ||
function validateCoordinates(coordinates) { | ||
const error = []; | ||
if (coordinates.lat > 90) { | ||
error.push('Your latitude is greater than 90°'); | ||
} | ||
if (coordinates.lat < -90) { | ||
error.push('Your latitude is less than -90°'); | ||
} | ||
if (coordinates.lng > 180) { | ||
error.push('Your longitude is greater than 180°'); | ||
} | ||
if (coordinates.lng < -180) { | ||
error.push('Your longitude is less than -180°'); | ||
} | ||
if (error.length !== 0) { | ||
return new Error(error.join(' ')); | ||
} | ||
} | ||
@@ -81,11 +59,40 @@ function validateHash(geohash, flag = false) { | ||
function distance(start, end, unit = 'km') { | ||
const startValid = validateCoordinates(start); | ||
if (startValid instanceof Error) { | ||
throw new Error('Start coordinates: ' + startValid.message); | ||
function validateCoordinates(coordinates, flag = false) { | ||
const error = []; | ||
if (!coordinates) { | ||
error.push('Coordinates must exist.'); | ||
} | ||
const endValid = validateCoordinates(end); | ||
if (endValid instanceof Error) { | ||
throw new Error('End coordinates: ' + endValid.message); | ||
else if (typeof coordinates.lat === 'undefined') { | ||
error.push('Latitude must exist on coordinates'); | ||
} | ||
else if (typeof coordinates.lng === 'undefined') { | ||
error.push('Longitude must exist on coordinates'); | ||
} | ||
else { | ||
const latitude = coordinates.lat; | ||
const longitude = coordinates.lng; | ||
if (typeof latitude !== 'number' || isNaN(latitude)) { | ||
error.push('Latitude must be a number.'); | ||
} | ||
else if (latitude < -90 || latitude > 90) { | ||
error.push('Latitude must be within the range [-90, 90].'); | ||
} | ||
else if (typeof longitude !== 'number' || isNaN(longitude)) { | ||
error.push('Longitude must be a number'); | ||
} | ||
else if (longitude < -180 || longitude > 180) { | ||
error.push('Longitude must be within the range [-180, 180]'); | ||
} | ||
} | ||
if (error.length > 0 && !flag) { | ||
throw new Error('Invalid coordinates: ' + error.join(' ')); | ||
} | ||
else { | ||
return !error; | ||
} | ||
} | ||
function distance(start, end, unit = 'km') { | ||
validateCoordinates(start); | ||
validateCoordinates(end); | ||
const radius = unit.toLowerCase() === 'miles' ? 3963 : 6371; | ||
@@ -103,20 +110,44 @@ const dLat = toRad(end.lat - start.lat); | ||
function hash(coordinates, precision = 10) { | ||
const valid = validateCoordinates(coordinates); | ||
if (valid instanceof Error) { | ||
throw valid; | ||
validateCoordinates(coordinates); | ||
if (typeof precision === 'number' && !isNaN(precision)) { | ||
if (precision <= 0) { | ||
throw new Error('Precision must be greater than 0'); | ||
} | ||
else if (precision > 22) { | ||
throw new Error('Precision cannot be greater than 22'); | ||
} | ||
else if (Math.round(precision) !== precision) { | ||
throw new Error('Precision must be an integer'); | ||
} | ||
} | ||
let hash = ''; | ||
else { | ||
throw new Error('Precision must be a number'); | ||
} | ||
const latRng = [-90, 90]; | ||
const lngRng = [-180, 180]; | ||
let hash = ''; | ||
let hashVal = 0; | ||
let bits = 0; | ||
let even = 1; | ||
while (hash.length < precision) { | ||
let temp = 0; | ||
for (let i = 0; i < 5; i++) { | ||
const even = (hash.length * 5 + i) % 2 === 0; | ||
const coord = even ? coordinates.lng : coordinates.lat; | ||
const range = even ? lngRng : latRng; | ||
const middle = (range[0] + range[1]) / 2; | ||
temp = (temp << 1) + getBit(coord, range); | ||
coord > middle ? (range[0] = middle) : (range[1] = middle); | ||
const val = even ? coordinates.lng : coordinates.lat; | ||
const range = even ? lngRng : latRng; | ||
const mid = (range[0] + range[1]) / 2; | ||
if (val > mid) { | ||
hashVal = (hashVal << 1) + 1; | ||
range[0] = mid; | ||
} | ||
hash += base32(temp); | ||
else { | ||
hashVal = (hashVal << 1) + 0; | ||
range[1] = mid; | ||
} | ||
even = !even; | ||
if (bits < 4) { | ||
bits++; | ||
} | ||
else { | ||
bits = 0; | ||
hash += base32(hashVal); | ||
hashVal = 0; | ||
} | ||
} | ||
@@ -129,2 +160,3 @@ return hash; | ||
exports.hash = hash; | ||
exports.validateCoordinates = validateCoordinates; | ||
exports.validateHash = validateHash; |
@@ -5,2 +5,3 @@ export * from './definitions'; | ||
export * from './functions/hash'; | ||
export * from './functions/validate-coordinates'; | ||
export * from './functions/validate-hash'; |
@@ -8,27 +8,5 @@ const BASE32 = '0123456789bcdefghjkmnpqrstuvwxyz'; | ||
} | ||
function getBit(point, range) { | ||
const middle = (range[0] + range[1]) / 2; | ||
return middle > point ? 0 : 1; | ||
} | ||
function toRad(degrees) { | ||
return (degrees * Math.PI) / 180; | ||
} | ||
function validateCoordinates(coordinates) { | ||
const error = []; | ||
if (coordinates.lat > 90) { | ||
error.push('Your latitude is greater than 90°'); | ||
} | ||
if (coordinates.lat < -90) { | ||
error.push('Your latitude is less than -90°'); | ||
} | ||
if (coordinates.lng > 180) { | ||
error.push('Your longitude is greater than 180°'); | ||
} | ||
if (coordinates.lng < -180) { | ||
error.push('Your longitude is less than -180°'); | ||
} | ||
if (error.length !== 0) { | ||
return new Error(error.join(' ')); | ||
} | ||
} | ||
@@ -77,11 +55,40 @@ function validateHash(geohash, flag = false) { | ||
function distance(start, end, unit = 'km') { | ||
const startValid = validateCoordinates(start); | ||
if (startValid instanceof Error) { | ||
throw new Error('Start coordinates: ' + startValid.message); | ||
function validateCoordinates(coordinates, flag = false) { | ||
const error = []; | ||
if (!coordinates) { | ||
error.push('Coordinates must exist.'); | ||
} | ||
const endValid = validateCoordinates(end); | ||
if (endValid instanceof Error) { | ||
throw new Error('End coordinates: ' + endValid.message); | ||
else if (typeof coordinates.lat === 'undefined') { | ||
error.push('Latitude must exist on coordinates'); | ||
} | ||
else if (typeof coordinates.lng === 'undefined') { | ||
error.push('Longitude must exist on coordinates'); | ||
} | ||
else { | ||
const latitude = coordinates.lat; | ||
const longitude = coordinates.lng; | ||
if (typeof latitude !== 'number' || isNaN(latitude)) { | ||
error.push('Latitude must be a number.'); | ||
} | ||
else if (latitude < -90 || latitude > 90) { | ||
error.push('Latitude must be within the range [-90, 90].'); | ||
} | ||
else if (typeof longitude !== 'number' || isNaN(longitude)) { | ||
error.push('Longitude must be a number'); | ||
} | ||
else if (longitude < -180 || longitude > 180) { | ||
error.push('Longitude must be within the range [-180, 180]'); | ||
} | ||
} | ||
if (error.length > 0 && !flag) { | ||
throw new Error('Invalid coordinates: ' + error.join(' ')); | ||
} | ||
else { | ||
return !error; | ||
} | ||
} | ||
function distance(start, end, unit = 'km') { | ||
validateCoordinates(start); | ||
validateCoordinates(end); | ||
const radius = unit.toLowerCase() === 'miles' ? 3963 : 6371; | ||
@@ -99,20 +106,44 @@ const dLat = toRad(end.lat - start.lat); | ||
function hash(coordinates, precision = 10) { | ||
const valid = validateCoordinates(coordinates); | ||
if (valid instanceof Error) { | ||
throw valid; | ||
validateCoordinates(coordinates); | ||
if (typeof precision === 'number' && !isNaN(precision)) { | ||
if (precision <= 0) { | ||
throw new Error('Precision must be greater than 0'); | ||
} | ||
else if (precision > 22) { | ||
throw new Error('Precision cannot be greater than 22'); | ||
} | ||
else if (Math.round(precision) !== precision) { | ||
throw new Error('Precision must be an integer'); | ||
} | ||
} | ||
let hash = ''; | ||
else { | ||
throw new Error('Precision must be a number'); | ||
} | ||
const latRng = [-90, 90]; | ||
const lngRng = [-180, 180]; | ||
let hash = ''; | ||
let hashVal = 0; | ||
let bits = 0; | ||
let even = 1; | ||
while (hash.length < precision) { | ||
let temp = 0; | ||
for (let i = 0; i < 5; i++) { | ||
const even = (hash.length * 5 + i) % 2 === 0; | ||
const coord = even ? coordinates.lng : coordinates.lat; | ||
const range = even ? lngRng : latRng; | ||
const middle = (range[0] + range[1]) / 2; | ||
temp = (temp << 1) + getBit(coord, range); | ||
coord > middle ? (range[0] = middle) : (range[1] = middle); | ||
const val = even ? coordinates.lng : coordinates.lat; | ||
const range = even ? lngRng : latRng; | ||
const mid = (range[0] + range[1]) / 2; | ||
if (val > mid) { | ||
hashVal = (hashVal << 1) + 1; | ||
range[0] = mid; | ||
} | ||
hash += base32(temp); | ||
else { | ||
hashVal = (hashVal << 1) + 0; | ||
range[1] = mid; | ||
} | ||
even = !even; | ||
if (bits < 4) { | ||
bits++; | ||
} | ||
else { | ||
bits = 0; | ||
hash += base32(hashVal); | ||
hashVal = 0; | ||
} | ||
} | ||
@@ -122,2 +153,2 @@ return hash; | ||
export { decodeHash, distance, hash, validateHash }; | ||
export { decodeHash, distance, hash, validateCoordinates, validateHash }; |
@@ -1,7 +0,4 @@ | ||
import { LatLngLiteral } from './definitions'; | ||
export declare const BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"; | ||
export declare function base32(value: number): string; | ||
export declare function decimalChunk(value: string): number; | ||
export declare function getBit(point: number, range: number[]): number; | ||
export declare function toRad(degrees: number): number; | ||
export declare function validateCoordinates(coordinates: LatLngLiteral): Error | void; |
{ | ||
"name": "geokit", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "An assortment of geolocation related tools, all packaged in one easy to use kit.", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -52,2 +52,16 @@ # geokit | ||
### `validateCoordinates(coordinates: LatLngLiteral): boolean` | ||
Validates coordinates and returns a boolean if valid, or throws an error if invalid. | ||
`coordinates` must be LatLngLiterals `{ lat: 0, lng: 0 }`. | ||
```TypeScript | ||
import * as geokit from 'geokit'; | ||
const coordinates = {lat: 41.3083, lng: -72.9279}; | ||
const isValid = geokit.validateCoordinates(coordinates); // true | ||
``` | ||
### `validateHash(hash: string): LatLngLiteral` | ||
@@ -54,0 +68,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
27596
15
326
77