@hebcal/geo-sqlite 5.0.1 → 5.0.3

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,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const {buildGeonamesSqlite} = require('@hebcal/geo-sqlite');
4
- const {pino} = require('pino');
3
+ import {buildGeonamesSqlite} from '@hebcal/geo-sqlite';
4
+ import {pino} from 'pino';
5
5
 
6
6
  const logger = pino({
7
7
  // level: argv.quiet ? 'warn' : 'info',
@@ -1,4 +1,4 @@
1
- /*! @hebcal/geo-sqlite v5.0.1 */
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 */
@@ -215,7 +224,8 @@ class GeoDb {
215
224
  const tzid = core.Location.getUsaTzid(result.State, result.TimeZone, result.DayLightSaving);
216
225
  const cityDescr = `${result.CityMixedCase}, ${result.State} ${zip}`;
217
226
  const elevation = result?.Elevation > 0 ? result.Elevation : 0;
218
- const location = new core.Location(result.Latitude, result.Longitude, false, tzid, cityDescr, 'US', zip, elevation);
227
+ const location = new core.Location(result.Latitude, result.Longitude, false, tzid, cityDescr,
228
+ 'US', zip, elevation);
219
229
  location.admin1 = location.state = result.State;
220
230
  location.stateName = stateNames[location.state];
221
231
  location.geo = 'zip';
@@ -292,7 +302,16 @@ class GeoDb {
292
302
  const admin1 = result.admin1 || '';
293
303
  const cityDescr = GeoDb.geonameCityDescr(result.name, admin1, country);
294
304
  const elevation = result?.elevation > 0 ? result.elevation : 0;
295
- const location = new core.Location(result.latitude, result.longitude, result.cc == 'IL', result.timezone, cityDescr, result.cc, geonameid, elevation);
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
+ );
296
315
  location.geo = 'geoname';
297
316
  location.geonameid = geonameid;
298
317
  location.asciiname = result.asciiname;
@@ -344,7 +363,7 @@ class GeoDb {
344
363
  longitude: res.Longitude,
345
364
  timezone: core.Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
346
365
  population: res.Population,
347
- geo: 'zip'
366
+ geo: 'zip',
348
367
  };
349
368
  if (res.Elevation && res.Elevation > 0) {
350
369
  obj.elevation = res.Elevation;
@@ -358,7 +377,7 @@ class GeoDb {
358
377
  * @param {boolean} latlong
359
378
  * @return {Object[]}
360
379
  */
361
- autoComplete(qraw, latlong = false) {
380
+ autoComplete(qraw, latlong=false) {
362
381
  qraw = qraw.trim();
363
382
  if (qraw.length === 0) {
364
383
  return [];
@@ -385,7 +404,7 @@ class GeoDb {
385
404
  geoRows.push(row);
386
405
  }
387
406
  }
388
- const geoMatches = geoRows.map(res => {
407
+ const geoMatches = geoRows.map((res) => {
389
408
  const loc = this.lookupGeoname(res.geonameid);
390
409
  return this.geonameLocToAutocomplete(loc, res);
391
410
  });
@@ -393,7 +412,7 @@ class GeoDb {
393
412
  this.zipFulltextCompStmt = this.zipsDb.prepare(ZIP_FULLTEXT_COMPLETE_SQL);
394
413
  }
395
414
  const zipRows = this.zipFulltextCompStmt.all(`{longname} : "${qraw}" *`);
396
- const zipMatches = zipRows.map(res => {
415
+ const zipMatches = zipRows.map((res) => {
397
416
  const loc = this.lookupZip(res.ZipCode);
398
417
  return GeoDb.zipLocToAutocomplete(loc);
399
418
  });
@@ -431,7 +450,7 @@ class GeoDb {
431
450
  latitude: loc.latitude,
432
451
  longitude: loc.longitude,
433
452
  timezone: loc.getTzid(),
434
- geo: 'geoname'
453
+ geo: 'geoname',
435
454
  };
436
455
  if (loc.population) {
437
456
  obj.population = loc.population;
@@ -468,7 +487,7 @@ class GeoDb {
468
487
  longitude: loc.longitude,
469
488
  timezone: loc.getTzid(),
470
489
  population: loc.population,
471
- geo: 'zip'
490
+ geo: 'zip',
472
491
  };
473
492
  }
474
493
 
@@ -544,7 +563,11 @@ async function buildGeonamesSqlite(opts) {
544
563
  logger.info(`Opening ${dbFilename}`);
545
564
  const db = new Database(dbFilename);
546
565
  db.pragma('journal_mode = MEMORY');
547
- 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 (
548
571
  ISO TEXT PRIMARY KEY,
549
572
  ISO3 TEXT NOT NULL,
550
573
  IsoNumeric TEXT NOT NULL,
@@ -564,9 +587,14 @@ async function buildGeonamesSqlite(opts) {
564
587
  geonameid INT NOT NULL,
565
588
  neighbours TEXT NOT NULL,
566
589
  EquivalentFipsCode TEXT NOT NULL
567
- );`);
590
+ );`,
591
+ );
568
592
  await doFile(logger, db, countryInfotxt, 'country', 19);
569
- 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 (
570
598
  geonameid int PRIMARY KEY,
571
599
  name nvarchar(200),
572
600
  asciiname nvarchar(200),
@@ -585,32 +613,48 @@ async function buildGeonamesSqlite(opts) {
585
613
  elevation int,
586
614
  gtopo30 int,
587
615
  timezone nvarchar(40),
588
- moddate date);`);
589
- const truncateAlternateNames = a => {
616
+ moddate date);`,
617
+ );
618
+
619
+ const truncateAlternateNames = (a) => {
590
620
  a[3] = '';
591
621
  return true;
592
622
  };
593
623
  await doFile(logger, db, cities5000txt, 'geoname', 19, truncateAlternateNames);
594
624
  await doFile(logger, db, citiesPatch, 'geoname', 19, truncateAlternateNames);
595
- await doFile(logger, db, ILtxt, 'geoname', 19, a => {
625
+ await doFile(logger, db, ILtxt, 'geoname', 19, (a) => {
596
626
  a[3] = '';
597
627
  return a[6] == 'P' && (a[7] == 'PPL' || a[7] == 'STLMT');
598
628
  });
599
- 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 (
600
634
  key TEXT PRIMARY KEY,
601
635
  name nvarchar(200) NOT NULL,
602
636
  asciiname nvarchar(200) NOT NULL,
603
637
  geonameid int NOT NULL
604
- );`);
638
+ );`,
639
+ );
640
+
605
641
  await doFile(logger, db, admin1CodesASCIItxt, 'admin1', 4);
606
642
 
607
643
  // fix inconsistencies with the USA capitol
608
- doSql(logger, db, `UPDATE geoname
644
+ doSql(logger, db,
645
+ `UPDATE geoname
609
646
  SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
610
- WHERE geonameid = 4140963;`, `UPDATE admin1
647
+ WHERE geonameid = 4140963;`,
648
+
649
+ `UPDATE admin1
611
650
  SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
612
- WHERE key = 'US.DC';`);
613
- 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 (
614
658
  id int PRIMARY KEY,
615
659
  geonameid int NOT NULL,
616
660
  isolanguage varchar(7),
@@ -621,8 +665,10 @@ async function buildGeonamesSqlite(opts) {
621
665
  isHistoric tinyint,
622
666
  periodFrom NULL,
623
667
  periodTo NULL
624
- );`);
625
- await doFile(logger, db, ILalternate, 'alternatenames', 10, a => {
668
+ );`,
669
+ );
670
+
671
+ await doFile(logger, db, ILalternate, 'alternatenames', 10, (a) => {
626
672
  const firstchar = a[3][0];
627
673
  if (a[2] === 'he' && (firstchar < '\u05D0' || firstchar > '\u05EA')) {
628
674
  a[2] = 'en';
@@ -650,16 +696,35 @@ async function buildGeonamesSqlite(opts) {
650
696
  });
651
697
 
652
698
  // remove duplicates from alternatenames
653
- 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
654
703
  AS SELECT geonameid, isolanguage, name
655
704
  FROM alternatenames
656
705
  GROUP BY 1, 2, 3
657
- `);
658
- 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;`);
659
- 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
660
719
  USING fts5(geonameid UNINDEXED, longname, population, city, admin1, country);
661
- `, `DROP TABLE IF EXISTS geoname_non_ascii`, `CREATE TABLE geoname_non_ascii AS
662
- 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
663
728
  SELECT g.geonameid,
664
729
  g.asciiname||', '||a.asciiname||', '||c.Country,
665
730
  g.population,
@@ -670,7 +735,9 @@ async function buildGeonamesSqlite(opts) {
670
735
  AND g.country <> 'IL'
671
736
  AND g.country <> 'GB'
672
737
  AND g.country||'.'||g.admin1 = a.key
673
- `, `INSERT INTO geoname_fulltext
738
+ `,
739
+
740
+ `INSERT INTO geoname_fulltext
674
741
  SELECT g.geonameid,
675
742
  g.asciiname||', '||a.asciiname||', USA',
676
743
  g.population,
@@ -678,7 +745,9 @@ async function buildGeonamesSqlite(opts) {
678
745
  FROM geoname g, admin1 a
679
746
  WHERE g.country = 'US'
680
747
  AND g.country||'.'||g.admin1 = a.key
681
- `, `INSERT INTO geoname_fulltext
748
+ `,
749
+
750
+ `INSERT INTO geoname_fulltext
682
751
  SELECT g.geonameid,
683
752
  g.asciiname||', '||a.asciiname||', UK',
684
753
  g.population,
@@ -686,14 +755,18 @@ async function buildGeonamesSqlite(opts) {
686
755
  FROM geoname g, admin1 a
687
756
  WHERE g.country = 'GB'
688
757
  AND g.country||'.'||g.admin1 = a.key
689
- `, `INSERT INTO geoname_fulltext
758
+ `,
759
+
760
+ `INSERT INTO geoname_fulltext
690
761
  SELECT g.geonameid,
691
762
  g.asciiname||', Israel',
692
763
  g.population,
693
764
  g.asciiname,NULL,'Israel'
694
765
  FROM geoname g
695
766
  WHERE g.country = 'IL'
696
- `, `INSERT INTO geoname_fulltext
767
+ `,
768
+
769
+ `INSERT INTO geoname_fulltext
697
770
  SELECT g.geonameid,
698
771
  g.asciiname||', '||c.Country,
699
772
  g.population,
@@ -701,7 +774,9 @@ async function buildGeonamesSqlite(opts) {
701
774
  FROM geoname g, country c
702
775
  WHERE g.country = c.ISO
703
776
  AND (g.admin1 = '' OR g.admin1 = '00')
704
- `, `INSERT INTO geoname_fulltext
777
+ `,
778
+
779
+ `INSERT INTO geoname_fulltext
705
780
  SELECT g.geonameid,
706
781
  g.name||', '||a.name||', '||c.Country,
707
782
  g.population,
@@ -710,7 +785,9 @@ async function buildGeonamesSqlite(opts) {
710
785
  WHERE gna.geonameid = g.geonameid
711
786
  AND g.country = c.ISO
712
787
  AND g.country||'.'||g.admin1 = a.key
713
- `, `INSERT INTO geoname_fulltext
788
+ `,
789
+
790
+ `INSERT INTO geoname_fulltext
714
791
  SELECT g.geonameid,
715
792
  alt.name||', ישראל',
716
793
  g.population,
@@ -719,7 +796,9 @@ async function buildGeonamesSqlite(opts) {
719
796
  WHERE g.country = 'IL'
720
797
  AND alt.isolanguage = 'he'
721
798
  AND g.geonameid = alt.geonameid
722
- `, `INSERT INTO geoname_fulltext
799
+ `,
800
+
801
+ `INSERT INTO geoname_fulltext
723
802
  SELECT g.geonameid,
724
803
  alt.name||', Israel',
725
804
  g.population,
@@ -728,7 +807,11 @@ async function buildGeonamesSqlite(opts) {
728
807
  WHERE g.country = 'IL'
729
808
  AND alt.isolanguage = 'en'
730
809
  AND g.geonameid = alt.geonameid
731
- `, 'VACUUM');
810
+ `,
811
+
812
+ 'VACUUM',
813
+ );
814
+
732
815
  return new Promise((resolve, reject) => {
733
816
  try {
734
817
  logger.info(`Closing ${dbFilename}`);
@@ -776,11 +859,11 @@ async function doFile(logger, db, infile, tableName, expectedFields, callback) {
776
859
  try {
777
860
  const rl = readline.createInterface({
778
861
  input: fs.createReadStream(infile),
779
- crlfDelay: Infinity
862
+ crlfDelay: Infinity,
780
863
  });
781
864
  let num = 0;
782
865
  let accepted = 0;
783
- rl.on('line', line => {
866
+ rl.on('line', (line) => {
784
867
  num++;
785
868
  if (line[0] == '#') {
786
869
  return;
@@ -799,11 +882,13 @@ async function doFile(logger, db, infile, tableName, expectedFields, callback) {
799
882
  stmt.run(a);
800
883
  accepted++;
801
884
  });
885
+
802
886
  rl.on('close', () => {
803
887
  logger.info(`Inserted ${accepted} / ${num} into ${tableName} from ${infile}`);
804
888
  stmt = null;
805
889
  db.exec('COMMIT');
806
890
  });
891
+
807
892
  return resolve(events.once(rl, 'close'));
808
893
  } catch (err) {
809
894
  logger.error(err);
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! @hebcal/geo-sqlite v5.0.1 */
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 */
@@ -213,7 +222,8 @@ class GeoDb {
213
222
  const tzid = Location.getUsaTzid(result.State, result.TimeZone, result.DayLightSaving);
214
223
  const cityDescr = `${result.CityMixedCase}, ${result.State} ${zip}`;
215
224
  const elevation = result?.Elevation > 0 ? result.Elevation : 0;
216
- const location = new Location(result.Latitude, result.Longitude, false, tzid, cityDescr, 'US', zip, elevation);
225
+ const location = new Location(result.Latitude, result.Longitude, false, tzid, cityDescr,
226
+ 'US', zip, elevation);
217
227
  location.admin1 = location.state = result.State;
218
228
  location.stateName = stateNames[location.state];
219
229
  location.geo = 'zip';
@@ -290,7 +300,16 @@ class GeoDb {
290
300
  const admin1 = result.admin1 || '';
291
301
  const cityDescr = GeoDb.geonameCityDescr(result.name, admin1, country);
292
302
  const elevation = result?.elevation > 0 ? result.elevation : 0;
293
- const location = new Location(result.latitude, result.longitude, result.cc == 'IL', result.timezone, cityDescr, result.cc, geonameid, elevation);
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
+ );
294
313
  location.geo = 'geoname';
295
314
  location.geonameid = geonameid;
296
315
  location.asciiname = result.asciiname;
@@ -342,7 +361,7 @@ class GeoDb {
342
361
  longitude: res.Longitude,
343
362
  timezone: Location.getUsaTzid(res.State, res.TimeZone, res.DayLightSaving),
344
363
  population: res.Population,
345
- geo: 'zip'
364
+ geo: 'zip',
346
365
  };
347
366
  if (res.Elevation && res.Elevation > 0) {
348
367
  obj.elevation = res.Elevation;
@@ -356,7 +375,7 @@ class GeoDb {
356
375
  * @param {boolean} latlong
357
376
  * @return {Object[]}
358
377
  */
359
- autoComplete(qraw, latlong = false) {
378
+ autoComplete(qraw, latlong=false) {
360
379
  qraw = qraw.trim();
361
380
  if (qraw.length === 0) {
362
381
  return [];
@@ -383,7 +402,7 @@ class GeoDb {
383
402
  geoRows.push(row);
384
403
  }
385
404
  }
386
- const geoMatches = geoRows.map(res => {
405
+ const geoMatches = geoRows.map((res) => {
387
406
  const loc = this.lookupGeoname(res.geonameid);
388
407
  return this.geonameLocToAutocomplete(loc, res);
389
408
  });
@@ -391,7 +410,7 @@ class GeoDb {
391
410
  this.zipFulltextCompStmt = this.zipsDb.prepare(ZIP_FULLTEXT_COMPLETE_SQL);
392
411
  }
393
412
  const zipRows = this.zipFulltextCompStmt.all(`{longname} : "${qraw}" *`);
394
- const zipMatches = zipRows.map(res => {
413
+ const zipMatches = zipRows.map((res) => {
395
414
  const loc = this.lookupZip(res.ZipCode);
396
415
  return GeoDb.zipLocToAutocomplete(loc);
397
416
  });
@@ -429,7 +448,7 @@ class GeoDb {
429
448
  latitude: loc.latitude,
430
449
  longitude: loc.longitude,
431
450
  timezone: loc.getTzid(),
432
- geo: 'geoname'
451
+ geo: 'geoname',
433
452
  };
434
453
  if (loc.population) {
435
454
  obj.population = loc.population;
@@ -466,7 +485,7 @@ class GeoDb {
466
485
  longitude: loc.longitude,
467
486
  timezone: loc.getTzid(),
468
487
  population: loc.population,
469
- geo: 'zip'
488
+ geo: 'zip',
470
489
  };
471
490
  }
472
491
 
@@ -542,7 +561,11 @@ async function buildGeonamesSqlite(opts) {
542
561
  logger.info(`Opening ${dbFilename}`);
543
562
  const db = new Database(dbFilename);
544
563
  db.pragma('journal_mode = MEMORY');
545
- 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 (
546
569
  ISO TEXT PRIMARY KEY,
547
570
  ISO3 TEXT NOT NULL,
548
571
  IsoNumeric TEXT NOT NULL,
@@ -562,9 +585,14 @@ async function buildGeonamesSqlite(opts) {
562
585
  geonameid INT NOT NULL,
563
586
  neighbours TEXT NOT NULL,
564
587
  EquivalentFipsCode TEXT NOT NULL
565
- );`);
588
+ );`,
589
+ );
566
590
  await doFile(logger, db, countryInfotxt, 'country', 19);
567
- 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 (
568
596
  geonameid int PRIMARY KEY,
569
597
  name nvarchar(200),
570
598
  asciiname nvarchar(200),
@@ -583,32 +611,48 @@ async function buildGeonamesSqlite(opts) {
583
611
  elevation int,
584
612
  gtopo30 int,
585
613
  timezone nvarchar(40),
586
- moddate date);`);
587
- const truncateAlternateNames = a => {
614
+ moddate date);`,
615
+ );
616
+
617
+ const truncateAlternateNames = (a) => {
588
618
  a[3] = '';
589
619
  return true;
590
620
  };
591
621
  await doFile(logger, db, cities5000txt, 'geoname', 19, truncateAlternateNames);
592
622
  await doFile(logger, db, citiesPatch, 'geoname', 19, truncateAlternateNames);
593
- await doFile(logger, db, ILtxt, 'geoname', 19, a => {
623
+ await doFile(logger, db, ILtxt, 'geoname', 19, (a) => {
594
624
  a[3] = '';
595
625
  return a[6] == 'P' && (a[7] == 'PPL' || a[7] == 'STLMT');
596
626
  });
597
- 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 (
598
632
  key TEXT PRIMARY KEY,
599
633
  name nvarchar(200) NOT NULL,
600
634
  asciiname nvarchar(200) NOT NULL,
601
635
  geonameid int NOT NULL
602
- );`);
636
+ );`,
637
+ );
638
+
603
639
  await doFile(logger, db, admin1CodesASCIItxt, 'admin1', 4);
604
640
 
605
641
  // fix inconsistencies with the USA capitol
606
- doSql(logger, db, `UPDATE geoname
642
+ doSql(logger, db,
643
+ `UPDATE geoname
607
644
  SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
608
- WHERE geonameid = 4140963;`, `UPDATE admin1
645
+ WHERE geonameid = 4140963;`,
646
+
647
+ `UPDATE admin1
609
648
  SET name = 'Washington, D.C.', asciiname = 'Washington, D.C.'
610
- WHERE key = 'US.DC';`);
611
- 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 (
612
656
  id int PRIMARY KEY,
613
657
  geonameid int NOT NULL,
614
658
  isolanguage varchar(7),
@@ -619,8 +663,10 @@ async function buildGeonamesSqlite(opts) {
619
663
  isHistoric tinyint,
620
664
  periodFrom NULL,
621
665
  periodTo NULL
622
- );`);
623
- await doFile(logger, db, ILalternate, 'alternatenames', 10, a => {
666
+ );`,
667
+ );
668
+
669
+ await doFile(logger, db, ILalternate, 'alternatenames', 10, (a) => {
624
670
  const firstchar = a[3][0];
625
671
  if (a[2] === 'he' && (firstchar < '\u05D0' || firstchar > '\u05EA')) {
626
672
  a[2] = 'en';
@@ -648,16 +694,35 @@ async function buildGeonamesSqlite(opts) {
648
694
  });
649
695
 
650
696
  // remove duplicates from alternatenames
651
- 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
652
701
  AS SELECT geonameid, isolanguage, name
653
702
  FROM alternatenames
654
703
  GROUP BY 1, 2, 3
655
- `);
656
- 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;`);
657
- 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
658
717
  USING fts5(geonameid UNINDEXED, longname, population, city, admin1, country);
659
- `, `DROP TABLE IF EXISTS geoname_non_ascii`, `CREATE TABLE geoname_non_ascii AS
660
- 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
661
726
  SELECT g.geonameid,
662
727
  g.asciiname||', '||a.asciiname||', '||c.Country,
663
728
  g.population,
@@ -668,7 +733,9 @@ async function buildGeonamesSqlite(opts) {
668
733
  AND g.country <> 'IL'
669
734
  AND g.country <> 'GB'
670
735
  AND g.country||'.'||g.admin1 = a.key
671
- `, `INSERT INTO geoname_fulltext
736
+ `,
737
+
738
+ `INSERT INTO geoname_fulltext
672
739
  SELECT g.geonameid,
673
740
  g.asciiname||', '||a.asciiname||', USA',
674
741
  g.population,
@@ -676,7 +743,9 @@ async function buildGeonamesSqlite(opts) {
676
743
  FROM geoname g, admin1 a
677
744
  WHERE g.country = 'US'
678
745
  AND g.country||'.'||g.admin1 = a.key
679
- `, `INSERT INTO geoname_fulltext
746
+ `,
747
+
748
+ `INSERT INTO geoname_fulltext
680
749
  SELECT g.geonameid,
681
750
  g.asciiname||', '||a.asciiname||', UK',
682
751
  g.population,
@@ -684,14 +753,18 @@ async function buildGeonamesSqlite(opts) {
684
753
  FROM geoname g, admin1 a
685
754
  WHERE g.country = 'GB'
686
755
  AND g.country||'.'||g.admin1 = a.key
687
- `, `INSERT INTO geoname_fulltext
756
+ `,
757
+
758
+ `INSERT INTO geoname_fulltext
688
759
  SELECT g.geonameid,
689
760
  g.asciiname||', Israel',
690
761
  g.population,
691
762
  g.asciiname,NULL,'Israel'
692
763
  FROM geoname g
693
764
  WHERE g.country = 'IL'
694
- `, `INSERT INTO geoname_fulltext
765
+ `,
766
+
767
+ `INSERT INTO geoname_fulltext
695
768
  SELECT g.geonameid,
696
769
  g.asciiname||', '||c.Country,
697
770
  g.population,
@@ -699,7 +772,9 @@ async function buildGeonamesSqlite(opts) {
699
772
  FROM geoname g, country c
700
773
  WHERE g.country = c.ISO
701
774
  AND (g.admin1 = '' OR g.admin1 = '00')
702
- `, `INSERT INTO geoname_fulltext
775
+ `,
776
+
777
+ `INSERT INTO geoname_fulltext
703
778
  SELECT g.geonameid,
704
779
  g.name||', '||a.name||', '||c.Country,
705
780
  g.population,
@@ -708,7 +783,9 @@ async function buildGeonamesSqlite(opts) {
708
783
  WHERE gna.geonameid = g.geonameid
709
784
  AND g.country = c.ISO
710
785
  AND g.country||'.'||g.admin1 = a.key
711
- `, `INSERT INTO geoname_fulltext
786
+ `,
787
+
788
+ `INSERT INTO geoname_fulltext
712
789
  SELECT g.geonameid,
713
790
  alt.name||', ישראל',
714
791
  g.population,
@@ -717,7 +794,9 @@ async function buildGeonamesSqlite(opts) {
717
794
  WHERE g.country = 'IL'
718
795
  AND alt.isolanguage = 'he'
719
796
  AND g.geonameid = alt.geonameid
720
- `, `INSERT INTO geoname_fulltext
797
+ `,
798
+
799
+ `INSERT INTO geoname_fulltext
721
800
  SELECT g.geonameid,
722
801
  alt.name||', Israel',
723
802
  g.population,
@@ -726,7 +805,11 @@ async function buildGeonamesSqlite(opts) {
726
805
  WHERE g.country = 'IL'
727
806
  AND alt.isolanguage = 'en'
728
807
  AND g.geonameid = alt.geonameid
729
- `, 'VACUUM');
808
+ `,
809
+
810
+ 'VACUUM',
811
+ );
812
+
730
813
  return new Promise((resolve, reject) => {
731
814
  try {
732
815
  logger.info(`Closing ${dbFilename}`);
@@ -774,11 +857,11 @@ async function doFile(logger, db, infile, tableName, expectedFields, callback) {
774
857
  try {
775
858
  const rl = readline.createInterface({
776
859
  input: fs.createReadStream(infile),
777
- crlfDelay: Infinity
860
+ crlfDelay: Infinity,
778
861
  });
779
862
  let num = 0;
780
863
  let accepted = 0;
781
- rl.on('line', line => {
864
+ rl.on('line', (line) => {
782
865
  num++;
783
866
  if (line[0] == '#') {
784
867
  return;
@@ -797,11 +880,13 @@ async function doFile(logger, db, infile, tableName, expectedFields, callback) {
797
880
  stmt.run(a);
798
881
  accepted++;
799
882
  });
883
+
800
884
  rl.on('close', () => {
801
885
  logger.info(`Inserted ${accepted} / ${num} into ${tableName} from ${infile}`);
802
886
  stmt = null;
803
887
  db.exec('COMMIT');
804
888
  });
889
+
805
890
  return resolve(events.once(rl, 'close'));
806
891
  } catch (err) {
807
892
  logger.error(err);
package/package.json CHANGED
@@ -1,13 +1,19 @@
1
1
  {
2
2
  "name": "@hebcal/geo-sqlite",
3
- "version": "5.0.1",
3
+ "version": "5.0.3",
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
+ "types": "./geo-sqlite.d.ts"
16
+ },
11
17
  "bin": {
12
18
  "build-geonames-sqlite": "bin/build-geonames-sqlite",
13
19
  "download-and-make-dbs": "bin/download-and-make-dbs"
@@ -20,6 +26,9 @@
20
26
  "url": "https://github.com/hebcal/hebcal-geo-sqlite/issues"
21
27
  },
22
28
  "typings": "geo-sqlite.d.ts",
29
+ "engines": {
30
+ "node": ">= 20.0.0"
31
+ },
23
32
  "files": [
24
33
  "dist",
25
34
  "bin",
@@ -28,11 +37,11 @@
28
37
  "geo-sqlite.d.ts"
29
38
  ],
30
39
  "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",
40
+ "@hebcal/cities": "^5.0.1",
41
+ "@hebcal/core": "^5.0.7",
42
+ "better-sqlite3": "^9.2.2",
43
+ "pino": "^8.17.2",
44
+ "pino-pretty": "^10.3.1",
36
45
  "transliteration": "^2.3.5"
37
46
  },
38
47
  "scripts": {
@@ -41,25 +50,16 @@
41
50
  "readme": "npx jsdoc2md dist/index.js",
42
51
  "test": "ava"
43
52
  },
44
- "ava": {
45
- "require": [
46
- "@babel/register"
47
- ]
48
- },
49
53
  "license": "BSD-2-Clause",
50
54
  "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
55
  "@rollup/plugin-commonjs": "^25.0.7",
56
- "@rollup/plugin-json": "^6.0.1",
56
+ "@rollup/plugin-json": "^6.1.0",
57
57
  "@rollup/plugin-node-resolve": "^15.2.3",
58
- "ava": "^5.3.1",
59
- "eslint": "^8.54.0",
58
+ "ava": "^6.0.1",
59
+ "eslint": "^8.56.0",
60
60
  "eslint-config-google": "^0.14.0",
61
61
  "jsdoc": "^4.0.2",
62
62
  "jsdoc-to-markdown": "^8.0.0",
63
- "rollup": "^4.5.1"
63
+ "rollup": "^4.9.4"
64
64
  }
65
65
  }