Skip to content

Commit a470222

Browse files
authored
Timezone overlay, etc. (#32)
* Improve non-English geographic search and other search engine improvements. * Live search mode. * Updated asteroid/comet data. * Update zoneloc API call to use geo-tz. * Update Angular to version 14. * Add timezone map overlay option. * Add visual reminder that found locations can be saved.
1 parent 1519cc9 commit a470222

38 files changed

Lines changed: 2338 additions & 1260 deletions

package-lock.json

Lines changed: 455 additions & 428 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"name": "svc-ng",
3-
"version": "1.15.2",
3+
"version": "1.16.0",
44
"license": "MIT AND GPL-3.0-or-later",
55
"author": "Kerry Shetline <kerry@shetline.com>",
66
"scripts": {
77
"ng": "ng",
8-
"start": "ng serve",
8+
"start": "ng serve --proxy-config proxy.conf.json",
99
"build": "rimraf dist && ng build --configuration production --source-map=true && cd server && npm run build && cd .. && copyfiles -u 2 \"server/dist/**/*\" \"dist\"",
1010
"build-debug": "ng build --source-map=true",
1111
"test": "ng test",
@@ -14,62 +14,62 @@
1414
},
1515
"private": true,
1616
"dependencies": {
17-
"@angular/animations": "^14.0.2",
18-
"@angular/cdk": "^14.0.2",
19-
"@angular/common": "^14.0.2",
20-
"@angular/compiler": "^14.0.2",
21-
"@angular/core": "^14.0.2",
22-
"@angular/forms": "^14.0.2",
23-
"@angular/platform-browser": "^14.0.2",
24-
"@angular/platform-browser-dynamic": "^14.0.2",
25-
"@angular/router": "^14.0.2",
17+
"@angular/animations": "^14.0.6",
18+
"@angular/cdk": "^14.0.5",
19+
"@angular/common": "^14.0.6",
20+
"@angular/compiler": "^14.0.6",
21+
"@angular/core": "^14.0.6",
22+
"@angular/forms": "^14.0.6",
23+
"@angular/platform-browser": "^14.0.6",
24+
"@angular/platform-browser-dynamic": "^14.0.6",
25+
"@angular/router": "^14.0.6",
2626
"@fortawesome/fontawesome-free": "^6.1.1",
2727
"@tubular/array-buffer-reader": "^3.0.2",
2828
"@tubular/astronomy": "^3.3.0",
2929
"@tubular/math": "^3.1.0",
3030
"@tubular/ng-widgets": "^2.2.0",
31-
"@tubular/time": "^3.8.2",
32-
"@tubular/util": "^4.11.0",
31+
"@tubular/time": "^3.8.3",
32+
"@tubular/util": "^4.12.0",
3333
"core-js": "^2.6.12",
3434
"detect-resize": "^0.1.5",
3535
"lodash-es": "^4.17.21",
3636
"primeicons": "^5.0.0",
37-
"primeng": "^13.4.1",
37+
"primeng": "^14.0.0-rc.1",
3838
"rxjs": "^6.6.7",
39-
"three": "^0.140.2",
40-
"zone.js": "~0.11.5"
39+
"three": "^0.142.0",
40+
"zone.js": "~0.11.6"
4141
},
4242
"devDependencies": {
43-
"@angular-devkit/build-angular": "^14.0.2",
44-
"@angular/cli": "^14.0.2",
45-
"@angular/compiler-cli": "^14.0.2",
46-
"@angular/language-service": "^14.0.2",
47-
"@tubular/browser-check": "^1.3.0",
43+
"@angular-devkit/build-angular": "^14.0.6",
44+
"@angular/cli": "^14.0.6",
45+
"@angular/compiler-cli": "^14.0.6",
46+
"@angular/language-service": "^14.0.6",
47+
"@tubular/browser-check": "^2.0.0",
4848
"@types/googlemaps": "^3.43.3",
4949
"@types/jasmine": "^4.0.3",
5050
"@types/jasminewd2": "^2.0.10",
5151
"@types/lodash-es": "^4.17.6",
52-
"@types/three": "^0.140.0",
53-
"@typescript-eslint/eslint-plugin": "^5.28.0",
54-
"@typescript-eslint/parser": "^5.28.0",
52+
"@types/three": "^0.141.0",
53+
"@typescript-eslint/eslint-plugin": "^5.30.6",
54+
"@typescript-eslint/parser": "^5.30.6",
5555
"codelyzer": "^6.0.2",
5656
"copyfiles": "^2.4.1",
57-
"eslint": "^8.17.0",
57+
"eslint": "^8.20.0",
5858
"eslint-config-standard": "^17.0.0",
5959
"eslint-plugin-import": "^2.26.0",
60-
"eslint-plugin-n": "^15.2.2",
60+
"eslint-plugin-n": "^15.2.4",
6161
"eslint-plugin-node": "^11.1.0",
6262
"eslint-plugin-promise": "^6.0.0",
6363
"jasmine-core": "~4.2.0",
6464
"jasmine-spec-reporter": "~7.0.0",
65-
"karma": "^6.3.19",
65+
"karma": "^6.4.0",
6666
"karma-chrome-launcher": "~3.1.1",
6767
"karma-coverage-istanbul-reporter": "~3.0.3",
68-
"karma-jasmine": "~5.0.0",
68+
"karma-jasmine": "~5.1.0",
6969
"karma-jasmine-html-reporter": "^1.7.0",
7070
"protractor": "~7.0.0",
7171
"rimraf": "^3.0.2",
72-
"ts-node": "^10.7.0",
72+
"ts-node": "^10.9.1",
7373
"typescript": "^4.5.5"
7474
}
7575
}

proxy.conf.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"/api/*": {
3+
"target": "http://localhost:4201"
4+
},
5+
"/maps/*": {
6+
"target": "http://localhost:4201"
7+
}
8+
}

server/app/app.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { router as stateRouter } from './states';
77
import { router as ipToLocationRouter } from './ip-to-location';
88
import { router as logRouter } from './log-access';
99
import { router as zoneRouter } from './zone-for-location';
10+
import { router as zoneMapRouter } from './zone-map-url';
1011
import { router as mapsRouter } from './maps-api';
1112
import { router as directoryRouter } from './directory-listing';
1213
import { initTimezoneLargeAlt } from '@tubular/time';
@@ -41,14 +42,21 @@ app.use(morgan((tokens, req, res) => {
4142
}));
4243

4344
app.use('/atlas/', atlasRouter);
45+
app.use('/api/atlas/', atlasRouter);
4446
app.use('/atlasdb/atlas/', atlasRouter); // Legacy Tomcat path
4547
app.use('/states/', stateRouter);
48+
app.use('/api/states/', stateRouter);
4649
app.use('/atlasdb/states/', stateRouter); // Legacy Tomcat path
4750
app.use('/ip/', ipToLocationRouter);
51+
app.use('/api/ip/', ipToLocationRouter);
4852
app.use('/log/', logRouter);
53+
app.use('/api/log/', logRouter);
4954
app.use('/zoneloc/', zoneRouter);
55+
app.use('/api/zoneloc/', zoneRouter);
5056
app.use('/timeservices/zoneloc/', zoneRouter); // Legacy Tomcat path
57+
app.use('/api/zonemap/', zoneMapRouter);
5158
app.use('/maps/', mapsRouter);
59+
app.use('/api/maps/', mapsRouter);
5260
// Make the flags folder browsable.
5361
app.use('/assets/resources/flags/', directoryRouter);
5462
app.use(express.static(pathJoin(__dirname, 'public')));
Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { doubleMetaphone } from './double-metaphone';
33
import { Pool, PoolConnection } from './mysql-await-async';
44
import {
55
closeMatchForState, code3ToName, countyStateCleanUp, getFlagCode, LocationMap, makeLocationKey,
6-
ParsedSearchString, simplify, closeMatchForCity, code3ToNameByLang, admin1ToNameByLang, admin1s, code2ToCode3, admin2s
6+
ParsedSearchString, simplify, closeMatchForCity, code3ToNameByLang, admin1ToNameByLang, admin1s, code2ToCode3, admin2s, admin1Abbreviations
77
} from './gazetteer';
88
import { AtlasLocation } from './atlas-location';
99
import { MIN_EXTERNAL_SOURCE } from './common';
@@ -24,11 +24,25 @@ const MAX_MONTHS_BEFORE_REDOING_EXTENDED_SEARCH = 12;
2424
const ZIP_RANK = 9;
2525
const ZIP_SUPPLEMENT_RANK = 1;
2626

27+
const logTimersByIp = new Map<string, NodeJS.Timeout>();
28+
2729
export function logMessage(message: string, lang?: string, ip?: string, noTrace = false): void {
2830
svcApiConsole.info(message);
2931

30-
if (!noTrace)
31-
logMessageAux(message, lang, ip, false);
32+
if (!noTrace) {
33+
if (!ip)
34+
logMessageAux(message, lang, '', false);
35+
else {
36+
let timer = logTimersByIp.get(ip);
37+
38+
if (timer)
39+
clearTimeout(timer);
40+
41+
timer = setTimeout(() => { logTimersByIp.delete(ip); logMessageAux(message, lang, ip, false); }, 3000);
42+
timer.unref();
43+
logTimersByIp.set(ip, timer);
44+
}
45+
}
3246
}
3347

3448
export function logWarning(message: string, noTrace = false): void {
@@ -132,22 +146,27 @@ export async function doDataBaseSearch(connection: PoolConnection, parsed: Parse
132146
const matches = new LocationMap();
133147
const postal = !!parsed.postalCode;
134148
let altParseMatches = 0;
149+
let passRankAdj = 0;
135150

136151
for (let pass = 0; pass < 2; ++pass) {
137152
const altParse = (pass > 0 && !!parsed.altCity);
138153
const city = (altParse ? parsed.altCity : parsed.targetCity);
139154
const targetState = (altParse ? parsed.altState : parsed.targetState);
140155
const simplifiedCity = simplify(city);
141-
const condition = (pass === 0 ? ' AND rank > 0' : '');
142156

143157
examined.clear();
144158

145159
for (let matchType: number = MatchType.EXACT_MATCH; matchType <= MatchType.SOUNDS_LIKE; ++matchType) {
146-
let rankAdjust = 0;
160+
if (lang && lang !== 'en' && matchType === MatchType.SOUNDS_LIKE)
161+
continue;
162+
163+
let rankAdjust = -passRankAdj;
147164
let query: string;
148165
let values: any[];
149166
let fromAlt = false;
150167

168+
passRankAdj = 0;
169+
151170
switch (matchType) {
152171
case MatchType.EXACT_MATCH:
153172
if (postal) {
@@ -165,7 +184,7 @@ export async function doDataBaseSearch(connection: PoolConnection, parsed: Parse
165184
query = `SELECT * FROM gazetteer_alt_names WHERE key_name = ? AND lang = ? AND type = 'P' AND colloquial = 0 AND historic = 0`;
166185
}
167186
else
168-
query = 'SELECT * FROM gazetteer WHERE key_name = ?' + condition;
187+
query = 'SELECT * FROM gazetteer WHERE key_name = ?';
169188
}
170189
break;
171190

@@ -184,7 +203,7 @@ export async function doDataBaseSearch(connection: PoolConnection, parsed: Parse
184203
fromAlt = true;
185204
}
186205
else
187-
query = 'SELECT * FROM gazetteer WHERE key_name >= ? AND key_name < ? ' + condition;
206+
query = 'SELECT * FROM gazetteer WHERE key_name >= ? AND key_name < ? ';
188207

189208
break;
190209

@@ -193,7 +212,7 @@ export async function doDataBaseSearch(connection: PoolConnection, parsed: Parse
193212
continue;
194213

195214
rankAdjust = -1;
196-
query = 'SELECT * FROM gazetteer WHERE mphone1 = ? OR mphone2 = ?' + condition;
215+
query = 'SELECT * FROM gazetteer WHERE mphone1 = ? OR mphone2 = ?';
197216
values = doubleMetaphone(parsed.targetCity);
198217
break;
199218
}
@@ -211,7 +230,7 @@ export async function doDataBaseSearch(connection: PoolConnection, parsed: Parse
211230
values = [results.map((row: any) => row.gazetteer_id).filter((id: number) => id !== 0).join(', ')];
212231

213232
if (values[0]) {
214-
query = `SELECT * FROM gazetteer WHERE id IN (${values[0]})` + condition;
233+
query = `SELECT * FROM gazetteer WHERE id IN (${values[0]})`;
215234
results = (await connection.queryResults(query)) || [];
216235
}
217236
else
@@ -338,11 +357,17 @@ export async function doDataBaseSearch(connection: PoolConnection, parsed: Parse
338357
location.source = source;
339358
location.geonamesID = geonamesID;
340359

341-
if (/^\d+$/.test(location.state) || (country !== 'USA' && country !== 'CAN' && /^[A-Z]{3,}$/.test(location.state))) {
360+
const numericState = /^\d+$/.test(location.state);
361+
362+
if (numericState || (country !== 'USA' && country !== 'CAN' && /^[A-Z]{3,}$/.test(location.state))) {
342363
const key = country + '.' + location.state;
364+
const abbr = admin1Abbreviations[key];
343365

344-
location.state = (admin1ToNameByLang[key] || {})[lang || 'en'] || (admin1ToNameByLang[key] || {})[''] ||
345-
admin1s[key] || location.state;
366+
if (numericState && abbr)
367+
location.state = abbr;
368+
else
369+
location.state = (admin1ToNameByLang[key] || {})[lang || 'en'] || (admin1ToNameByLang[key] || {})[''] ||
370+
admin1s[key] || location.state;
346371
}
347372

348373
if (matchType === MatchType.EXACT_MATCH_ALT)
@@ -364,8 +389,25 @@ export async function doDataBaseSearch(connection: PoolConnection, parsed: Parse
364389
break;
365390
}
366391

367-
if (postal)
392+
let postalOnce = postal;
393+
394+
if (postal && parsed.postalCode.includes(' ')) {
395+
parsed.postalCode = parsed.postalCode.replace(/\s+\S.*$/, '');
396+
postalOnce = false;
397+
}
398+
399+
if (postalOnce)
368400
break;
401+
else if (lang && lang !== 'en') {
402+
--pass;
403+
lang = '';
404+
passRankAdj = (matches.size > 0 ? 2 : 0);
405+
}
406+
else if (pass === 0 && matches.size === 0 &&
407+
parsed.targetState && simplifiedCity === simplify(parsed.targetState)) {
408+
--pass;
409+
parsed.targetState = '';
410+
}
369411
}
370412

371413
if (altParseMatches === 0) {

server/app/atlas.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { max } from '@tubular/math';
22
import { Request, Response, Router } from 'express';
33

44
import { asyncHandler, notFoundForEverythingElse, formatVariablePrecision } from './common';
5-
import { doDataBaseSearch, hasSearchBeenDoneRecently, logMessage, logSearchResults, pool } from './atlas_database';
5+
import { doDataBaseSearch, hasSearchBeenDoneRecently, logMessage, logSearchResults, pool } from './atlas-database';
66
import {
77
celestialNames, initGazetteer, LocationMap, ParsedSearchString, parseSearchString,
88
roughDistanceBetweenLocationsInKm
@@ -68,7 +68,7 @@ router.get('/', asyncHandler(async (req: Request, res: Response) => {
6868
const withoutDB = /only|geonames|getty/i.test(remoteMode);
6969
const extend = (remoteMode === 'extend' || remoteMode === 'only' || remoteMode === 'forced');
7070
const client = (req.query.client ? req.query.client.toString().toLowerCase() : '');
71-
const svc = (!client || client === 'sa' || client === 'web');
71+
const svc = (!client || /^(sa|svc|web)$/.test(client));
7272
const limit = Math.min(toInt(req.query.limit, DEFAULT_MATCH_LIMIT), MAX_MATCH_LIMIT);
7373
const noTrace = toBoolean(req.query.notrace, false, true) || remoteMode === 'only';
7474
const dbUpdate = DB_UPDATE && !noTrace;
@@ -180,7 +180,7 @@ router.get('/', asyncHandler(async (req: Request, res: Response) => {
180180
else if (callback)
181181
res.jsonp(result);
182182
else
183-
res.send(result);
183+
res.json(result);
184184
}));
185185

186186
function copyAndMergeLocations(destination: LocationArrayMap, source: LocationMap): void {

0 commit comments

Comments
 (0)