-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
106 lines (88 loc) · 2.98 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// See http://gis.stackexchange.com/questions/88811/how-to-find-nearest-city-using-geonames-features-codes
var fs = require("fs"),
csv = require("fast-csv"),
quadtree = require("quadtree"),
distance = require("geodist");
var cities = [];
var quads = {};
var PRECISION = 6;
//module.exports = {};
module.exports.initialize = function(callback, opts) {
opts = opts || {};
var headers = [ "geonameid", "name", "asciiname", "alternatenames", "latitude", "longitude", "feature class", "feature code", "country code", "cc2", "admin1 code", "admin2 code", "admin3 code", "admin4 code", "population", "elevation", "dem", "timezone", "modification date" ];
//var banned = ["PPLA3", "PPLA4", "PPLX", "PPL"];
if (opts.level == 1000) {
var filename = "/cities1000.txt";
} else {
var filename = "/cities5000.txt";
}
var stream = fs.createReadStream(__dirname + filename);
csv
.fromStream(stream, { headers : headers, delimiter: "\t", quote: null })
.on("data", function(city) {
city.latitude = parseFloat(city.latitude);
city.longitude = parseFloat(city.longitude);
city.population = parseInt(city.population);
delete city.alternatenames;
if (!opts.country || opts.country == city["country code"]) {
cities.push(city);
}
})
.on("end", function() {
console.log("Loaded " + cities.length + " cities, now building quadtree");
makeQuadtree();
});
function makeQuadtree() {
cities.forEach(function(city) {
var q = quadtree.encode({ lng: city.longitude, lat: city.latitude }, PRECISION);
quads[q] = quads[q] || [];
quads[q].push(city);
});
for (var q in quads) {
//console.log(quads[q].length + " cities in " + q);
}
callback();
}
}
module.exports.locate = function(point) {
point.longitude = parseFloat(point.lng || point.longitude);
point.latitude = parseFloat(point.lat || point.latitude);
//console.log(point);
var q = quadtree.encode({ lng: point.longitude, lat: point.latitude }, PRECISION),
candidates = [],
min = Infinity,
nearest;
// add the surrounding quadrants if they exist
for (var n = -1; n <= 1; n += 1) {
for (var w = -1; w <= 1; w += 1) {
var neighborkey = quadtree.neighbour(q, n, w);
if (quads[neighborkey]) {
candidates = candidates.concat(quads[neighborkey]);
}
}
}
if (!candidates.length) {
//console.log("Couldn't locate a city near ", point);
return null;
}
candidates.forEach(function(neighbor) {
var dist = distance({ lat: point.latitude, lng: point.longitude }, { lat: neighbor.latitude, lng: neighbor.longitude }, { unit: "mi", exact: true } );
//console.log(neighbor.name, neighbor.latitude, neighbor.longitude, dist);
// won't allow 0 in case the point inself is in here
if (dist < min && dist !== 0) {
min = dist;
nearest = neighbor;
}
});
if (!nearest) {
console.log("Couldn't locate a city near ", point);
return null;
}
//console.log(nearest)
return {
nearest: nearest,
distance: min,
quadtree: q,
log: "Checked " + candidates.length + " neighbors in quadrant " + q
};
}