@hebcal/geo-sqlite 3.6.1 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +145 -26
- package/dist/index.mjs +145 -26
- package/package.json +11 -11
- package/zips-dummy.sql +3 -0
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @hebcal/geo-sqlite v3.
|
|
1
|
+
/*! @hebcal/geo-sqlite v3.8.0 */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
@@ -52,18 +52,76 @@ const ZIPCODE_SQL = `SELECT CityMixedCase,State,Latitude,Longitude,TimeZone,DayL
|
|
|
52
52
|
FROM ZIPCodes_Primary WHERE ZipCode = ?`;
|
|
53
53
|
const ZIPCODE_ALL_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving
|
|
54
54
|
FROM ZIPCodes_Primary`;
|
|
55
|
-
const ZIP_COMPLETE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving
|
|
55
|
+
const ZIP_COMPLETE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving,Population
|
|
56
56
|
FROM ZIPCodes_Primary
|
|
57
57
|
WHERE ZipCode LIKE ?
|
|
58
58
|
ORDER BY Population DESC
|
|
59
59
|
LIMIT 10`;
|
|
60
|
+
const ZIP_FULLTEXT_COMPLETE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving,Population
|
|
61
|
+
FROM ZIPCodes_CityFullText
|
|
62
|
+
WHERE CityMixedCase MATCH ?
|
|
63
|
+
ORDER BY Population DESC
|
|
64
|
+
LIMIT 15`;
|
|
60
65
|
const GEONAME_COMPLETE_SQL = `SELECT geonameid, asciiname, admin1, country,
|
|
61
66
|
population, latitude, longitude, timezone
|
|
62
67
|
FROM geoname_fulltext
|
|
63
68
|
WHERE longname MATCH ?
|
|
64
69
|
GROUP BY geonameid
|
|
65
70
|
ORDER BY population DESC
|
|
66
|
-
LIMIT
|
|
71
|
+
LIMIT 15`;
|
|
72
|
+
const stateNames = {
|
|
73
|
+
'AK': 'Alaska',
|
|
74
|
+
'AL': 'Alabama',
|
|
75
|
+
'AR': 'Arkansas',
|
|
76
|
+
'AZ': 'Arizona',
|
|
77
|
+
'CA': 'California',
|
|
78
|
+
'CO': 'Colorado',
|
|
79
|
+
'CT': 'Connecticut',
|
|
80
|
+
'DC': 'Washington, D.C.',
|
|
81
|
+
'DE': 'Delaware',
|
|
82
|
+
'FL': 'Florida',
|
|
83
|
+
'GA': 'Georgia',
|
|
84
|
+
'HI': 'Hawaii',
|
|
85
|
+
'IA': 'Iowa',
|
|
86
|
+
'ID': 'Idaho',
|
|
87
|
+
'IL': 'Illinois',
|
|
88
|
+
'IN': 'Indiana',
|
|
89
|
+
'KS': 'Kansas',
|
|
90
|
+
'KY': 'Kentucky',
|
|
91
|
+
'LA': 'Louisiana',
|
|
92
|
+
'MA': 'Massachusetts',
|
|
93
|
+
'MD': 'Maryland',
|
|
94
|
+
'ME': 'Maine',
|
|
95
|
+
'MI': 'Michigan',
|
|
96
|
+
'MN': 'Minnesota',
|
|
97
|
+
'MO': 'Missouri',
|
|
98
|
+
'MS': 'Mississippi',
|
|
99
|
+
'MT': 'Montana',
|
|
100
|
+
'NC': 'North Carolina',
|
|
101
|
+
'ND': 'North Dakota',
|
|
102
|
+
'NE': 'Nebraska',
|
|
103
|
+
'NH': 'New Hampshire',
|
|
104
|
+
'NJ': 'New Jersey',
|
|
105
|
+
'NM': 'New Mexico',
|
|
106
|
+
'NV': 'Nevada',
|
|
107
|
+
'NY': 'New York',
|
|
108
|
+
'OH': 'Ohio',
|
|
109
|
+
'OK': 'Oklahoma',
|
|
110
|
+
'OR': 'Oregon',
|
|
111
|
+
'PA': 'Pennsylvania',
|
|
112
|
+
'RI': 'Rhode Island',
|
|
113
|
+
'SC': 'South Carolina',
|
|
114
|
+
'SD': 'South Dakota',
|
|
115
|
+
'TN': 'Tennessee',
|
|
116
|
+
'TX': 'Texas',
|
|
117
|
+
'UT': 'Utah',
|
|
118
|
+
'VA': 'Virginia',
|
|
119
|
+
'VT': 'Vermont',
|
|
120
|
+
'WA': 'Washington',
|
|
121
|
+
'WI': 'Wisconsin',
|
|
122
|
+
'WV': 'West Virginia',
|
|
123
|
+
'WY': 'Wyoming'
|
|
124
|
+
};
|
|
67
125
|
/** Wrapper around sqlite databases */
|
|
68
126
|
|
|
69
127
|
class GeoDb {
|
|
@@ -83,14 +141,32 @@ class GeoDb {
|
|
|
83
141
|
fileMustExist: true
|
|
84
142
|
});
|
|
85
143
|
this.zipStmt = this.zipsDb.prepare(ZIPCODE_SQL);
|
|
144
|
+
/** @type {Map<string, Location>} */
|
|
145
|
+
|
|
86
146
|
this.zipCache = new Map();
|
|
87
147
|
this.geonamesStmt = this.geonamesDb.prepare(GEONAME_SQL);
|
|
148
|
+
/** @type {Map<number, Location>} */
|
|
149
|
+
|
|
88
150
|
this.geonamesCache = new Map();
|
|
151
|
+
/** @type {Map<string, Location>} */
|
|
152
|
+
|
|
89
153
|
this.legacyCities = new Map();
|
|
90
154
|
|
|
91
155
|
for (const [name, id] of Object.entries(city2geonameid)) {
|
|
92
156
|
this.legacyCities.set(GeoDb.munge(name), id);
|
|
93
157
|
}
|
|
158
|
+
|
|
159
|
+
const stmt = this.geonamesDb.prepare(`SELECT ISO, Country FROM country WHERE Country <> ''`);
|
|
160
|
+
const rows = stmt.all();
|
|
161
|
+
const map = new Map();
|
|
162
|
+
|
|
163
|
+
for (const row of rows) {
|
|
164
|
+
map.set(row.ISO, row.Country);
|
|
165
|
+
}
|
|
166
|
+
/** @type {Map<string, string>} */
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
this.countryNames = map;
|
|
94
170
|
}
|
|
95
171
|
/** Closes database handles */
|
|
96
172
|
|
|
@@ -116,19 +192,20 @@ class GeoDb {
|
|
|
116
192
|
|
|
117
193
|
|
|
118
194
|
lookupZip(zip) {
|
|
119
|
-
const
|
|
195
|
+
const zip5 = zip.trim().substring(0, 5);
|
|
196
|
+
const found = this.zipCache.get(zip5);
|
|
120
197
|
if (typeof found !== 'undefined') return found;
|
|
121
|
-
const result = this.zipStmt.get(
|
|
198
|
+
const result = this.zipStmt.get(zip5);
|
|
122
199
|
|
|
123
200
|
if (!result) {
|
|
124
|
-
if (this.logger) this.logger.warn(`GeoDb: unknown zipcode=${
|
|
125
|
-
this.zipCache.set(
|
|
201
|
+
if (this.logger) this.logger.warn(`GeoDb: unknown zipcode=${zip5}`);
|
|
202
|
+
this.zipCache.set(zip5, null);
|
|
126
203
|
return null;
|
|
127
204
|
}
|
|
128
205
|
|
|
129
|
-
result.ZipCode = String(
|
|
206
|
+
result.ZipCode = String(zip5);
|
|
130
207
|
const location = this.makeZipLocation(result);
|
|
131
|
-
this.zipCache.set(
|
|
208
|
+
this.zipCache.set(zip5, location);
|
|
132
209
|
return location;
|
|
133
210
|
}
|
|
134
211
|
/**
|
|
@@ -218,6 +295,28 @@ class GeoDb {
|
|
|
218
295
|
return null;
|
|
219
296
|
}
|
|
220
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* @private
|
|
300
|
+
* @param {any[]} res
|
|
301
|
+
* @return {Object[]}
|
|
302
|
+
*/
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
static zipResultToObj(res) {
|
|
306
|
+
const obj = {
|
|
307
|
+
id: String(res.ZipCode),
|
|
308
|
+
value: `${res.CityMixedCase}, ${res.State} ${res.ZipCode}`,
|
|
309
|
+
admin1: res.State,
|
|
310
|
+
asciiname: res.CityMixedCase,
|
|
311
|
+
country: 'United States',
|
|
312
|
+
latitude: res.Latitude,
|
|
313
|
+
longitude: res.Longitude,
|
|
314
|
+
timezone: core.Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
|
|
315
|
+
population: res.Population,
|
|
316
|
+
geo: 'zip'
|
|
317
|
+
};
|
|
318
|
+
return obj;
|
|
319
|
+
}
|
|
221
320
|
/**
|
|
222
321
|
* Generates autocomplete results based on a query string
|
|
223
322
|
* @param {string} qraw
|
|
@@ -226,43 +325,37 @@ class GeoDb {
|
|
|
226
325
|
|
|
227
326
|
|
|
228
327
|
autoComplete(qraw) {
|
|
328
|
+
qraw = qraw.trim();
|
|
329
|
+
|
|
229
330
|
if (qraw.length === 0) {
|
|
230
331
|
return [];
|
|
231
332
|
}
|
|
232
333
|
|
|
233
|
-
|
|
334
|
+
const firstCharCode = qraw.charCodeAt(0);
|
|
335
|
+
|
|
336
|
+
if (firstCharCode >= 48 && firstCharCode <= 57) {
|
|
234
337
|
if (!this.zipCompStmt) {
|
|
235
338
|
this.zipCompStmt = this.zipsDb.prepare(ZIP_COMPLETE_SQL);
|
|
236
339
|
}
|
|
237
340
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
id: String(res.ZipCode),
|
|
241
|
-
value: `${res.CityMixedCase}, ${res.State} ${res.ZipCode}`,
|
|
242
|
-
admin1: res.State,
|
|
243
|
-
asciiname: res.CityMixedCase,
|
|
244
|
-
country: 'United States',
|
|
245
|
-
latitude: res.Latitude,
|
|
246
|
-
longitude: res.Longitude,
|
|
247
|
-
timezone: core.Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
|
|
248
|
-
population: res.Population,
|
|
249
|
-
geo: 'zip'
|
|
250
|
-
};
|
|
251
|
-
return obj;
|
|
252
|
-
});
|
|
341
|
+
const zip5 = qraw.substring(0, 5);
|
|
342
|
+
return this.zipCompStmt.all(zip5 + '%').map(GeoDb.zipResultToObj);
|
|
253
343
|
} else {
|
|
254
344
|
if (!this.geonamesCompStmt) {
|
|
255
345
|
this.geonamesCompStmt = this.geonamesDb.prepare(GEONAME_COMPLETE_SQL);
|
|
256
346
|
}
|
|
257
347
|
|
|
258
348
|
qraw = qraw.replace(/\"/g, '""');
|
|
259
|
-
|
|
349
|
+
const geoRows = this.geonamesCompStmt.all(`"${qraw}*"`);
|
|
350
|
+
const geoMatches = geoRows.map(res => {
|
|
260
351
|
const country = res.country || '';
|
|
261
352
|
const admin1 = res.admin1 || '';
|
|
262
353
|
const obj = {
|
|
263
354
|
id: res.geonameid,
|
|
264
355
|
value: core.Location.geonameCityDescr(res.asciiname, admin1, country),
|
|
265
356
|
asciiname: res.asciiname,
|
|
357
|
+
admin1,
|
|
358
|
+
country,
|
|
266
359
|
latitude: res.latitude,
|
|
267
360
|
longitude: res.longitude,
|
|
268
361
|
timezone: res.timezone,
|
|
@@ -281,6 +374,32 @@ class GeoDb {
|
|
|
281
374
|
obj.tokens = Array.from(new Set(res.asciiname.split(' ').concat(admin1.split(' '), country.split(' '))));
|
|
282
375
|
return obj;
|
|
283
376
|
});
|
|
377
|
+
|
|
378
|
+
if (!this.zipFulltextCompStmt) {
|
|
379
|
+
this.zipFulltextCompStmt = this.zipsDb.prepare(ZIP_FULLTEXT_COMPLETE_SQL);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const zipRows = this.zipFulltextCompStmt.all(`"${qraw}*"`);
|
|
383
|
+
const zipMatches = zipRows.map(GeoDb.zipResultToObj);
|
|
384
|
+
const map = new Map();
|
|
385
|
+
|
|
386
|
+
for (const obj of zipMatches) {
|
|
387
|
+
const key = [obj.asciiname, stateNames[obj.admin1], obj.country].join('|');
|
|
388
|
+
|
|
389
|
+
if (!map.has(key)) {
|
|
390
|
+
map.set(key, obj);
|
|
391
|
+
}
|
|
392
|
+
} // GeoNames takes priority over USA ZIP code matches
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
for (const obj of geoMatches) {
|
|
396
|
+
const key = [obj.asciiname, obj.admin1, obj.country].join('|');
|
|
397
|
+
map.set(key, obj);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const values = Array.from(map.values());
|
|
401
|
+
values.sort((a, b) => b.population - a.population);
|
|
402
|
+
return values.slice(0, 10);
|
|
284
403
|
}
|
|
285
404
|
}
|
|
286
405
|
/** Reads entire ZIP database and caches in-memory */
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @hebcal/geo-sqlite v3.
|
|
1
|
+
/*! @hebcal/geo-sqlite v3.8.0 */
|
|
2
2
|
import Database from 'better-sqlite3';
|
|
3
3
|
import { Location, Locale } from '@hebcal/core';
|
|
4
4
|
import pino from 'pino';
|
|
@@ -40,18 +40,76 @@ const ZIPCODE_SQL = `SELECT CityMixedCase,State,Latitude,Longitude,TimeZone,DayL
|
|
|
40
40
|
FROM ZIPCodes_Primary WHERE ZipCode = ?`;
|
|
41
41
|
const ZIPCODE_ALL_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving
|
|
42
42
|
FROM ZIPCodes_Primary`;
|
|
43
|
-
const ZIP_COMPLETE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving
|
|
43
|
+
const ZIP_COMPLETE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving,Population
|
|
44
44
|
FROM ZIPCodes_Primary
|
|
45
45
|
WHERE ZipCode LIKE ?
|
|
46
46
|
ORDER BY Population DESC
|
|
47
47
|
LIMIT 10`;
|
|
48
|
+
const ZIP_FULLTEXT_COMPLETE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving,Population
|
|
49
|
+
FROM ZIPCodes_CityFullText
|
|
50
|
+
WHERE CityMixedCase MATCH ?
|
|
51
|
+
ORDER BY Population DESC
|
|
52
|
+
LIMIT 15`;
|
|
48
53
|
const GEONAME_COMPLETE_SQL = `SELECT geonameid, asciiname, admin1, country,
|
|
49
54
|
population, latitude, longitude, timezone
|
|
50
55
|
FROM geoname_fulltext
|
|
51
56
|
WHERE longname MATCH ?
|
|
52
57
|
GROUP BY geonameid
|
|
53
58
|
ORDER BY population DESC
|
|
54
|
-
LIMIT
|
|
59
|
+
LIMIT 15`;
|
|
60
|
+
const stateNames = {
|
|
61
|
+
'AK': 'Alaska',
|
|
62
|
+
'AL': 'Alabama',
|
|
63
|
+
'AR': 'Arkansas',
|
|
64
|
+
'AZ': 'Arizona',
|
|
65
|
+
'CA': 'California',
|
|
66
|
+
'CO': 'Colorado',
|
|
67
|
+
'CT': 'Connecticut',
|
|
68
|
+
'DC': 'Washington, D.C.',
|
|
69
|
+
'DE': 'Delaware',
|
|
70
|
+
'FL': 'Florida',
|
|
71
|
+
'GA': 'Georgia',
|
|
72
|
+
'HI': 'Hawaii',
|
|
73
|
+
'IA': 'Iowa',
|
|
74
|
+
'ID': 'Idaho',
|
|
75
|
+
'IL': 'Illinois',
|
|
76
|
+
'IN': 'Indiana',
|
|
77
|
+
'KS': 'Kansas',
|
|
78
|
+
'KY': 'Kentucky',
|
|
79
|
+
'LA': 'Louisiana',
|
|
80
|
+
'MA': 'Massachusetts',
|
|
81
|
+
'MD': 'Maryland',
|
|
82
|
+
'ME': 'Maine',
|
|
83
|
+
'MI': 'Michigan',
|
|
84
|
+
'MN': 'Minnesota',
|
|
85
|
+
'MO': 'Missouri',
|
|
86
|
+
'MS': 'Mississippi',
|
|
87
|
+
'MT': 'Montana',
|
|
88
|
+
'NC': 'North Carolina',
|
|
89
|
+
'ND': 'North Dakota',
|
|
90
|
+
'NE': 'Nebraska',
|
|
91
|
+
'NH': 'New Hampshire',
|
|
92
|
+
'NJ': 'New Jersey',
|
|
93
|
+
'NM': 'New Mexico',
|
|
94
|
+
'NV': 'Nevada',
|
|
95
|
+
'NY': 'New York',
|
|
96
|
+
'OH': 'Ohio',
|
|
97
|
+
'OK': 'Oklahoma',
|
|
98
|
+
'OR': 'Oregon',
|
|
99
|
+
'PA': 'Pennsylvania',
|
|
100
|
+
'RI': 'Rhode Island',
|
|
101
|
+
'SC': 'South Carolina',
|
|
102
|
+
'SD': 'South Dakota',
|
|
103
|
+
'TN': 'Tennessee',
|
|
104
|
+
'TX': 'Texas',
|
|
105
|
+
'UT': 'Utah',
|
|
106
|
+
'VA': 'Virginia',
|
|
107
|
+
'VT': 'Vermont',
|
|
108
|
+
'WA': 'Washington',
|
|
109
|
+
'WI': 'Wisconsin',
|
|
110
|
+
'WV': 'West Virginia',
|
|
111
|
+
'WY': 'Wyoming'
|
|
112
|
+
};
|
|
55
113
|
/** Wrapper around sqlite databases */
|
|
56
114
|
|
|
57
115
|
class GeoDb {
|
|
@@ -71,14 +129,32 @@ class GeoDb {
|
|
|
71
129
|
fileMustExist: true
|
|
72
130
|
});
|
|
73
131
|
this.zipStmt = this.zipsDb.prepare(ZIPCODE_SQL);
|
|
132
|
+
/** @type {Map<string, Location>} */
|
|
133
|
+
|
|
74
134
|
this.zipCache = new Map();
|
|
75
135
|
this.geonamesStmt = this.geonamesDb.prepare(GEONAME_SQL);
|
|
136
|
+
/** @type {Map<number, Location>} */
|
|
137
|
+
|
|
76
138
|
this.geonamesCache = new Map();
|
|
139
|
+
/** @type {Map<string, Location>} */
|
|
140
|
+
|
|
77
141
|
this.legacyCities = new Map();
|
|
78
142
|
|
|
79
143
|
for (const [name, id] of Object.entries(city2geonameid)) {
|
|
80
144
|
this.legacyCities.set(GeoDb.munge(name), id);
|
|
81
145
|
}
|
|
146
|
+
|
|
147
|
+
const stmt = this.geonamesDb.prepare(`SELECT ISO, Country FROM country WHERE Country <> ''`);
|
|
148
|
+
const rows = stmt.all();
|
|
149
|
+
const map = new Map();
|
|
150
|
+
|
|
151
|
+
for (const row of rows) {
|
|
152
|
+
map.set(row.ISO, row.Country);
|
|
153
|
+
}
|
|
154
|
+
/** @type {Map<string, string>} */
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
this.countryNames = map;
|
|
82
158
|
}
|
|
83
159
|
/** Closes database handles */
|
|
84
160
|
|
|
@@ -104,19 +180,20 @@ class GeoDb {
|
|
|
104
180
|
|
|
105
181
|
|
|
106
182
|
lookupZip(zip) {
|
|
107
|
-
const
|
|
183
|
+
const zip5 = zip.trim().substring(0, 5);
|
|
184
|
+
const found = this.zipCache.get(zip5);
|
|
108
185
|
if (typeof found !== 'undefined') return found;
|
|
109
|
-
const result = this.zipStmt.get(
|
|
186
|
+
const result = this.zipStmt.get(zip5);
|
|
110
187
|
|
|
111
188
|
if (!result) {
|
|
112
|
-
if (this.logger) this.logger.warn(`GeoDb: unknown zipcode=${
|
|
113
|
-
this.zipCache.set(
|
|
189
|
+
if (this.logger) this.logger.warn(`GeoDb: unknown zipcode=${zip5}`);
|
|
190
|
+
this.zipCache.set(zip5, null);
|
|
114
191
|
return null;
|
|
115
192
|
}
|
|
116
193
|
|
|
117
|
-
result.ZipCode = String(
|
|
194
|
+
result.ZipCode = String(zip5);
|
|
118
195
|
const location = this.makeZipLocation(result);
|
|
119
|
-
this.zipCache.set(
|
|
196
|
+
this.zipCache.set(zip5, location);
|
|
120
197
|
return location;
|
|
121
198
|
}
|
|
122
199
|
/**
|
|
@@ -206,6 +283,28 @@ class GeoDb {
|
|
|
206
283
|
return null;
|
|
207
284
|
}
|
|
208
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* @private
|
|
288
|
+
* @param {any[]} res
|
|
289
|
+
* @return {Object[]}
|
|
290
|
+
*/
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
static zipResultToObj(res) {
|
|
294
|
+
const obj = {
|
|
295
|
+
id: String(res.ZipCode),
|
|
296
|
+
value: `${res.CityMixedCase}, ${res.State} ${res.ZipCode}`,
|
|
297
|
+
admin1: res.State,
|
|
298
|
+
asciiname: res.CityMixedCase,
|
|
299
|
+
country: 'United States',
|
|
300
|
+
latitude: res.Latitude,
|
|
301
|
+
longitude: res.Longitude,
|
|
302
|
+
timezone: Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
|
|
303
|
+
population: res.Population,
|
|
304
|
+
geo: 'zip'
|
|
305
|
+
};
|
|
306
|
+
return obj;
|
|
307
|
+
}
|
|
209
308
|
/**
|
|
210
309
|
* Generates autocomplete results based on a query string
|
|
211
310
|
* @param {string} qraw
|
|
@@ -214,43 +313,37 @@ class GeoDb {
|
|
|
214
313
|
|
|
215
314
|
|
|
216
315
|
autoComplete(qraw) {
|
|
316
|
+
qraw = qraw.trim();
|
|
317
|
+
|
|
217
318
|
if (qraw.length === 0) {
|
|
218
319
|
return [];
|
|
219
320
|
}
|
|
220
321
|
|
|
221
|
-
|
|
322
|
+
const firstCharCode = qraw.charCodeAt(0);
|
|
323
|
+
|
|
324
|
+
if (firstCharCode >= 48 && firstCharCode <= 57) {
|
|
222
325
|
if (!this.zipCompStmt) {
|
|
223
326
|
this.zipCompStmt = this.zipsDb.prepare(ZIP_COMPLETE_SQL);
|
|
224
327
|
}
|
|
225
328
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
id: String(res.ZipCode),
|
|
229
|
-
value: `${res.CityMixedCase}, ${res.State} ${res.ZipCode}`,
|
|
230
|
-
admin1: res.State,
|
|
231
|
-
asciiname: res.CityMixedCase,
|
|
232
|
-
country: 'United States',
|
|
233
|
-
latitude: res.Latitude,
|
|
234
|
-
longitude: res.Longitude,
|
|
235
|
-
timezone: Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
|
|
236
|
-
population: res.Population,
|
|
237
|
-
geo: 'zip'
|
|
238
|
-
};
|
|
239
|
-
return obj;
|
|
240
|
-
});
|
|
329
|
+
const zip5 = qraw.substring(0, 5);
|
|
330
|
+
return this.zipCompStmt.all(zip5 + '%').map(GeoDb.zipResultToObj);
|
|
241
331
|
} else {
|
|
242
332
|
if (!this.geonamesCompStmt) {
|
|
243
333
|
this.geonamesCompStmt = this.geonamesDb.prepare(GEONAME_COMPLETE_SQL);
|
|
244
334
|
}
|
|
245
335
|
|
|
246
336
|
qraw = qraw.replace(/\"/g, '""');
|
|
247
|
-
|
|
337
|
+
const geoRows = this.geonamesCompStmt.all(`"${qraw}*"`);
|
|
338
|
+
const geoMatches = geoRows.map(res => {
|
|
248
339
|
const country = res.country || '';
|
|
249
340
|
const admin1 = res.admin1 || '';
|
|
250
341
|
const obj = {
|
|
251
342
|
id: res.geonameid,
|
|
252
343
|
value: Location.geonameCityDescr(res.asciiname, admin1, country),
|
|
253
344
|
asciiname: res.asciiname,
|
|
345
|
+
admin1,
|
|
346
|
+
country,
|
|
254
347
|
latitude: res.latitude,
|
|
255
348
|
longitude: res.longitude,
|
|
256
349
|
timezone: res.timezone,
|
|
@@ -269,6 +362,32 @@ class GeoDb {
|
|
|
269
362
|
obj.tokens = Array.from(new Set(res.asciiname.split(' ').concat(admin1.split(' '), country.split(' '))));
|
|
270
363
|
return obj;
|
|
271
364
|
});
|
|
365
|
+
|
|
366
|
+
if (!this.zipFulltextCompStmt) {
|
|
367
|
+
this.zipFulltextCompStmt = this.zipsDb.prepare(ZIP_FULLTEXT_COMPLETE_SQL);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const zipRows = this.zipFulltextCompStmt.all(`"${qraw}*"`);
|
|
371
|
+
const zipMatches = zipRows.map(GeoDb.zipResultToObj);
|
|
372
|
+
const map = new Map();
|
|
373
|
+
|
|
374
|
+
for (const obj of zipMatches) {
|
|
375
|
+
const key = [obj.asciiname, stateNames[obj.admin1], obj.country].join('|');
|
|
376
|
+
|
|
377
|
+
if (!map.has(key)) {
|
|
378
|
+
map.set(key, obj);
|
|
379
|
+
}
|
|
380
|
+
} // GeoNames takes priority over USA ZIP code matches
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
for (const obj of geoMatches) {
|
|
384
|
+
const key = [obj.asciiname, obj.admin1, obj.country].join('|');
|
|
385
|
+
map.set(key, obj);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const values = Array.from(map.values());
|
|
389
|
+
values.sort((a, b) => b.population - a.population);
|
|
390
|
+
return values.slice(0, 10);
|
|
272
391
|
}
|
|
273
392
|
}
|
|
274
393
|
/** Reads entire ZIP database and caches in-memory */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hebcal/geo-sqlite",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"author": "Michael J. Radwin (https://github.com/mjradwin)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hebcal"
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
"geo-sqlite.d.ts"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@hebcal/core": "^3.
|
|
32
|
-
"better-sqlite3": "^7.
|
|
33
|
-
"pino": "^7.6.
|
|
34
|
-
"pino-pretty": "^7.
|
|
31
|
+
"@hebcal/core": "^3.33.2",
|
|
32
|
+
"better-sqlite3": "^7.5.0",
|
|
33
|
+
"pino": "^7.6.4",
|
|
34
|
+
"pino-pretty": "^7.5.0"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "rollup -c",
|
|
@@ -60,19 +60,19 @@
|
|
|
60
60
|
"license": "BSD-2-Clause",
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@ava/babel": "^2.0.0",
|
|
63
|
-
"@babel/core": "^7.16.
|
|
63
|
+
"@babel/core": "^7.16.12",
|
|
64
64
|
"@babel/polyfill": "^7.12.1",
|
|
65
|
-
"@babel/preset-env": "^7.16.
|
|
66
|
-
"@babel/register": "^7.16.
|
|
65
|
+
"@babel/preset-env": "^7.16.11",
|
|
66
|
+
"@babel/register": "^7.16.9",
|
|
67
67
|
"@rollup/plugin-babel": "^5.3.0",
|
|
68
68
|
"@rollup/plugin-commonjs": "^21.0.1",
|
|
69
69
|
"@rollup/plugin-json": "^4.1.0",
|
|
70
70
|
"@rollup/plugin-node-resolve": "^13.1.3",
|
|
71
71
|
"ava": "^3.15.0",
|
|
72
|
-
"eslint": "^8.
|
|
72
|
+
"eslint": "^8.7.0",
|
|
73
73
|
"eslint-config-google": "^0.14.0",
|
|
74
|
-
"jsdoc": "^3.6.
|
|
74
|
+
"jsdoc": "^3.6.9",
|
|
75
75
|
"jsdoc-to-markdown": "^7.1.0",
|
|
76
|
-
"rollup": "^2.
|
|
76
|
+
"rollup": "^2.66.0"
|
|
77
77
|
}
|
|
78
78
|
}
|