-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgeocoder.js
163 lines (147 loc) · 5.12 KB
/
geocoder.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*jslint node: true, indent: 2, sloppy: true, white: true, vars: true */
/*
* Multi-service geocoder.
* Uses a cache to store geocoder results. For cache misses, tries the Google
* Maps API. If we've reached the Google Maps rate limit, then tries Yahoo
* Placefinder and Nominatum. If Yahoo's quality indication is poor, we use
* Nominatum, unless the latter didn't return any results.
*/
var request = require('request');
var url = require('url');
var Q = require('q');
var yahoo = require('./geocoder-yboss.js');
var mapzen = require('./geocoder-mapzen.js');
var nominatim = require('./geocoder-nominatum.js');
var google = require('./geocoder-google.js');
var cache = require('./geocoder-cache.js');
var metrics = require('./metrics.js');
var minYahooQuality = 40;
var detroit = 'Detroit, MI';
var dearborn = 'Dearborn, MI';
var highlandpark = 'Highland Park, MI';
var hamtramck = 'Hamtramck, MI';
var harperwoods = 'Harper Woods, MI';
module.exports = (function () {
var self = {};
self.comboCode = function (line1) {
return yahoo.code(line1, detroit)
.then(function (coords) {
// Check if the Yahoo Placefinder result meets the quality bar.
if (coords.meta.quality >= minYahooQuality) {
console.log('Geocoder: using Yahoo Placefinder');
// Add to cache.
cache.add(line1, detroit, coords);
return coords;
}
var promises = [dearborn, highlandpark, hamtramck, harperwoods].map(function (city) {
return yahoo.code(line1, city);
});
return Q.allResolved(promises)
.then(function (promises) {
var bestCoords;
promises.forEach(function (promise) {
if (promise.isFulfilled()) {
var coords = promise.valueOf();
if (bestCoords === undefined || coords.meta.quality > bestCoords.meta.quality) {
bestCoords = coords;
}
}
});
if (bestCoords === undefined) {
throw new Error('No results from Yahoo for other cities');
} else {
if (bestCoords.meta.quality >= minYahooQuality) {
console.log('Geocoder: using Yahoo Placefinder for ' + bestCoords.meta.line2);
// Add to cache with detroit, since that's how we'll look it up
// later.
cache.add(line1, detroit, bestCoords);
return bestCoords;
}
throw {
name: 'BadLocationError',
message: 'Yahoo gave us a low-quality location'
};
}
});
})
.fail(function (reason) {
console.log('Yahoo failed: ' + reason.message);
// Yahoo Placefinder failed, so use Nominatim.
return nominatim.code(line1, detroit)
.then(function (coords) {
console.log('Geocoder: using Nominatim');
return coords;
})
.fail(function (reason) {
// All geocoders failed!
console.log(reason.message);
console.log('All geocoders failed!');
throw reason;
});
});
};
self.mapzenCode = function(line1) {
return mapzen.code(line1)
.then(function (coords) {
// Don't cache mapzen results for now.
// cache.add(line1, detroit, coords);
console.log('Geocoder: using Mapzen');
return coords;
})
.fail(function(reason) {
console.log('Mapzen failed: ' + reason.message);
return self.comboCode(line1);
});
};
self.googleCode = function (line1) {
return google.code(line1, detroit).then(function (coords) {
cache.add(line1, detroit, coords);
console.log('Geocoder: using Google Maps');
return coords;
})
.fail(function (reason) {
console.log(reason.message);
if (reason.name === 'BadLocationError') {
// If Google choked on this location, then try outside Detroit-proper.
return google.code(line1, dearborn)
.then(function (coords) {
// Add to cache with detroit, since that's how we'll look it up next
// time.
cache.add(line1, detroit, coords);
console.log('Geocoder: using Google Maps Dearborn');
return coords;
})
.fail(function (reason) {
console.log(reason.message);
// If we still get a bad location, then give up.
if (reason.name === 'BadLocationError') {
throw reason;
}
// We've used Google too recently or something went wrong. Try mapzen.
return self.mapzenCode(line1);
});
}
// We've used Google too recently or something went wrong. Try mapzen.
return self.mapzenCode(line1);
});
};
self.code = function (line1) {
// Check the cache
return cache.get(line1, detroit).then(function (cachedCoords) {
if (cachedCoords !== null) {
console.log('Geocoder: using cache');
return cachedCoords;
}
// Cache miss. Try Google Maps.
metrics.cacheMiss();
return self.googleCode(line1);
}, function (reason) {
// Cache error.
console.log('Error in cache.get:');
console.log(reason.message);
// Try Google Maps.
return self.googleCode(line1);
});
};
return self;
}());