mirror of
https://github.com/appy-one/acebase.git
synced 2026-06-30 06:02:02 -06:00
chore: build
This commit is contained in:
parent
b46448e19f
commit
02ba2ef8a2
7 changed files with 515 additions and 1 deletions
|
|
@ -3492,7 +3492,7 @@ function _getGeoHash(obj) {
|
|||
return geohash;
|
||||
}
|
||||
|
||||
// Berekent welke hashes (van verschillende precisies) er in een straal liggen vanaf middelpunt
|
||||
// Calculates which hashes (of different precisions) are within the radius of a point
|
||||
function _hashesInRadius(lat, lon, radiusM, precision) {
|
||||
|
||||
const isInCircle = (checkLat, checkLon, lat, lon, radiusM) => {
|
||||
|
|
|
|||
60
src/geohash.d.ts
vendored
Normal file
60
src/geohash.d.ts
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Encodes latitude/longitude to geohash, either to specified precision or to automatically
|
||||
* evaluated precision.
|
||||
* @param lat - Latitude in degrees.
|
||||
* @param lon - Longitude in degrees.
|
||||
* @param precision - Number of characters in resulting
|
||||
* @returns Geohash of supplied latitude/longitude.
|
||||
* @example
|
||||
* let geohash = encode(52.205, 0.119, 7); // geohash: 'u120fxw'
|
||||
*/
|
||||
export declare const encode: (lat: number, lon: number, precision?: number) => string;
|
||||
/**
|
||||
* Decode geohash to latitude/longitude (location is approximate centre of geohash cell,
|
||||
* to reasonable precision).
|
||||
* @param geohash Geohash string to be converted to latitude/longitude.
|
||||
* @returns (Center of) geohashed location.
|
||||
*
|
||||
* @example
|
||||
* let latlon = decode('u120fxw'); // latlon: { lat: 52.205, lon: 0.1188 }
|
||||
*/
|
||||
export declare const decode: (geohash: string) => {
|
||||
lat: number;
|
||||
lon: number;
|
||||
};
|
||||
/**
|
||||
* Returns SW/NE latitude/longitude bounds of specified cell
|
||||
* @param geohash Cell that bounds are required of.
|
||||
*/
|
||||
export declare const bounds: (geohash: string) => {
|
||||
sw: {
|
||||
lat: number;
|
||||
lon: number;
|
||||
};
|
||||
ne: {
|
||||
lat: number;
|
||||
lon: number;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Determines adjacent cell in given direction.
|
||||
* @param geohash Cell to which adjacent cell is required.
|
||||
* @param direction Direction from geohash (N/S/E/W).
|
||||
* @returns Geocode of adjacent cell.
|
||||
* @throws Invalid
|
||||
*/
|
||||
export declare const adjacent: (geohash: string, direction: 'N' | 'n' | 'S' | 's' | 'E' | 'e' | 'W' | 'w') => string;
|
||||
/**
|
||||
* Returns all 8 adjacent cells to specified cell
|
||||
* @param geohash Geohash neighbours are required of.
|
||||
*/
|
||||
export declare const neighbours: (geohash: string) => {
|
||||
n: string;
|
||||
ne: string;
|
||||
e: string;
|
||||
se: string;
|
||||
s: string;
|
||||
sw: string;
|
||||
w: string;
|
||||
nw: string;
|
||||
};
|
||||
207
src/geohash.js
Normal file
207
src/geohash.js
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
"use strict";
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
/* Geohash encoding/decoding and associated functions (c) Chris Veness 2014-2016 / MIT Licence */
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.neighbours = exports.adjacent = exports.bounds = exports.decode = exports.encode = void 0;
|
||||
/**
|
||||
* Geohash encode, decode, bounds, neighbours.
|
||||
*/
|
||||
/* (Geohash-specific) Base32 map */
|
||||
const base32 = '0123456789bcdefghjkmnpqrstuvwxyz';
|
||||
/**
|
||||
* Encodes latitude/longitude to geohash, either to specified precision or to automatically
|
||||
* evaluated precision.
|
||||
* @param lat - Latitude in degrees.
|
||||
* @param lon - Longitude in degrees.
|
||||
* @param precision - Number of characters in resulting
|
||||
* @returns Geohash of supplied latitude/longitude.
|
||||
* @example
|
||||
* let geohash = encode(52.205, 0.119, 7); // geohash: 'u120fxw'
|
||||
*/
|
||||
const encode = function (lat, lon, precision) {
|
||||
// infer precision?
|
||||
if (typeof precision == 'undefined') {
|
||||
// refine geohash until it matches precision of supplied lat/lon
|
||||
for (let p = 1; p <= 12; p++) {
|
||||
let hash = (0, exports.encode)(lat, lon, p);
|
||||
let posn = (0, exports.decode)(hash);
|
||||
if (posn.lat == lat && posn.lon == lon)
|
||||
return hash;
|
||||
}
|
||||
precision = 12; // set to maximum
|
||||
}
|
||||
lat = Number(lat);
|
||||
lon = Number(lon);
|
||||
precision = Number(precision);
|
||||
if (isNaN(lat) || isNaN(lon) || isNaN(precision))
|
||||
throw new Error('Invalid geohash');
|
||||
let idx = 0; // index into base32 map
|
||||
let bit = 0; // each char holds 5 bits
|
||||
let evenBit = true;
|
||||
let geohash = '';
|
||||
let latMin = -90, latMax = 90;
|
||||
let lonMin = -180, lonMax = 180;
|
||||
while (geohash.length < precision) {
|
||||
if (evenBit) {
|
||||
// bisect E-W longitude
|
||||
let lonMid = (lonMin + lonMax) / 2;
|
||||
if (lon >= lonMid) {
|
||||
idx = idx * 2 + 1;
|
||||
lonMin = lonMid;
|
||||
}
|
||||
else {
|
||||
idx = idx * 2;
|
||||
lonMax = lonMid;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// bisect N-S latitude
|
||||
let latMid = (latMin + latMax) / 2;
|
||||
if (lat >= latMid) {
|
||||
idx = idx * 2 + 1;
|
||||
latMin = latMid;
|
||||
}
|
||||
else {
|
||||
idx = idx * 2;
|
||||
latMax = latMid;
|
||||
}
|
||||
}
|
||||
evenBit = !evenBit;
|
||||
if (++bit == 5) {
|
||||
// 5 bits gives us a character: append it and start over
|
||||
geohash += base32.charAt(idx);
|
||||
bit = 0;
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
return geohash;
|
||||
};
|
||||
exports.encode = encode;
|
||||
/**
|
||||
* Decode geohash to latitude/longitude (location is approximate centre of geohash cell,
|
||||
* to reasonable precision).
|
||||
* @param geohash Geohash string to be converted to latitude/longitude.
|
||||
* @returns (Center of) geohashed location.
|
||||
*
|
||||
* @example
|
||||
* let latlon = decode('u120fxw'); // latlon: { lat: 52.205, lon: 0.1188 }
|
||||
*/
|
||||
const decode = function (geohash) {
|
||||
const b = (0, exports.bounds)(geohash); // <-- the hard work
|
||||
// now just determine the centre of the cell...
|
||||
const latMin = b.sw.lat, lonMin = b.sw.lon;
|
||||
const latMax = b.ne.lat, lonMax = b.ne.lon;
|
||||
// cell centre
|
||||
let lat = (latMin + latMax) / 2;
|
||||
let lon = (lonMin + lonMax) / 2;
|
||||
// round to close to centre without excessive precision: ⌊2-log10(Δ°)⌋ decimal places
|
||||
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 };
|
||||
};
|
||||
exports.decode = decode;
|
||||
/**
|
||||
* Returns SW/NE latitude/longitude bounds of specified cell
|
||||
* @param geohash Cell that bounds are required of.
|
||||
*/
|
||||
const bounds = function (geohash) {
|
||||
if (geohash.length === 0)
|
||||
throw new Error('Invalid geohash');
|
||||
geohash = geohash.toLowerCase();
|
||||
let evenBit = true;
|
||||
let latMin = -90, latMax = 90;
|
||||
let lonMin = -180, lonMax = 180;
|
||||
for (let i = 0; i < geohash.length; i++) {
|
||||
let chr = geohash.charAt(i);
|
||||
let idx = base32.indexOf(chr);
|
||||
if (idx == -1)
|
||||
throw new Error('Invalid geohash');
|
||||
for (let n = 4; n >= 0; n--) {
|
||||
let bitN = idx >> n & 1;
|
||||
if (evenBit) {
|
||||
// longitude
|
||||
let lonMid = (lonMin + lonMax) / 2;
|
||||
if (bitN == 1) {
|
||||
lonMin = lonMid;
|
||||
}
|
||||
else {
|
||||
lonMax = lonMid;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// latitude
|
||||
let latMid = (latMin + latMax) / 2;
|
||||
if (bitN == 1) {
|
||||
latMin = latMid;
|
||||
}
|
||||
else {
|
||||
latMax = latMid;
|
||||
}
|
||||
}
|
||||
evenBit = !evenBit;
|
||||
}
|
||||
}
|
||||
let bounds = {
|
||||
sw: { lat: latMin, lon: lonMin },
|
||||
ne: { lat: latMax, lon: lonMax },
|
||||
};
|
||||
return bounds;
|
||||
};
|
||||
exports.bounds = bounds;
|
||||
/**
|
||||
* Determines adjacent cell in given direction.
|
||||
* @param geohash Cell to which adjacent cell is required.
|
||||
* @param direction Direction from geohash (N/S/E/W).
|
||||
* @returns Geocode of adjacent cell.
|
||||
* @throws Invalid
|
||||
*/
|
||||
const adjacent = function (geohash, direction) {
|
||||
// based on github.com/davetroy/geohash-js
|
||||
geohash = geohash.toLowerCase();
|
||||
direction = direction.toLowerCase();
|
||||
if (length === 0)
|
||||
throw new Error('Invalid geohash');
|
||||
if ('nsew'.indexOf(direction) == -1)
|
||||
throw new Error('Invalid direction');
|
||||
let neighbour = {
|
||||
n: ['p0r21436x8zb9dcf5h7kjnmqesgutwvy', 'bc01fg45238967deuvhjyznpkmstqrwx'],
|
||||
s: ['14365h7k9dcfesgujnmqp0r2twvyx8zb', '238967debc01fg45kmstqrwxuvhjyznp'],
|
||||
e: ['bc01fg45238967deuvhjyznpkmstqrwx', 'p0r21436x8zb9dcf5h7kjnmqesgutwvy'],
|
||||
w: ['238967debc01fg45kmstqrwxuvhjyznp', '14365h7k9dcfesgujnmqp0r2twvyx8zb'],
|
||||
};
|
||||
let border = {
|
||||
n: ['prxz', 'bcfguvyz'],
|
||||
s: ['028b', '0145hjnp'],
|
||||
e: ['bcfguvyz', 'prxz'],
|
||||
w: ['0145hjnp', '028b'],
|
||||
};
|
||||
let lastCh = geohash.slice(-1); // last character of hash
|
||||
let parent = geohash.slice(0, -1); // hash without last character
|
||||
let type = geohash.length % 2;
|
||||
// check for edge-cases which don't share common prefix
|
||||
if (border[direction][type].indexOf(lastCh) != -1 && parent !== '') {
|
||||
parent = (0, exports.adjacent)(parent, direction);
|
||||
}
|
||||
// append letter for direction to parent
|
||||
return parent + base32.charAt(neighbour[direction][type].indexOf(lastCh));
|
||||
};
|
||||
exports.adjacent = adjacent;
|
||||
/**
|
||||
* Returns all 8 adjacent cells to specified cell
|
||||
* @param geohash Geohash neighbours are required of.
|
||||
*/
|
||||
const neighbours = function (geohash) {
|
||||
return {
|
||||
'n': (0, exports.adjacent)(geohash, 'n'),
|
||||
'ne': (0, exports.adjacent)((0, exports.adjacent)(geohash, 'n'), 'e'),
|
||||
'e': (0, exports.adjacent)(geohash, 'e'),
|
||||
'se': (0, exports.adjacent)((0, exports.adjacent)(geohash, 's'), 'e'),
|
||||
's': (0, exports.adjacent)(geohash, 's'),
|
||||
'sw': (0, exports.adjacent)((0, exports.adjacent)(geohash, 's'), 'w'),
|
||||
'w': (0, exports.adjacent)(geohash, 'w'),
|
||||
'nw': (0, exports.adjacent)((0, exports.adjacent)(geohash, 'n'), 'w'),
|
||||
};
|
||||
};
|
||||
exports.neighbours = neighbours;
|
||||
//# sourceMappingURL=geohash.js.map
|
||||
1
src/geohash.js.map
Normal file
1
src/geohash.js.map
Normal file
File diff suppressed because one or more lines are too long
63
src/thread-safe.d.ts
vendored
Normal file
63
src/thread-safe.d.ts
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/// <reference types="node" />
|
||||
import { SimpleEventEmitter } from "acebase-core";
|
||||
export interface ThreadSafeLockOptions {
|
||||
/** max amount of ms the target is allowed to be locked (and max time to wait to get it), default is 60000 (60s) */
|
||||
timeout?: number;
|
||||
/** flag that indicates whether this lock does critical work, canceling queued lock requests if this lock is not released in time */
|
||||
critical?: boolean;
|
||||
/** name of the lock, good for debugging purposes */
|
||||
name?: string;
|
||||
/** if this lock is allowed to be shared with others also requesting a shared lock. Requested lock will be exclusive otherwise (default) */
|
||||
shared?: boolean;
|
||||
/** if you are using a string to uniquely identify the locking target, you can pass the actual object target with this option; lock.target will be set to this value instead. */
|
||||
target?: any;
|
||||
}
|
||||
interface ThreadSafeLockQueueItem {
|
||||
resolve: (lock: ThreadSafeLock) => void;
|
||||
reject: (err: Error) => void;
|
||||
waitTimeout: NodeJS.Timeout;
|
||||
options: ThreadSafeLockOptions;
|
||||
}
|
||||
export interface ThreadSafeLock {
|
||||
achieved: Date;
|
||||
release: () => void;
|
||||
target: any;
|
||||
name: string;
|
||||
_timeout: NodeJS.Timeout;
|
||||
_queue: ThreadSafeLockQueueItem[];
|
||||
/** If DEBUG_MODE is enabled: contains stack trace of ThreadSafe.lock call */
|
||||
stack: string;
|
||||
}
|
||||
export declare abstract class ThreadSafe {
|
||||
/**
|
||||
*
|
||||
* @param target Target object to lock. Do not use object references!
|
||||
* @param options Locking options
|
||||
* @returns returns a lock
|
||||
*/
|
||||
static lock(target: any, options?: ThreadSafeLockOptions): Promise<ThreadSafeLock>;
|
||||
}
|
||||
/**
|
||||
* New locking mechasnism that supports exclusive or shared locking
|
||||
*/
|
||||
export declare class ThreadSafeLock2 extends SimpleEventEmitter {
|
||||
readonly target: any;
|
||||
readonly achieved: Date;
|
||||
private shares;
|
||||
private queue;
|
||||
private _shared;
|
||||
get shared(): boolean;
|
||||
constructor(target: any, shared: boolean);
|
||||
release(): void;
|
||||
request(shared: boolean): Promise<void>;
|
||||
}
|
||||
export declare abstract class ThreadSafe2 {
|
||||
/**
|
||||
*
|
||||
* @param target Target object to lock. Do not use object references!
|
||||
* @param options Locking options
|
||||
* @returns returns a lock
|
||||
*/
|
||||
static lock(target: any, shared?: boolean): Promise<ThreadSafeLock2>;
|
||||
}
|
||||
export {};
|
||||
182
src/thread-safe.js
Normal file
182
src/thread-safe.js
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ThreadSafe2 = exports.ThreadSafeLock2 = exports.ThreadSafe = void 0;
|
||||
const acebase_core_1 = require("acebase-core");
|
||||
/** Set to true to add stack traces to achieved locks (performance impact!) */
|
||||
const DEBUG_MODE = false;
|
||||
const _lockTimeoutMsg = 'Lock "${name}" timed out! lock.release() was not called in a timely fashion';
|
||||
const _lockWaitTimeoutMsg = 'Lock "${name}" wait time expired, failed to lock target';
|
||||
const _threadSafeLocks = new Map();
|
||||
class ThreadSafe {
|
||||
/**
|
||||
*
|
||||
* @param target Target object to lock. Do not use object references!
|
||||
* @param options Locking options
|
||||
* @returns returns a lock
|
||||
*/
|
||||
static lock(target, options = { timeout: 60000 * 15, critical: true, name: 'unnamed lock', shared: false }) {
|
||||
if (typeof options !== 'object') {
|
||||
options = {};
|
||||
}
|
||||
if (typeof options.timeout !== 'number') {
|
||||
options.timeout = 60 * 1000;
|
||||
}
|
||||
if (typeof options.critical !== 'boolean') {
|
||||
options.critical = true;
|
||||
}
|
||||
if (typeof options.name !== 'string') {
|
||||
options.name = typeof target === 'string' ? target : 'unnamed lock';
|
||||
}
|
||||
if (typeof options.shared !== 'boolean') {
|
||||
options.shared = false;
|
||||
}
|
||||
if (options.shared) {
|
||||
// TODO: Implement
|
||||
// console.warn('shared locking not implemented yet, using exclusive lock');
|
||||
}
|
||||
let lock = _threadSafeLocks.get(target);
|
||||
const timeoutHandler = (critical) => {
|
||||
console.error(_lockTimeoutMsg.replace('${name}', lock.name));
|
||||
// Copy lock object so we can alter the original's release method to throw an exception
|
||||
let copy = Object.assign({}, lock);
|
||||
let originalName = lock.name;
|
||||
lock.release = () => {
|
||||
throw new Error(`Cannot release lock "${originalName}" because it timed out earlier`);
|
||||
};
|
||||
lock = copy;
|
||||
if (critical) {
|
||||
// cancel any queued requests
|
||||
_threadSafeLocks.delete(target);
|
||||
lock._queue.forEach(item => {
|
||||
clearTimeout(item.waitTimeout);
|
||||
item.reject(new Error(`Could not achieve lock because the current lock ("${lock.name}") was not released in time (and lock is flagged critical)`));
|
||||
});
|
||||
}
|
||||
else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
const next = () => {
|
||||
clearTimeout(lock._timeout);
|
||||
if (lock._queue.length === 0) {
|
||||
return _threadSafeLocks.delete(target);
|
||||
}
|
||||
let item = lock._queue.shift();
|
||||
clearTimeout(item.waitTimeout);
|
||||
lock._timeout = setTimeout(timeoutHandler, item.options.timeout, item.options.critical);
|
||||
lock.target = item.options.target || target;
|
||||
lock.achieved = new Date();
|
||||
lock.name = item.options.name;
|
||||
lock.stack = DEBUG_MODE ? (new Error()).stack : 'not available';
|
||||
item.resolve(lock);
|
||||
};
|
||||
if (!lock) {
|
||||
// Create lock
|
||||
lock = {
|
||||
target: options.target || target,
|
||||
achieved: new Date(),
|
||||
release() {
|
||||
next();
|
||||
},
|
||||
name: options.name,
|
||||
stack: DEBUG_MODE ? (new Error()).stack : 'not available',
|
||||
_timeout: setTimeout(timeoutHandler, options.timeout, options.critical),
|
||||
_queue: []
|
||||
};
|
||||
_threadSafeLocks.set(target, lock);
|
||||
return Promise.resolve(lock);
|
||||
}
|
||||
else {
|
||||
// Add to queue
|
||||
return new Promise((resolve, reject) => {
|
||||
const waitTimeout = setTimeout(() => {
|
||||
lock._queue.splice(lock._queue.indexOf(item), 1);
|
||||
if (lock._queue.length === 0) {
|
||||
_threadSafeLocks.delete(target);
|
||||
}
|
||||
reject(_lockWaitTimeoutMsg.replace('${name}', options.name));
|
||||
}, options.timeout);
|
||||
const item = { resolve, reject, waitTimeout, options };
|
||||
lock._queue.push(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.ThreadSafe = ThreadSafe;
|
||||
/**
|
||||
* New locking mechasnism that supports exclusive or shared locking
|
||||
*/
|
||||
class ThreadSafeLock2 extends acebase_core_1.SimpleEventEmitter {
|
||||
constructor(target, shared) {
|
||||
super();
|
||||
this.target = target;
|
||||
this.shares = 0;
|
||||
this.queue = [];
|
||||
this._shared = shared;
|
||||
this.achieved = new Date();
|
||||
}
|
||||
get shared() { return this._shared; }
|
||||
release() {
|
||||
if (this.shared && this.shares > 0) {
|
||||
this.shares--;
|
||||
}
|
||||
else if (this.queue.length > 0) {
|
||||
const next = this.queue.shift();
|
||||
this._shared = next.shared;
|
||||
next.grant();
|
||||
if (next.shared) {
|
||||
// Also grant other pending shared requests
|
||||
while (this.queue.length > 0 && this.queue[0].shared) {
|
||||
this.queue.shift().grant();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No more shares, no queue: this lock can be now be released entirely
|
||||
this.emitOnce('released');
|
||||
}
|
||||
}
|
||||
async request(shared) {
|
||||
if (this.shared && shared) {
|
||||
// Grant!
|
||||
this.shares++;
|
||||
}
|
||||
else {
|
||||
// Add to queue, wait until granted
|
||||
let grant;
|
||||
const promise = new Promise(resolve => { grant = resolve; });
|
||||
this.queue.push({ shared, grant });
|
||||
await promise;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.ThreadSafeLock2 = ThreadSafeLock2;
|
||||
const locks2 = new Map();
|
||||
class ThreadSafe2 {
|
||||
/**
|
||||
*
|
||||
* @param target Target object to lock. Do not use object references!
|
||||
* @param options Locking options
|
||||
* @returns returns a lock
|
||||
*/
|
||||
static async lock(target, shared = false) {
|
||||
const timeout = 60 * 1000;
|
||||
if (!locks2.has(target)) {
|
||||
// New lock
|
||||
const lock = new ThreadSafeLock2(target, shared);
|
||||
locks2.set(target, lock);
|
||||
lock.once('released', () => {
|
||||
locks2.delete(target);
|
||||
});
|
||||
return lock;
|
||||
}
|
||||
else {
|
||||
// Existing lock
|
||||
const lock = locks2.get(target);
|
||||
await lock.request(shared);
|
||||
return lock;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.ThreadSafe2 = ThreadSafe2;
|
||||
//# sourceMappingURL=thread-safe.js.map
|
||||
1
src/thread-safe.js.map
Normal file
1
src/thread-safe.js.map
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue