UNPKG

s2-tools

Version:

A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.

141 lines 4.42 kB
/** A local KD key-value store */ export class KDSpatialIndex { nodeSize; #store = []; /** * @param nodeSize - the size of each kd-tree node */ constructor(nodeSize = 64) { this.nodeSize = nodeSize; } /** @returns the length of the store */ get length() { return this.#store.length; } /** * Push a value into the store * @param value - the value to store */ push(value) { this.#store.push(value); } /** * Get a value at index * @param index - the position in the store to get the value from * @returns the value */ get(index) { return this.#store[index]; } /** * Get a range of values within an index range * @param indexStart - the start index * @param indexEnd - the end index * @returns the values */ getRange(indexStart, indexEnd) { return this.#store.slice(indexStart, indexEnd); } /** * iterate through the values * @yields an iterator */ *values() { for (const value of this.#store) yield value; } /** Sort the store in place */ sort() { this.#sort(0, this.#store.length - 1, 0); } /** * iterate through the values * @returns an iterator */ [Symbol.iterator]() { return this.values(); } /** Closes the store */ close() { this.#store = []; } /** * Recursively kd-sort the store * @param left - the leftmost index * @param right - the rightmost index * @param axis - 0 for x, 1 for y */ #sort(left, right, axis) { if (right - left <= this.nodeSize) return; const m = (left + right) >> 1; // middle index // sort ids and coords around the middle index so that the halves lie // either left/right or top/bottom correspondingly (taking turns) this.#select(m, left, right, axis); // recursively kd-sort first half and second half on the opposite axis this.#sort(left, m - 1, 1 - axis); this.#sort(m + 1, right, 1 - axis); } /** * Custom Floyd-Rivest selection algorithm: sort coords so that * [left..k-1] items are smaller than k-th item (on either x or y axis) * @param k - the sorting anchor index between left and right * @param left - the leftmost index * @param right - the rightmost index * @param axis - 0 for x, 1 for y */ #select(k, left, right, axis) { while (right > left) { if (right - left > 600) { const n = right - left + 1; const m = k - left + 1; const z = Math.log(n); const s = 0.5 * Math.exp((2 * z) / 3); const sd = 0.5 * Math.sqrt((z * s * (n - s)) / n) * (m - n / 2 < 0 ? -1 : 1); const newLeft = Math.max(left, Math.floor(k - (m * s) / n + sd)); const newRight = Math.min(right, Math.floor(k + ((n - m) * s) / n + sd)); this.#select(k, newLeft, newRight, axis); } const p = this.#store[k]; const t = axis === 0 ? p.x : p.y; let i = left; let j = right; this.#swap(left, k); const rp = this.#store[right]; if ((axis === 0 ? rp.x : rp.y) > t) this.#swap(left, right); while (i < j) { this.#swap(i, j); i++; j--; while (i < this.length && (axis === 0 ? this.#store[i].x : this.#store[i].y) < t) i++; while (j >= 0 && (axis === 0 ? this.#store[j].x : this.#store[j].y) > t) j--; } if ((axis === 0 ? this.#store[left].x : this.#store[left].y) === t) { this.#swap(left, j); } else { j++; this.#swap(j, right); } if (j <= k) left = j + 1; if (k <= j) right = j - 1; } } /** * swap two values given their indices * @param i - the first index * @param j - the second index */ #swap(i, j) { const store = this.#store; const tmp = store[i]; store[i] = store[j]; store[j] = tmp; } } //# sourceMappingURL=index.js.map