@hebcal/geo-sqlite 3.5.0 → 3.7.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 CHANGED
@@ -1,4 +1,4 @@
1
- /*! @hebcal/geo-sqlite v3.5.0 */
1
+ /*! @hebcal/geo-sqlite v3.7.0 */
2
2
  'use strict';
3
3
 
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
@@ -52,17 +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
+ ORDER BY Population DESC
58
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`;
59
65
  const GEONAME_COMPLETE_SQL = `SELECT geonameid, asciiname, admin1, country,
60
66
  population, latitude, longitude, timezone
61
67
  FROM geoname_fulltext
62
68
  WHERE longname MATCH ?
63
69
  GROUP BY geonameid
64
70
  ORDER BY population DESC
65
- LIMIT 10`;
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
+ };
66
125
  /** Wrapper around sqlite databases */
67
126
 
68
127
  class GeoDb {
@@ -217,6 +276,28 @@ class GeoDb {
217
276
  return null;
218
277
  }
219
278
  }
279
+ /**
280
+ * @private
281
+ * @param {any[]} res
282
+ * @return {Object[]}
283
+ */
284
+
285
+
286
+ static zipResultToObj(res) {
287
+ const obj = {
288
+ id: String(res.ZipCode),
289
+ value: `${res.CityMixedCase}, ${res.State} ${res.ZipCode}`,
290
+ admin1: res.State,
291
+ asciiname: res.CityMixedCase,
292
+ country: 'United States',
293
+ latitude: res.Latitude,
294
+ longitude: res.Longitude,
295
+ timezone: core.Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
296
+ population: res.Population,
297
+ geo: 'zip'
298
+ };
299
+ return obj;
300
+ }
220
301
  /**
221
302
  * Generates autocomplete results based on a query string
222
303
  * @param {string} qraw
@@ -234,33 +315,23 @@ class GeoDb {
234
315
  this.zipCompStmt = this.zipsDb.prepare(ZIP_COMPLETE_SQL);
235
316
  }
236
317
 
237
- return this.zipCompStmt.all(qraw + '%').map(res => {
238
- const obj = {
239
- id: String(res.ZipCode),
240
- value: `${res.CityMixedCase}, ${res.State} ${res.ZipCode}`,
241
- admin1: res.State,
242
- asciiname: res.CityMixedCase,
243
- country: 'United States',
244
- latitude: res.Latitude,
245
- longitude: res.Longitude,
246
- timezone: core.Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
247
- geo: 'zip'
248
- };
249
- return obj;
250
- });
318
+ return this.zipCompStmt.all(qraw + '%').map(GeoDb.zipResultToObj);
251
319
  } else {
252
320
  if (!this.geonamesCompStmt) {
253
321
  this.geonamesCompStmt = this.geonamesDb.prepare(GEONAME_COMPLETE_SQL);
254
322
  }
255
323
 
256
324
  qraw = qraw.replace(/\"/g, '""');
257
- return this.geonamesCompStmt.all(`"${qraw}*"`).map(res => {
325
+ const geoRows = this.geonamesCompStmt.all(`"${qraw}*"`);
326
+ const geoMatches = geoRows.map(res => {
258
327
  const country = res.country || '';
259
328
  const admin1 = res.admin1 || '';
260
329
  const obj = {
261
330
  id: res.geonameid,
262
331
  value: core.Location.geonameCityDescr(res.asciiname, admin1, country),
263
332
  asciiname: res.asciiname,
333
+ admin1,
334
+ country,
264
335
  latitude: res.latitude,
265
336
  longitude: res.longitude,
266
337
  timezone: res.timezone,
@@ -279,6 +350,32 @@ class GeoDb {
279
350
  obj.tokens = Array.from(new Set(res.asciiname.split(' ').concat(admin1.split(' '), country.split(' '))));
280
351
  return obj;
281
352
  });
353
+
354
+ if (!this.zipFulltextCompStmt) {
355
+ this.zipFulltextCompStmt = this.zipsDb.prepare(ZIP_FULLTEXT_COMPLETE_SQL);
356
+ }
357
+
358
+ const zipRows = this.zipFulltextCompStmt.all(`"${qraw}*"`);
359
+ const zipMatches = zipRows.map(GeoDb.zipResultToObj);
360
+ const map = new Map();
361
+
362
+ for (const obj of zipMatches) {
363
+ const key = [obj.asciiname, stateNames[obj.admin1], obj.country].join('|');
364
+
365
+ if (!map.has(key)) {
366
+ map.set(key, obj);
367
+ }
368
+ } // GeoNames takes priority over USA ZIP code matches
369
+
370
+
371
+ for (const obj of geoMatches) {
372
+ const key = [obj.asciiname, obj.admin1, obj.country].join('|');
373
+ map.set(key, obj);
374
+ }
375
+
376
+ const values = Array.from(map.values());
377
+ values.sort((a, b) => b.population - a.population);
378
+ return values.slice(0, 10);
282
379
  }
283
380
  }
284
381
  /** Reads entire ZIP database and caches in-memory */
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! @hebcal/geo-sqlite v3.5.0 */
1
+ /*! @hebcal/geo-sqlite v3.7.0 */
2
2
  import Database from 'better-sqlite3';
3
3
  import { Location, Locale } from '@hebcal/core';
4
4
  import pino from 'pino';
@@ -40,17 +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
+ ORDER BY Population DESC
46
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`;
47
53
  const GEONAME_COMPLETE_SQL = `SELECT geonameid, asciiname, admin1, country,
48
54
  population, latitude, longitude, timezone
49
55
  FROM geoname_fulltext
50
56
  WHERE longname MATCH ?
51
57
  GROUP BY geonameid
52
58
  ORDER BY population DESC
53
- LIMIT 10`;
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
+ };
54
113
  /** Wrapper around sqlite databases */
55
114
 
56
115
  class GeoDb {
@@ -205,6 +264,28 @@ class GeoDb {
205
264
  return null;
206
265
  }
207
266
  }
267
+ /**
268
+ * @private
269
+ * @param {any[]} res
270
+ * @return {Object[]}
271
+ */
272
+
273
+
274
+ static zipResultToObj(res) {
275
+ const obj = {
276
+ id: String(res.ZipCode),
277
+ value: `${res.CityMixedCase}, ${res.State} ${res.ZipCode}`,
278
+ admin1: res.State,
279
+ asciiname: res.CityMixedCase,
280
+ country: 'United States',
281
+ latitude: res.Latitude,
282
+ longitude: res.Longitude,
283
+ timezone: Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
284
+ population: res.Population,
285
+ geo: 'zip'
286
+ };
287
+ return obj;
288
+ }
208
289
  /**
209
290
  * Generates autocomplete results based on a query string
210
291
  * @param {string} qraw
@@ -222,33 +303,23 @@ class GeoDb {
222
303
  this.zipCompStmt = this.zipsDb.prepare(ZIP_COMPLETE_SQL);
223
304
  }
224
305
 
225
- return this.zipCompStmt.all(qraw + '%').map(res => {
226
- const obj = {
227
- id: String(res.ZipCode),
228
- value: `${res.CityMixedCase}, ${res.State} ${res.ZipCode}`,
229
- admin1: res.State,
230
- asciiname: res.CityMixedCase,
231
- country: 'United States',
232
- latitude: res.Latitude,
233
- longitude: res.Longitude,
234
- timezone: Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
235
- geo: 'zip'
236
- };
237
- return obj;
238
- });
306
+ return this.zipCompStmt.all(qraw + '%').map(GeoDb.zipResultToObj);
239
307
  } else {
240
308
  if (!this.geonamesCompStmt) {
241
309
  this.geonamesCompStmt = this.geonamesDb.prepare(GEONAME_COMPLETE_SQL);
242
310
  }
243
311
 
244
312
  qraw = qraw.replace(/\"/g, '""');
245
- return this.geonamesCompStmt.all(`"${qraw}*"`).map(res => {
313
+ const geoRows = this.geonamesCompStmt.all(`"${qraw}*"`);
314
+ const geoMatches = geoRows.map(res => {
246
315
  const country = res.country || '';
247
316
  const admin1 = res.admin1 || '';
248
317
  const obj = {
249
318
  id: res.geonameid,
250
319
  value: Location.geonameCityDescr(res.asciiname, admin1, country),
251
320
  asciiname: res.asciiname,
321
+ admin1,
322
+ country,
252
323
  latitude: res.latitude,
253
324
  longitude: res.longitude,
254
325
  timezone: res.timezone,
@@ -267,6 +338,32 @@ class GeoDb {
267
338
  obj.tokens = Array.from(new Set(res.asciiname.split(' ').concat(admin1.split(' '), country.split(' '))));
268
339
  return obj;
269
340
  });
341
+
342
+ if (!this.zipFulltextCompStmt) {
343
+ this.zipFulltextCompStmt = this.zipsDb.prepare(ZIP_FULLTEXT_COMPLETE_SQL);
344
+ }
345
+
346
+ const zipRows = this.zipFulltextCompStmt.all(`"${qraw}*"`);
347
+ const zipMatches = zipRows.map(GeoDb.zipResultToObj);
348
+ const map = new Map();
349
+
350
+ for (const obj of zipMatches) {
351
+ const key = [obj.asciiname, stateNames[obj.admin1], obj.country].join('|');
352
+
353
+ if (!map.has(key)) {
354
+ map.set(key, obj);
355
+ }
356
+ } // GeoNames takes priority over USA ZIP code matches
357
+
358
+
359
+ for (const obj of geoMatches) {
360
+ const key = [obj.asciiname, obj.admin1, obj.country].join('|');
361
+ map.set(key, obj);
362
+ }
363
+
364
+ const values = Array.from(map.values());
365
+ values.sort((a, b) => b.population - a.population);
366
+ return values.slice(0, 10);
270
367
  }
271
368
  }
272
369
  /** 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.5.0",
3
+ "version": "3.7.0",
4
4
  "author": "Michael J. Radwin (https://github.com/mjradwin)",
5
5
  "keywords": [
6
6
  "hebcal"
package/zips-dummy.sql CHANGED
@@ -6,5 +6,8 @@ CREATE TABLE ZIPCodes_Primary (
6
6
  Longitude decimal(12, 6),
7
7
  TimeZone char(2) NULL,
8
8
  DayLightSaving char(1) NULL,
9
- Elevation int NULL
9
+ Population int NULL
10
10
  );
11
+
12
+ CREATE VIRTUAL TABLE ZIPCodes_CityFullText
13
+ USING fts3(ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving,Population);