@hebcal/geo-sqlite 5.0.0 → 5.0.2

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.
@@ -1,4 +1,4 @@
1
- /*! @hebcal/geo-sqlite v5.0.0 */
1
+ /*! @hebcal/geo-sqlite v5.0.2 */
2
2
  'use strict';
3
3
 
4
4
  var Database = require('better-sqlite3');
@@ -18,7 +18,10 @@ let a=[["\0","","","","","","","","\b","\t","\n","\v","\f","\r","","","
18
18
  * @return {string}
19
19
  */
20
20
  function munge(s) {
21
- return s.toLowerCase().replace(/'/g, '').replace(/ /g, '').replace(/\+/g, '');
21
+ return s.toLowerCase()
22
+ .replace(/'/g, '')
23
+ .replace(/ /g, '')
24
+ .replace(/\+/g, '');
22
25
  }
23
26
 
24
27
  const GEONAME_SQL = `SELECT
@@ -37,6 +40,7 @@ LEFT JOIN country c on g.country = c.iso
37
40
  LEFT JOIN admin1 a on g.country||'.'||g.admin1 = a.key
38
41
  WHERE g.geonameid = ?
39
42
  `;
43
+
40
44
  const GEONAME_ALL_SQL = `SELECT
41
45
  g.geonameid as geonameid,
42
46
  g.name as name,
@@ -53,27 +57,35 @@ FROM geoname g
53
57
  LEFT JOIN country c on g.country = c.iso
54
58
  LEFT JOIN admin1 a on g.country||'.'||g.admin1 = a.key
55
59
  `;
60
+
56
61
  const ZIPCODE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,Elevation,
57
62
  TimeZone,DayLightSaving,Population
58
63
  FROM ZIPCodes_Primary WHERE ZipCode = ?`;
64
+
59
65
  const ZIPCODE_ALL_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,Elevation,
60
66
  TimeZone,DayLightSaving,Population
61
67
  FROM ZIPCodes_Primary`;
68
+
62
69
  const ZIP_COMPLETE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving,Population
63
70
  FROM ZIPCodes_Primary
64
71
  WHERE ZipCode LIKE ?
65
72
  ORDER BY Population DESC
66
73
  LIMIT 10`;
67
- const ZIP_FULLTEXT_COMPLETE_SQL = `SELECT ZipCode
74
+
75
+ const ZIP_FULLTEXT_COMPLETE_SQL =
76
+ `SELECT ZipCode
68
77
  FROM ZIPCodes_CityFullText5
69
78
  WHERE ZIPCodes_CityFullText5 MATCH ?
70
79
  ORDER BY Population DESC
71
80
  LIMIT 20`;
72
- const GEONAME_COMPLETE_SQL = `SELECT geonameid, longname, city, admin1, country
81
+
82
+ const GEONAME_COMPLETE_SQL =
83
+ `SELECT geonameid, longname, city, admin1, country
73
84
  FROM geoname_fulltext
74
85
  WHERE geoname_fulltext MATCH ?
75
86
  ORDER BY population DESC
76
87
  LIMIT 20`;
88
+
77
89
  const stateNames = {
78
90
  'AK': 'Alaska',
79
91
  'AL': 'Alabama',
@@ -125,7 +137,7 @@ const stateNames = {
125
137
  'WA': 'Washington',
126
138
  'WI': 'Wisconsin',
127
139
  'WV': 'West Virginia',
128
- 'WY': 'Wyoming'
140
+ 'WY': 'Wyoming',
129
141
  };
130
142
 
131
143
  /** Wrapper around sqlite databases */
@@ -138,13 +150,9 @@ class GeoDb {
138
150
  constructor(logger, zipsFilename, geonamesFilename) {
139
151
  this.logger = logger;
140
152
  if (logger) logger.info(`GeoDb: opening ${zipsFilename}...`);
141
- this.zipsDb = new Database(zipsFilename, {
142
- fileMustExist: true
143
- });
153
+ this.zipsDb = new Database(zipsFilename, {fileMustExist: true});
144
154
  if (logger) logger.info(`GeoDb: opening ${geonamesFilename}...`);
145
- this.geonamesDb = new Database(geonamesFilename, {
146
- fileMustExist: true
147
- });
155
+ this.geonamesDb = new Database(geonamesFilename, {fileMustExist: true});
148
156
  this.zipStmt = this.zipsDb.prepare(ZIPCODE_SQL);
149
157
  /** @type {Map<string, Location>} */
150
158
  this.zipCache = new Map();
@@ -164,6 +172,7 @@ class GeoDb {
164
172
  }
165
173
  /** @type {Map<string, string>} */
166
174
  this.countryNames = map;
175
+ if (logger) logger.info(`GeoDb: ${map.size} countries, ${this.legacyCities.size} legacy cities`);
167
176
  }
168
177
 
169
178
  /** Closes database handles */
@@ -214,15 +223,14 @@ class GeoDb {
214
223
  const zip = result.ZipCode;
215
224
  const tzid = core.Location.getUsaTzid(result.State, result.TimeZone, result.DayLightSaving);
216
225
  const cityDescr = `${result.CityMixedCase}, ${result.State} ${zip}`;
217
- const location = new core.Location(result.Latitude, result.Longitude, false, tzid, cityDescr, 'US', zip);
226
+ const elevation = result?.Elevation > 0 ? result.Elevation : 0;
227
+ const location = new core.Location(result.Latitude, result.Longitude, false, tzid, cityDescr,
228
+ 'US', zip, elevation);
218
229
  location.admin1 = location.state = result.State;
219
230
  location.stateName = stateNames[location.state];
220
231
  location.geo = 'zip';
221
232
  location.zip = zip;
222
233
  location.population = result.Population;
223
- if (result.Elevation && result.Elevation > 0) {
224
- location.elevation = result.Elevation;
225
- }
226
234
  return location;
227
235
  }
228
236
 
@@ -293,7 +301,17 @@ class GeoDb {
293
301
  const country = result.country || '';
294
302
  const admin1 = result.admin1 || '';
295
303
  const cityDescr = GeoDb.geonameCityDescr(result.name, admin1, country);
296
- const location = new core.Location(result.latitude, result.longitude, result.cc == 'IL', result.timezone, cityDescr, result.cc, geonameid);
304
+ const elevation = result?.elevation > 0 ? result.elevation : 0;
305
+ const location = new core.Location(
306
+ result.latitude,
307
+ result.longitude,
308
+ result.cc == 'IL',
309
+ result.timezone,
310
+ cityDescr,
311
+ result.cc,
312
+ geonameid,
313
+ elevation,
314
+ );
297
315
  location.geo = 'geoname';
298
316
  location.geonameid = geonameid;
299
317
  location.asciiname = result.asciiname;
@@ -306,9 +324,6 @@ class GeoDb {
306
324
  if (result.population) {
307
325
  location.population = result.population;
308
326
  }
309
- if (result.elevation && result.elevation > 0) {
310
- location.elevation = result.elevation;
311
- }
312
327
  return location;
313
328
  }
314
329
 
@@ -348,7 +363,7 @@ class GeoDb {
348
363
  longitude: res.Longitude,
349
364
  timezone: core.Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
350
365
  population: res.Population,
351
- geo: 'zip'
366
+ geo: 'zip',
352
367
  };
353
368
  if (res.Elevation && res.Elevation > 0) {
354
369
  obj.elevation = res.Elevation;
@@ -362,7 +377,7 @@ class GeoDb {
362
377
  * @param {boolean} latlong
363
378
  * @return {Object[]}
364
379
  */
365
- autoComplete(qraw, latlong = false) {
380
+ autoComplete(qraw, latlong=false) {
366
381
  qraw = qraw.trim();
367
382
  if (qraw.length === 0) {
368
383
  return [];
@@ -389,7 +404,7 @@ class GeoDb {
389
404
  geoRows.push(row);
390
405
  }
391
406
  }
392
- const geoMatches = geoRows.map(res => {
407
+ const geoMatches = geoRows.map((res) => {
393
408
  const loc = this.lookupGeoname(res.geonameid);
394
409
  return this.geonameLocToAutocomplete(loc, res);
395
410
  });
@@ -397,7 +412,7 @@ class GeoDb {
397
412
  this.zipFulltextCompStmt = this.zipsDb.prepare(ZIP_FULLTEXT_COMPLETE_SQL);
398
413
  }
399
414
  const zipRows = this.zipFulltextCompStmt.all(`{longname} : "${qraw}" *`);
400
- const zipMatches = zipRows.map(res => {
415
+ const zipMatches = zipRows.map((res) => {
401
416
  const loc = this.lookupZip(res.ZipCode);
402
417
  return GeoDb.zipLocToAutocomplete(loc);
403
418
  });
@@ -435,7 +450,7 @@ class GeoDb {
435
450
  latitude: loc.latitude,
436
451
  longitude: loc.longitude,
437
452
  timezone: loc.getTzid(),
438
- geo: 'geoname'
453
+ geo: 'geoname',
439
454
  };
440
455
  if (loc.population) {
441
456
  obj.population = loc.population;
@@ -472,7 +487,7 @@ class GeoDb {
472
487
  longitude: loc.longitude,
473
488
  timezone: loc.getTzid(),
474
489
  population: loc.population,
475
- geo: 'zip'
490
+ geo: 'zip',
476
491
  };
477
492
  }
478
493
 
@@ -548,7 +563,11 @@ async function buildGeonamesSqlite(opts) {
548
563
  logger.info(`Opening ${dbFilename}`);
549
564
  const db = new Database(dbFilename);
550
565
  db.pragma('journal_mode = MEMORY');
551
- doSql(logger, db, `DROP TABLE IF EXISTS country`, `CREATE TABLE country (
566
+
567
+ doSql(logger, db,
568
+ `DROP TABLE IF EXISTS country`,
569
+
570
+ `CREATE TABLE country (
552
571
  ISO TEXT PRIMARY KEY,
553
572
  ISO3 TEXT NOT NULL,
554
573
  IsoNumeric TEXT NOT NULL,
@@ -568,9 +587,14 @@ async function buildGeonamesSqlite(opts) {
568
587
  geonameid INT NOT NULL,
569
588
  neighbours TEXT NOT NULL,
570
589
  EquivalentFipsCode TEXT NOT NULL
571
- );`);
590
+ );`,
591
+ );
572
592
  await doFile(logger, db, countryInfotxt, 'country', 19);
573
- doSql(logger, db, `DROP TABLE IF EXISTS geoname`, `CREATE TABLE geoname (
593
+
594
+ doSql(logger, db,
595
+ `DROP TABLE IF EXISTS geoname`,
596
+
597
+ `CREATE TABLE geoname (
574
598
  geonameid int PRIMARY KEY,
575
599
  name nvarchar(200),
576
600
  asciiname nvarchar(200),
@@ -589,32 +613,48 @@ async function buildGeonamesSqlite(opts) {
589
613
  elevation int,
590
614
  gtopo30 int,
591
615
  timezone nvarchar(40),
592
- moddate date);`);
593
- const truncateAlternateNames = a => {
616
+ moddate date);`,
617
+ );
618
+
619
+ const truncateAlternateNames = (a) => {
594
620
  a[3] = '';
595
621
  return true;
596
622
  };
597
623
  await doFile(logger, db, cities5000txt, 'geoname', 19, truncateAlternateNames);
598
624
  await doFile(logger, db, citiesPatch, 'geoname', 19, truncateAlternateNames);
599
- await doFile(logger, db, ILtxt, 'geoname', 19, a => {
625
+ await doFile(logger, db, ILtxt, 'geoname', 19, (a) => {
600
626
  a[3] = '';
601
627
  return a[6] == 'P' && (a[7] == 'PPL' || a[7] == 'STLMT');
602
628
  });
603
- doSql(logger, db, `DROP TABLE IF EXISTS admin1`, `CREATE TABLE admin1 (
629
+
630
+ doSql(logger, db,
631
+ `DROP TABLE IF EXISTS admin1`,
632
+
633
+ `CREATE TABLE admin1 (
604
634
  key TEXT PRIMARY KEY,
605
635
  name nvarchar(200) NOT NULL,
606
636
  asciiname nvarchar(200) NOT NULL,
607
637
  geonameid int NOT NULL
608
- );`);
638
+ );`,
639
+ );
640
+
609
641
  await doFile(logger, db, admin1CodesASCIItxt, 'admin1', 4);
610
642
 
611
643
  // fix inconsistencies with the USA capitol
612
- doSql(logger, db, `UPDATE geoname
644
+ doSql(logger, db,
645
+ `UPDATE geoname
613
646
  SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
614
- WHERE geonameid = 4140963;`, `UPDATE admin1
647
+ WHERE geonameid = 4140963;`,
648
+
649
+ `UPDATE admin1
615
650
  SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
616
- WHERE key = 'US.DC';`);
617
- doSql(logger, db, `DROP TABLE IF EXISTS alternatenames`, `CREATE TABLE alternatenames (
651
+ WHERE key = 'US.DC';`,
652
+ );
653
+
654
+ doSql(logger, db,
655
+ `DROP TABLE IF EXISTS alternatenames`,
656
+
657
+ `CREATE TABLE alternatenames (
618
658
  id int PRIMARY KEY,
619
659
  geonameid int NOT NULL,
620
660
  isolanguage varchar(7),
@@ -625,8 +665,10 @@ async function buildGeonamesSqlite(opts) {
625
665
  isHistoric tinyint,
626
666
  periodFrom NULL,
627
667
  periodTo NULL
628
- );`);
629
- await doFile(logger, db, ILalternate, 'alternatenames', 10, a => {
668
+ );`,
669
+ );
670
+
671
+ await doFile(logger, db, ILalternate, 'alternatenames', 10, (a) => {
630
672
  const firstchar = a[3][0];
631
673
  if (a[2] === 'he' && (firstchar < '\u05D0' || firstchar > '\u05EA')) {
632
674
  a[2] = 'en';
@@ -654,16 +696,35 @@ async function buildGeonamesSqlite(opts) {
654
696
  });
655
697
 
656
698
  // remove duplicates from alternatenames
657
- doSql(logger, db, `DROP TABLE IF EXISTS altnames`, `CREATE TABLE altnames
699
+ doSql(logger, db,
700
+ `DROP TABLE IF EXISTS altnames`,
701
+
702
+ `CREATE TABLE altnames
658
703
  AS SELECT geonameid, isolanguage, name
659
704
  FROM alternatenames
660
705
  GROUP BY 1, 2, 3
661
- `);
662
- doSql(logger, db, `update admin1 set name='',asciiname='' where key like 'PS.%';`, `update country set country = '' where iso = 'PS';`, `delete from geoname where geonameid = 7303419;`);
663
- doSql(logger, db, `DROP TABLE IF EXISTS geoname_fulltext`, `CREATE VIRTUAL TABLE geoname_fulltext
706
+ `,
707
+ );
708
+
709
+ doSql(logger, db,
710
+ `update admin1 set name='',asciiname='' where key like 'PS.%';`,
711
+ `update country set country = '' where iso = 'PS';`,
712
+ `delete from geoname where geonameid = 7303419;`,
713
+ );
714
+
715
+ doSql(logger, db,
716
+ `DROP TABLE IF EXISTS geoname_fulltext`,
717
+
718
+ `CREATE VIRTUAL TABLE geoname_fulltext
664
719
  USING fts5(geonameid UNINDEXED, longname, population, city, admin1, country);
665
- `, `DROP TABLE IF EXISTS geoname_non_ascii`, `CREATE TABLE geoname_non_ascii AS
666
- SELECT geonameid FROM geoname WHERE asciiname <> name`, `INSERT INTO geoname_fulltext
720
+ `,
721
+
722
+ `DROP TABLE IF EXISTS geoname_non_ascii`,
723
+
724
+ `CREATE TABLE geoname_non_ascii AS
725
+ SELECT geonameid FROM geoname WHERE asciiname <> name`,
726
+
727
+ `INSERT INTO geoname_fulltext
667
728
  SELECT g.geonameid,
668
729
  g.asciiname||', '||a.asciiname||', '||c.Country,
669
730
  g.population,
@@ -674,7 +735,9 @@ async function buildGeonamesSqlite(opts) {
674
735
  AND g.country <> 'IL'
675
736
  AND g.country <> 'GB'
676
737
  AND g.country||'.'||g.admin1 = a.key
677
- `, `INSERT INTO geoname_fulltext
738
+ `,
739
+
740
+ `INSERT INTO geoname_fulltext
678
741
  SELECT g.geonameid,
679
742
  g.asciiname||', '||a.asciiname||', USA',
680
743
  g.population,
@@ -682,7 +745,9 @@ async function buildGeonamesSqlite(opts) {
682
745
  FROM geoname g, admin1 a
683
746
  WHERE g.country = 'US'
684
747
  AND g.country||'.'||g.admin1 = a.key
685
- `, `INSERT INTO geoname_fulltext
748
+ `,
749
+
750
+ `INSERT INTO geoname_fulltext
686
751
  SELECT g.geonameid,
687
752
  g.asciiname||', '||a.asciiname||', UK',
688
753
  g.population,
@@ -690,14 +755,18 @@ async function buildGeonamesSqlite(opts) {
690
755
  FROM geoname g, admin1 a
691
756
  WHERE g.country = 'GB'
692
757
  AND g.country||'.'||g.admin1 = a.key
693
- `, `INSERT INTO geoname_fulltext
758
+ `,
759
+
760
+ `INSERT INTO geoname_fulltext
694
761
  SELECT g.geonameid,
695
762
  g.asciiname||', Israel',
696
763
  g.population,
697
764
  g.asciiname,NULL,'Israel'
698
765
  FROM geoname g
699
766
  WHERE g.country = 'IL'
700
- `, `INSERT INTO geoname_fulltext
767
+ `,
768
+
769
+ `INSERT INTO geoname_fulltext
701
770
  SELECT g.geonameid,
702
771
  g.asciiname||', '||c.Country,
703
772
  g.population,
@@ -705,7 +774,9 @@ async function buildGeonamesSqlite(opts) {
705
774
  FROM geoname g, country c
706
775
  WHERE g.country = c.ISO
707
776
  AND (g.admin1 = '' OR g.admin1 = '00')
708
- `, `INSERT INTO geoname_fulltext
777
+ `,
778
+
779
+ `INSERT INTO geoname_fulltext
709
780
  SELECT g.geonameid,
710
781
  g.name||', '||a.name||', '||c.Country,
711
782
  g.population,
@@ -714,7 +785,9 @@ async function buildGeonamesSqlite(opts) {
714
785
  WHERE gna.geonameid = g.geonameid
715
786
  AND g.country = c.ISO
716
787
  AND g.country||'.'||g.admin1 = a.key
717
- `, `INSERT INTO geoname_fulltext
788
+ `,
789
+
790
+ `INSERT INTO geoname_fulltext
718
791
  SELECT g.geonameid,
719
792
  alt.name||', ישראל',
720
793
  g.population,
@@ -723,7 +796,9 @@ async function buildGeonamesSqlite(opts) {
723
796
  WHERE g.country = 'IL'
724
797
  AND alt.isolanguage = 'he'
725
798
  AND g.geonameid = alt.geonameid
726
- `, `INSERT INTO geoname_fulltext
799
+ `,
800
+
801
+ `INSERT INTO geoname_fulltext
727
802
  SELECT g.geonameid,
728
803
  alt.name||', Israel',
729
804
  g.population,
@@ -732,7 +807,11 @@ async function buildGeonamesSqlite(opts) {
732
807
  WHERE g.country = 'IL'
733
808
  AND alt.isolanguage = 'en'
734
809
  AND g.geonameid = alt.geonameid
735
- `, 'VACUUM');
810
+ `,
811
+
812
+ 'VACUUM',
813
+ );
814
+
736
815
  return new Promise((resolve, reject) => {
737
816
  try {
738
817
  logger.info(`Closing ${dbFilename}`);
@@ -780,11 +859,11 @@ async function doFile(logger, db, infile, tableName, expectedFields, callback) {
780
859
  try {
781
860
  const rl = readline.createInterface({
782
861
  input: fs.createReadStream(infile),
783
- crlfDelay: Infinity
862
+ crlfDelay: Infinity,
784
863
  });
785
864
  let num = 0;
786
865
  let accepted = 0;
787
- rl.on('line', line => {
866
+ rl.on('line', (line) => {
788
867
  num++;
789
868
  if (line[0] == '#') {
790
869
  return;
@@ -803,11 +882,13 @@ async function doFile(logger, db, infile, tableName, expectedFields, callback) {
803
882
  stmt.run(a);
804
883
  accepted++;
805
884
  });
885
+
806
886
  rl.on('close', () => {
807
887
  logger.info(`Inserted ${accepted} / ${num} into ${tableName} from ${infile}`);
808
888
  stmt = null;
809
889
  db.exec('COMMIT');
810
890
  });
891
+
811
892
  return resolve(events.once(rl, 'close'));
812
893
  } catch (err) {
813
894
  logger.error(err);
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! @hebcal/geo-sqlite v5.0.0 */
1
+ /*! @hebcal/geo-sqlite v5.0.2 */
2
2
  import Database from 'better-sqlite3';
3
3
  import { Location, Locale } from '@hebcal/core';
4
4
  import '@hebcal/cities';
@@ -16,7 +16,10 @@ let a=[["\0","","","","","","","","\b","\t","\n","\v","\f","\r","","","
16
16
  * @return {string}
17
17
  */
18
18
  function munge(s) {
19
- return s.toLowerCase().replace(/'/g, '').replace(/ /g, '').replace(/\+/g, '');
19
+ return s.toLowerCase()
20
+ .replace(/'/g, '')
21
+ .replace(/ /g, '')
22
+ .replace(/\+/g, '');
20
23
  }
21
24
 
22
25
  const GEONAME_SQL = `SELECT
@@ -35,6 +38,7 @@ LEFT JOIN country c on g.country = c.iso
35
38
  LEFT JOIN admin1 a on g.country||'.'||g.admin1 = a.key
36
39
  WHERE g.geonameid = ?
37
40
  `;
41
+
38
42
  const GEONAME_ALL_SQL = `SELECT
39
43
  g.geonameid as geonameid,
40
44
  g.name as name,
@@ -51,27 +55,35 @@ FROM geoname g
51
55
  LEFT JOIN country c on g.country = c.iso
52
56
  LEFT JOIN admin1 a on g.country||'.'||g.admin1 = a.key
53
57
  `;
58
+
54
59
  const ZIPCODE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,Elevation,
55
60
  TimeZone,DayLightSaving,Population
56
61
  FROM ZIPCodes_Primary WHERE ZipCode = ?`;
62
+
57
63
  const ZIPCODE_ALL_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,Elevation,
58
64
  TimeZone,DayLightSaving,Population
59
65
  FROM ZIPCodes_Primary`;
66
+
60
67
  const ZIP_COMPLETE_SQL = `SELECT ZipCode,CityMixedCase,State,Latitude,Longitude,TimeZone,DayLightSaving,Population
61
68
  FROM ZIPCodes_Primary
62
69
  WHERE ZipCode LIKE ?
63
70
  ORDER BY Population DESC
64
71
  LIMIT 10`;
65
- const ZIP_FULLTEXT_COMPLETE_SQL = `SELECT ZipCode
72
+
73
+ const ZIP_FULLTEXT_COMPLETE_SQL =
74
+ `SELECT ZipCode
66
75
  FROM ZIPCodes_CityFullText5
67
76
  WHERE ZIPCodes_CityFullText5 MATCH ?
68
77
  ORDER BY Population DESC
69
78
  LIMIT 20`;
70
- const GEONAME_COMPLETE_SQL = `SELECT geonameid, longname, city, admin1, country
79
+
80
+ const GEONAME_COMPLETE_SQL =
81
+ `SELECT geonameid, longname, city, admin1, country
71
82
  FROM geoname_fulltext
72
83
  WHERE geoname_fulltext MATCH ?
73
84
  ORDER BY population DESC
74
85
  LIMIT 20`;
86
+
75
87
  const stateNames = {
76
88
  'AK': 'Alaska',
77
89
  'AL': 'Alabama',
@@ -123,7 +135,7 @@ const stateNames = {
123
135
  'WA': 'Washington',
124
136
  'WI': 'Wisconsin',
125
137
  'WV': 'West Virginia',
126
- 'WY': 'Wyoming'
138
+ 'WY': 'Wyoming',
127
139
  };
128
140
 
129
141
  /** Wrapper around sqlite databases */
@@ -136,13 +148,9 @@ class GeoDb {
136
148
  constructor(logger, zipsFilename, geonamesFilename) {
137
149
  this.logger = logger;
138
150
  if (logger) logger.info(`GeoDb: opening ${zipsFilename}...`);
139
- this.zipsDb = new Database(zipsFilename, {
140
- fileMustExist: true
141
- });
151
+ this.zipsDb = new Database(zipsFilename, {fileMustExist: true});
142
152
  if (logger) logger.info(`GeoDb: opening ${geonamesFilename}...`);
143
- this.geonamesDb = new Database(geonamesFilename, {
144
- fileMustExist: true
145
- });
153
+ this.geonamesDb = new Database(geonamesFilename, {fileMustExist: true});
146
154
  this.zipStmt = this.zipsDb.prepare(ZIPCODE_SQL);
147
155
  /** @type {Map<string, Location>} */
148
156
  this.zipCache = new Map();
@@ -162,6 +170,7 @@ class GeoDb {
162
170
  }
163
171
  /** @type {Map<string, string>} */
164
172
  this.countryNames = map;
173
+ if (logger) logger.info(`GeoDb: ${map.size} countries, ${this.legacyCities.size} legacy cities`);
165
174
  }
166
175
 
167
176
  /** Closes database handles */
@@ -212,15 +221,14 @@ class GeoDb {
212
221
  const zip = result.ZipCode;
213
222
  const tzid = Location.getUsaTzid(result.State, result.TimeZone, result.DayLightSaving);
214
223
  const cityDescr = `${result.CityMixedCase}, ${result.State} ${zip}`;
215
- const location = new Location(result.Latitude, result.Longitude, false, tzid, cityDescr, 'US', zip);
224
+ const elevation = result?.Elevation > 0 ? result.Elevation : 0;
225
+ const location = new Location(result.Latitude, result.Longitude, false, tzid, cityDescr,
226
+ 'US', zip, elevation);
216
227
  location.admin1 = location.state = result.State;
217
228
  location.stateName = stateNames[location.state];
218
229
  location.geo = 'zip';
219
230
  location.zip = zip;
220
231
  location.population = result.Population;
221
- if (result.Elevation && result.Elevation > 0) {
222
- location.elevation = result.Elevation;
223
- }
224
232
  return location;
225
233
  }
226
234
 
@@ -291,7 +299,17 @@ class GeoDb {
291
299
  const country = result.country || '';
292
300
  const admin1 = result.admin1 || '';
293
301
  const cityDescr = GeoDb.geonameCityDescr(result.name, admin1, country);
294
- const location = new Location(result.latitude, result.longitude, result.cc == 'IL', result.timezone, cityDescr, result.cc, geonameid);
302
+ const elevation = result?.elevation > 0 ? result.elevation : 0;
303
+ const location = new Location(
304
+ result.latitude,
305
+ result.longitude,
306
+ result.cc == 'IL',
307
+ result.timezone,
308
+ cityDescr,
309
+ result.cc,
310
+ geonameid,
311
+ elevation,
312
+ );
295
313
  location.geo = 'geoname';
296
314
  location.geonameid = geonameid;
297
315
  location.asciiname = result.asciiname;
@@ -304,9 +322,6 @@ class GeoDb {
304
322
  if (result.population) {
305
323
  location.population = result.population;
306
324
  }
307
- if (result.elevation && result.elevation > 0) {
308
- location.elevation = result.elevation;
309
- }
310
325
  return location;
311
326
  }
312
327
 
@@ -346,7 +361,7 @@ class GeoDb {
346
361
  longitude: res.Longitude,
347
362
  timezone: Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
348
363
  population: res.Population,
349
- geo: 'zip'
364
+ geo: 'zip',
350
365
  };
351
366
  if (res.Elevation && res.Elevation > 0) {
352
367
  obj.elevation = res.Elevation;
@@ -360,7 +375,7 @@ class GeoDb {
360
375
  * @param {boolean} latlong
361
376
  * @return {Object[]}
362
377
  */
363
- autoComplete(qraw, latlong = false) {
378
+ autoComplete(qraw, latlong=false) {
364
379
  qraw = qraw.trim();
365
380
  if (qraw.length === 0) {
366
381
  return [];
@@ -387,7 +402,7 @@ class GeoDb {
387
402
  geoRows.push(row);
388
403
  }
389
404
  }
390
- const geoMatches = geoRows.map(res => {
405
+ const geoMatches = geoRows.map((res) => {
391
406
  const loc = this.lookupGeoname(res.geonameid);
392
407
  return this.geonameLocToAutocomplete(loc, res);
393
408
  });
@@ -395,7 +410,7 @@ class GeoDb {
395
410
  this.zipFulltextCompStmt = this.zipsDb.prepare(ZIP_FULLTEXT_COMPLETE_SQL);
396
411
  }
397
412
  const zipRows = this.zipFulltextCompStmt.all(`{longname} : "${qraw}" *`);
398
- const zipMatches = zipRows.map(res => {
413
+ const zipMatches = zipRows.map((res) => {
399
414
  const loc = this.lookupZip(res.ZipCode);
400
415
  return GeoDb.zipLocToAutocomplete(loc);
401
416
  });
@@ -433,7 +448,7 @@ class GeoDb {
433
448
  latitude: loc.latitude,
434
449
  longitude: loc.longitude,
435
450
  timezone: loc.getTzid(),
436
- geo: 'geoname'
451
+ geo: 'geoname',
437
452
  };
438
453
  if (loc.population) {
439
454
  obj.population = loc.population;
@@ -470,7 +485,7 @@ class GeoDb {
470
485
  longitude: loc.longitude,
471
486
  timezone: loc.getTzid(),
472
487
  population: loc.population,
473
- geo: 'zip'
488
+ geo: 'zip',
474
489
  };
475
490
  }
476
491
 
@@ -546,7 +561,11 @@ async function buildGeonamesSqlite(opts) {
546
561
  logger.info(`Opening ${dbFilename}`);
547
562
  const db = new Database(dbFilename);
548
563
  db.pragma('journal_mode = MEMORY');
549
- doSql(logger, db, `DROP TABLE IF EXISTS country`, `CREATE TABLE country (
564
+
565
+ doSql(logger, db,
566
+ `DROP TABLE IF EXISTS country`,
567
+
568
+ `CREATE TABLE country (
550
569
  ISO TEXT PRIMARY KEY,
551
570
  ISO3 TEXT NOT NULL,
552
571
  IsoNumeric TEXT NOT NULL,
@@ -566,9 +585,14 @@ async function buildGeonamesSqlite(opts) {
566
585
  geonameid INT NOT NULL,
567
586
  neighbours TEXT NOT NULL,
568
587
  EquivalentFipsCode TEXT NOT NULL
569
- );`);
588
+ );`,
589
+ );
570
590
  await doFile(logger, db, countryInfotxt, 'country', 19);
571
- doSql(logger, db, `DROP TABLE IF EXISTS geoname`, `CREATE TABLE geoname (
591
+
592
+ doSql(logger, db,
593
+ `DROP TABLE IF EXISTS geoname`,
594
+
595
+ `CREATE TABLE geoname (
572
596
  geonameid int PRIMARY KEY,
573
597
  name nvarchar(200),
574
598
  asciiname nvarchar(200),
@@ -587,32 +611,48 @@ async function buildGeonamesSqlite(opts) {
587
611
  elevation int,
588
612
  gtopo30 int,
589
613
  timezone nvarchar(40),
590
- moddate date);`);
591
- const truncateAlternateNames = a => {
614
+ moddate date);`,
615
+ );
616
+
617
+ const truncateAlternateNames = (a) => {
592
618
  a[3] = '';
593
619
  return true;
594
620
  };
595
621
  await doFile(logger, db, cities5000txt, 'geoname', 19, truncateAlternateNames);
596
622
  await doFile(logger, db, citiesPatch, 'geoname', 19, truncateAlternateNames);
597
- await doFile(logger, db, ILtxt, 'geoname', 19, a => {
623
+ await doFile(logger, db, ILtxt, 'geoname', 19, (a) => {
598
624
  a[3] = '';
599
625
  return a[6] == 'P' && (a[7] == 'PPL' || a[7] == 'STLMT');
600
626
  });
601
- doSql(logger, db, `DROP TABLE IF EXISTS admin1`, `CREATE TABLE admin1 (
627
+
628
+ doSql(logger, db,
629
+ `DROP TABLE IF EXISTS admin1`,
630
+
631
+ `CREATE TABLE admin1 (
602
632
  key TEXT PRIMARY KEY,
603
633
  name nvarchar(200) NOT NULL,
604
634
  asciiname nvarchar(200) NOT NULL,
605
635
  geonameid int NOT NULL
606
- );`);
636
+ );`,
637
+ );
638
+
607
639
  await doFile(logger, db, admin1CodesASCIItxt, 'admin1', 4);
608
640
 
609
641
  // fix inconsistencies with the USA capitol
610
- doSql(logger, db, `UPDATE geoname
642
+ doSql(logger, db,
643
+ `UPDATE geoname
611
644
  SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
612
- WHERE geonameid = 4140963;`, `UPDATE admin1
645
+ WHERE geonameid = 4140963;`,
646
+
647
+ `UPDATE admin1
613
648
  SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
614
- WHERE key = 'US.DC';`);
615
- doSql(logger, db, `DROP TABLE IF EXISTS alternatenames`, `CREATE TABLE alternatenames (
649
+ WHERE key = 'US.DC';`,
650
+ );
651
+
652
+ doSql(logger, db,
653
+ `DROP TABLE IF EXISTS alternatenames`,
654
+
655
+ `CREATE TABLE alternatenames (
616
656
  id int PRIMARY KEY,
617
657
  geonameid int NOT NULL,
618
658
  isolanguage varchar(7),
@@ -623,8 +663,10 @@ async function buildGeonamesSqlite(opts) {
623
663
  isHistoric tinyint,
624
664
  periodFrom NULL,
625
665
  periodTo NULL
626
- );`);
627
- await doFile(logger, db, ILalternate, 'alternatenames', 10, a => {
666
+ );`,
667
+ );
668
+
669
+ await doFile(logger, db, ILalternate, 'alternatenames', 10, (a) => {
628
670
  const firstchar = a[3][0];
629
671
  if (a[2] === 'he' && (firstchar < '\u05D0' || firstchar > '\u05EA')) {
630
672
  a[2] = 'en';
@@ -652,16 +694,35 @@ async function buildGeonamesSqlite(opts) {
652
694
  });
653
695
 
654
696
  // remove duplicates from alternatenames
655
- doSql(logger, db, `DROP TABLE IF EXISTS altnames`, `CREATE TABLE altnames
697
+ doSql(logger, db,
698
+ `DROP TABLE IF EXISTS altnames`,
699
+
700
+ `CREATE TABLE altnames
656
701
  AS SELECT geonameid, isolanguage, name
657
702
  FROM alternatenames
658
703
  GROUP BY 1, 2, 3
659
- `);
660
- doSql(logger, db, `update admin1 set name='',asciiname='' where key like 'PS.%';`, `update country set country = '' where iso = 'PS';`, `delete from geoname where geonameid = 7303419;`);
661
- doSql(logger, db, `DROP TABLE IF EXISTS geoname_fulltext`, `CREATE VIRTUAL TABLE geoname_fulltext
704
+ `,
705
+ );
706
+
707
+ doSql(logger, db,
708
+ `update admin1 set name='',asciiname='' where key like 'PS.%';`,
709
+ `update country set country = '' where iso = 'PS';`,
710
+ `delete from geoname where geonameid = 7303419;`,
711
+ );
712
+
713
+ doSql(logger, db,
714
+ `DROP TABLE IF EXISTS geoname_fulltext`,
715
+
716
+ `CREATE VIRTUAL TABLE geoname_fulltext
662
717
  USING fts5(geonameid UNINDEXED, longname, population, city, admin1, country);
663
- `, `DROP TABLE IF EXISTS geoname_non_ascii`, `CREATE TABLE geoname_non_ascii AS
664
- SELECT geonameid FROM geoname WHERE asciiname <> name`, `INSERT INTO geoname_fulltext
718
+ `,
719
+
720
+ `DROP TABLE IF EXISTS geoname_non_ascii`,
721
+
722
+ `CREATE TABLE geoname_non_ascii AS
723
+ SELECT geonameid FROM geoname WHERE asciiname <> name`,
724
+
725
+ `INSERT INTO geoname_fulltext
665
726
  SELECT g.geonameid,
666
727
  g.asciiname||', '||a.asciiname||', '||c.Country,
667
728
  g.population,
@@ -672,7 +733,9 @@ async function buildGeonamesSqlite(opts) {
672
733
  AND g.country <> 'IL'
673
734
  AND g.country <> 'GB'
674
735
  AND g.country||'.'||g.admin1 = a.key
675
- `, `INSERT INTO geoname_fulltext
736
+ `,
737
+
738
+ `INSERT INTO geoname_fulltext
676
739
  SELECT g.geonameid,
677
740
  g.asciiname||', '||a.asciiname||', USA',
678
741
  g.population,
@@ -680,7 +743,9 @@ async function buildGeonamesSqlite(opts) {
680
743
  FROM geoname g, admin1 a
681
744
  WHERE g.country = 'US'
682
745
  AND g.country||'.'||g.admin1 = a.key
683
- `, `INSERT INTO geoname_fulltext
746
+ `,
747
+
748
+ `INSERT INTO geoname_fulltext
684
749
  SELECT g.geonameid,
685
750
  g.asciiname||', '||a.asciiname||', UK',
686
751
  g.population,
@@ -688,14 +753,18 @@ async function buildGeonamesSqlite(opts) {
688
753
  FROM geoname g, admin1 a
689
754
  WHERE g.country = 'GB'
690
755
  AND g.country||'.'||g.admin1 = a.key
691
- `, `INSERT INTO geoname_fulltext
756
+ `,
757
+
758
+ `INSERT INTO geoname_fulltext
692
759
  SELECT g.geonameid,
693
760
  g.asciiname||', Israel',
694
761
  g.population,
695
762
  g.asciiname,NULL,'Israel'
696
763
  FROM geoname g
697
764
  WHERE g.country = 'IL'
698
- `, `INSERT INTO geoname_fulltext
765
+ `,
766
+
767
+ `INSERT INTO geoname_fulltext
699
768
  SELECT g.geonameid,
700
769
  g.asciiname||', '||c.Country,
701
770
  g.population,
@@ -703,7 +772,9 @@ async function buildGeonamesSqlite(opts) {
703
772
  FROM geoname g, country c
704
773
  WHERE g.country = c.ISO
705
774
  AND (g.admin1 = '' OR g.admin1 = '00')
706
- `, `INSERT INTO geoname_fulltext
775
+ `,
776
+
777
+ `INSERT INTO geoname_fulltext
707
778
  SELECT g.geonameid,
708
779
  g.name||', '||a.name||', '||c.Country,
709
780
  g.population,
@@ -712,7 +783,9 @@ async function buildGeonamesSqlite(opts) {
712
783
  WHERE gna.geonameid = g.geonameid
713
784
  AND g.country = c.ISO
714
785
  AND g.country||'.'||g.admin1 = a.key
715
- `, `INSERT INTO geoname_fulltext
786
+ `,
787
+
788
+ `INSERT INTO geoname_fulltext
716
789
  SELECT g.geonameid,
717
790
  alt.name||', ישראל',
718
791
  g.population,
@@ -721,7 +794,9 @@ async function buildGeonamesSqlite(opts) {
721
794
  WHERE g.country = 'IL'
722
795
  AND alt.isolanguage = 'he'
723
796
  AND g.geonameid = alt.geonameid
724
- `, `INSERT INTO geoname_fulltext
797
+ `,
798
+
799
+ `INSERT INTO geoname_fulltext
725
800
  SELECT g.geonameid,
726
801
  alt.name||', Israel',
727
802
  g.population,
@@ -730,7 +805,11 @@ async function buildGeonamesSqlite(opts) {
730
805
  WHERE g.country = 'IL'
731
806
  AND alt.isolanguage = 'en'
732
807
  AND g.geonameid = alt.geonameid
733
- `, 'VACUUM');
808
+ `,
809
+
810
+ 'VACUUM',
811
+ );
812
+
734
813
  return new Promise((resolve, reject) => {
735
814
  try {
736
815
  logger.info(`Closing ${dbFilename}`);
@@ -778,11 +857,11 @@ async function doFile(logger, db, infile, tableName, expectedFields, callback) {
778
857
  try {
779
858
  const rl = readline.createInterface({
780
859
  input: fs.createReadStream(infile),
781
- crlfDelay: Infinity
860
+ crlfDelay: Infinity,
782
861
  });
783
862
  let num = 0;
784
863
  let accepted = 0;
785
- rl.on('line', line => {
864
+ rl.on('line', (line) => {
786
865
  num++;
787
866
  if (line[0] == '#') {
788
867
  return;
@@ -801,11 +880,13 @@ async function doFile(logger, db, infile, tableName, expectedFields, callback) {
801
880
  stmt.run(a);
802
881
  accepted++;
803
882
  });
883
+
804
884
  rl.on('close', () => {
805
885
  logger.info(`Inserted ${accepted} / ${num} into ${tableName} from ${infile}`);
806
886
  stmt = null;
807
887
  db.exec('COMMIT');
808
888
  });
889
+
809
890
  return resolve(events.once(rl, 'close'));
810
891
  } catch (err) {
811
892
  logger.error(err);
package/package.json CHANGED
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "name": "@hebcal/geo-sqlite",
3
- "version": "5.0.0",
3
+ "version": "5.0.2",
4
4
  "author": "Michael J. Radwin (https://github.com/mjradwin)",
5
5
  "keywords": [
6
6
  "hebcal"
7
7
  ],
8
8
  "description": "Hebcal ES6 interface to GeoNames and USA ZIP code SQLite databases",
9
- "main": "./dist/index.js",
9
+ "main": "./dist/index.cjs",
10
10
  "module": "./dist/index.mjs",
11
+ "type": "module",
12
+ "exports": {
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.cjs"
15
+ },
11
16
  "bin": {
12
17
  "build-geonames-sqlite": "bin/build-geonames-sqlite",
13
18
  "download-and-make-dbs": "bin/download-and-make-dbs"
@@ -20,6 +25,9 @@
20
25
  "url": "https://github.com/hebcal/hebcal-geo-sqlite/issues"
21
26
  },
22
27
  "typings": "geo-sqlite.d.ts",
28
+ "engines": {
29
+ "node": ">= 20.0.0"
30
+ },
23
31
  "files": [
24
32
  "dist",
25
33
  "bin",
@@ -28,11 +36,11 @@
28
36
  "geo-sqlite.d.ts"
29
37
  ],
30
38
  "dependencies": {
31
- "@hebcal/cities": "^5.0.0",
32
- "@hebcal/core": "^5.0.0-rc2",
33
- "better-sqlite3": "^9.1.1",
34
- "pino": "^8.16.2",
35
- "pino-pretty": "^10.2.3",
39
+ "@hebcal/cities": "^5.0.1",
40
+ "@hebcal/core": "^5.0.3",
41
+ "better-sqlite3": "^9.2.2",
42
+ "pino": "^8.17.1",
43
+ "pino-pretty": "^10.3.0",
36
44
  "transliteration": "^2.3.5"
37
45
  },
38
46
  "scripts": {
@@ -41,25 +49,16 @@
41
49
  "readme": "npx jsdoc2md dist/index.js",
42
50
  "test": "ava"
43
51
  },
44
- "ava": {
45
- "require": [
46
- "@babel/register"
47
- ]
48
- },
49
52
  "license": "BSD-2-Clause",
50
53
  "devDependencies": {
51
- "@babel/core": "^7.23.3",
52
- "@babel/preset-env": "^7.23.3",
53
- "@babel/register": "^7.22.15",
54
- "@rollup/plugin-babel": "^6.0.4",
55
54
  "@rollup/plugin-commonjs": "^25.0.7",
56
- "@rollup/plugin-json": "^6.0.1",
55
+ "@rollup/plugin-json": "^6.1.0",
57
56
  "@rollup/plugin-node-resolve": "^15.2.3",
58
- "ava": "^5.3.1",
59
- "eslint": "^8.54.0",
57
+ "ava": "^6.0.1",
58
+ "eslint": "^8.56.0",
60
59
  "eslint-config-google": "^0.14.0",
61
60
  "jsdoc": "^4.0.2",
62
61
  "jsdoc-to-markdown": "^8.0.0",
63
- "rollup": "^4.5.1"
62
+ "rollup": "^4.9.1"
64
63
  }
65
64
  }