@webkitty/geo-rev
Version:
Reverse geocoder for Node.js working with offline data and fast lookup times.
93 lines • 3.25 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RevGeocoder = exports.createRevGeocoder = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const readline_1 = require("readline");
const events_1 = require("events");
const createKDTree = require("static-kdtree");
__exportStar(require("./types"), exports);
async function createRevGeocoder(options) {
return new RevGeocoder(options).init();
}
exports.createRevGeocoder = createRevGeocoder;
class RevGeocoder {
constructor(options) {
this.records = [];
options = options || {};
options.dataset = options.dataset || path_1.join(__dirname, '..', 'data', 'cities500.csv');
this.dataset = options.dataset;
}
async init() {
this.records = await parseDataset(this.dataset);
this.index = createIndex(this.records);
return this;
}
lookup(point) {
if (!this.index) {
throw new Error('Index not initialized.');
}
const nearestRecordIndex = this.index.nn([point.latitude, point.longitude]);
if (nearestRecordIndex < 0 || nearestRecordIndex >= this.records.length) {
throw new Error('Record index out of bounds.');
}
return {
record: this.records[nearestRecordIndex],
distance: distanceBetween(point, this.records[nearestRecordIndex]),
};
}
}
exports.RevGeocoder = RevGeocoder;
async function parseDataset(dataset) {
const records = [];
const reader = readline_1.createInterface({
input: fs_1.createReadStream(dataset),
historySize: 0,
crlfDelay: Infinity,
});
reader.on('line', line => records.push(parseLine(line)));
await events_1.once(reader, 'close');
return records;
}
function parseLine(line) {
const cols = line.split(';');
return {
name: cols[0],
latitude: parseFloat(cols[1]),
longitude: parseFloat(cols[2]),
countryCode: cols[3],
};
}
function createIndex(records) {
return createKDTree(records.map(record => [record.latitude, record.longitude]));
}
function distanceBetween(x, y) {
const lat1 = x.latitude;
const lon1 = x.longitude;
const lat2 = y.latitude;
const lon2 = y.longitude;
const R = 6371;
const φ1 = toRad(lat1);
const φ2 = toRad(lat2);
const Δφ = toRad(lat2 - lat1);
const Δλ = toRad(lon2 - lon1);
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
function toRad(deg) {
return deg * Math.PI / 180;
}
//# sourceMappingURL=index.js.map