-
Notifications
You must be signed in to change notification settings - Fork 1
/
web.js
130 lines (104 loc) · 3.29 KB
/
web.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
var request = require('request'),
express = require('express'),
morgan = require('morgan'),
cors = require('cors'),
sphereKnn = require('sphere-knn'),
zipToLatLon = require('./zip_lat_lon.json');
var app = express();
var logger = morgan('combined');
app.use(logger);
var featuresLookup = null,
featuresCount = null,
geojsonEtag = null;
var clamp = function(val, min, max) {
return Math.min(Math.max(val, min), max);
}
app.get('/', function(req, res) {
return res.sendfile('index.html');
});
app.get('/points/statistics', cors(), function(req, res) {
if (!featuresCount) {
return res.status(500).send({error: 'No points available.'});
}
return res.send({
count: featuresCount
});
});
app.get('/points/nearby', cors(), function(req, res) {
if (!featuresLookup) {
return res.status(500).send({error: 'No points available.'});
}
var lat = null,
lon = null;
if (req.query.zipcode) {
var ll = zipToLatLon[req.query.zipcode];
if (!ll) {
return res.status(400).send({error: 'That zipcode was not found.'});
}
lat = ll[0];
lon = ll[1];
}
if (req.query.lat && req.query.lon) {
lat = req.query.lat;
lon = req.query.lon;
}
if (!(lat && lon)) {
return res.status(400).send({error: 'Requires lat and lon query arg or zipcode query arg.'});
}
var points = featuresLookup(lat, lon),
points = points.map(function(f) { return f[2] }),
max_points = clamp(req.query.limit || 5, 1, 50);
points = points.slice(0, max_points);
return res.send({
type: "FeatureCollection",
features: points
});
});
function updateGeojsonData() {
options = {
uri: process.env.GEOJSON_URL,
json: true,
}
if (geojsonEtag) {
options.headers = {
'If-None-Match': geojsonEtag
}
}
request(options, function(err, resp, body) {
if (err) {
console.log(err);
return;
}
if (resp.statusCode === 304) {
console.log("No changes to GeoJSON since last check.");
return;
}
// The features come in as GeoJSON Features that have lat/lon on an array
// in a 'coordinates' object, but the sphere-knn library wants to see the
// lat/lon at the root level. To solve that we insert a list with
// [lat, lon, feature] and then peel it apart later when we access it.
var features = body.features.map(function (f) {
return [
f.geometry.coordinates[1],
f.geometry.coordinates[0],
f
]
});
featuresCount = features.length;
featuresLookup = new sphereKnn(features);
console.log("Successfully loaded " + featuresCount + " features.");
if (resp.headers['etag']) {
geojsonEtag = resp.headers['etag'];
}
});
}
var port = process.env.PORT || 5000;
app.listen(port, function() {
if (!process.env.GEOJSON_URL) {
console.log("No GEOJSON_URL environment variable set to load dataset.");
throw "No GEOJSON_URL envvar set.";
}
console.log("Listening on " + port);
setInterval(updateGeojsonData, 1000 * 60);
updateGeojsonData();
});