@mailwoman/resolver-wof-sqlite 10.7.0 → 11.0.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/lib/address/point/interpolation.ts +4 -4
- package/lib/address/point/spine-schema.ts +59 -0
- package/lib/address/point.ts +44 -1
- package/lib/ancestry/backfill.ts +3 -25
- package/lib/build/candidate.ts +22 -5
- package/lib/build/slim.ts +0 -19
- package/lib/candidate/alias-bags.ts +32 -14
- package/lib/candidate/bare-admin-names.ts +54 -0
- package/lib/candidate/importance.ts +2 -2
- package/lib/candidate/lookup.ts +63 -30
- package/lib/candidate/own-name.ts +1 -5
- package/lib/candidate/scoring.ts +4 -4
- package/lib/capitals.ts +2 -2
- package/lib/currency-backfill.ts +4 -4
- package/lib/fst/autocomplete.ts +1 -1
- package/lib/fst/binary-matcher.ts +379 -0
- package/lib/fst/builder.ts +1 -1
- package/lib/fst/deserialize-web.ts +14 -143
- package/lib/fst/matcher.ts +3 -3
- package/lib/fst/serialize.ts +1 -1
- package/lib/fst.ts +1 -0
- package/lib/geonames/aliases.ts +2 -2
- package/lib/index.ts +0 -1
- package/lib/interpolation.ts +3 -2
- package/lib/landmark/rows.ts +91 -0
- package/lib/landmark.ts +94 -0
- package/lib/lookup.ts +7 -27
- package/lib/nsul/lookup.ts +2 -1
- package/lib/nsul/schema.ts +2 -8
- package/lib/place-importance-schema.ts +1 -1
- package/lib/poi/lookup.ts +14 -14
- package/lib/primary-preference.ts +13 -20
- package/lib/proximity-rerank.ts +2 -2
- package/lib/reverse.ts +8 -5
- package/lib/schema.ts +0 -18
- package/lib/search-fetch.ts +3 -3
- package/lib/sqlite/convention-source.ts +1 -1
- package/lib/street/centroid.ts +2 -2
- package/lib/street/morphology/fst/builder.ts +3 -3
- package/lib/street/morphology/fst/loader.ts +3 -3
- package/lib/street/normalize.ts +150 -1
- package/lib/types.ts +15 -2
- package/lib/uprn/existence.ts +0 -1
- package/lib/uprn/lookup.ts +5 -5
- package/lib/uprn/schema.ts +3 -4
- package/lib/weights-overlay-linker.ts +4 -4
- package/out/address/point/interpolation.js +4 -4
- package/out/address/point/interpolation.js.map +1 -1
- package/out/address/point/spine-schema.d.ts +40 -0
- package/out/address/point/spine-schema.d.ts.map +1 -0
- package/out/address/point/spine-schema.js +40 -0
- package/out/address/point/spine-schema.js.map +1 -0
- package/out/address/point.d.ts.map +1 -1
- package/out/address/point.js +36 -1
- package/out/address/point.js.map +1 -1
- package/out/ancestry/backfill.d.ts.map +1 -1
- package/out/ancestry/backfill.js +3 -22
- package/out/ancestry/backfill.js.map +1 -1
- package/out/build/candidate.d.ts.map +1 -1
- package/out/build/candidate.js +17 -5
- package/out/build/candidate.js.map +1 -1
- package/out/build/slim.d.ts +1 -1
- package/out/build/slim.d.ts.map +1 -1
- package/out/build/slim.js +0 -12
- package/out/build/slim.js.map +1 -1
- package/out/candidate/alias-bags.d.ts +8 -0
- package/out/candidate/alias-bags.d.ts.map +1 -1
- package/out/candidate/alias-bags.js +20 -11
- package/out/candidate/alias-bags.js.map +1 -1
- package/out/candidate/bare-admin-names.d.ts +26 -0
- package/out/candidate/bare-admin-names.d.ts.map +1 -0
- package/out/candidate/bare-admin-names.js +38 -0
- package/out/candidate/bare-admin-names.js.map +1 -0
- package/out/candidate/importance.js +2 -2
- package/out/candidate/importance.js.map +1 -1
- package/out/candidate/lookup.d.ts.map +1 -1
- package/out/candidate/lookup.js +43 -23
- package/out/candidate/lookup.js.map +1 -1
- package/out/candidate/own-name.d.ts.map +1 -1
- package/out/candidate/own-name.js +1 -5
- package/out/candidate/own-name.js.map +1 -1
- package/out/candidate/scoring.js +4 -4
- package/out/candidate/scoring.js.map +1 -1
- package/out/capitals.js +2 -2
- package/out/capitals.js.map +1 -1
- package/out/currency-backfill.js +4 -4
- package/out/currency-backfill.js.map +1 -1
- package/out/env.d.ts +3 -0
- package/out/env.d.ts.map +1 -1
- package/out/fst/autocomplete.js +1 -1
- package/out/fst/autocomplete.js.map +1 -1
- package/out/fst/binary-matcher.d.ts +34 -0
- package/out/fst/binary-matcher.d.ts.map +1 -0
- package/out/fst/binary-matcher.js +294 -0
- package/out/fst/binary-matcher.js.map +1 -0
- package/out/fst/builder.js +1 -1
- package/out/fst/builder.js.map +1 -1
- package/out/fst/deserialize-web.d.ts +11 -6
- package/out/fst/deserialize-web.d.ts.map +1 -1
- package/out/fst/deserialize-web.js +12 -94
- package/out/fst/deserialize-web.js.map +1 -1
- package/out/fst/matcher.js +3 -3
- package/out/fst/matcher.js.map +1 -1
- package/out/fst/serialize.js +1 -1
- package/out/fst/serialize.js.map +1 -1
- package/out/fst.d.ts +1 -0
- package/out/fst.d.ts.map +1 -1
- package/out/fst.js +1 -0
- package/out/fst.js.map +1 -1
- package/out/geonames/aliases.js +2 -2
- package/out/geonames/aliases.js.map +1 -1
- package/out/index.d.ts +1 -1
- package/out/index.d.ts.map +1 -1
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts.map +1 -1
- package/out/interpolation.js +3 -2
- package/out/interpolation.js.map +1 -1
- package/out/landmark/rows.d.ts +63 -0
- package/out/landmark/rows.d.ts.map +1 -0
- package/out/landmark/rows.js +48 -0
- package/out/landmark/rows.js.map +1 -0
- package/out/landmark.d.ts +40 -0
- package/out/landmark.d.ts.map +1 -0
- package/out/landmark.js +78 -0
- package/out/landmark.js.map +1 -0
- package/out/lookup.d.ts +3 -7
- package/out/lookup.d.ts.map +1 -1
- package/out/lookup.js +4 -21
- package/out/lookup.js.map +1 -1
- package/out/nsul/lookup.d.ts +1 -1
- package/out/nsul/lookup.d.ts.map +1 -1
- package/out/nsul/lookup.js +1 -1
- package/out/nsul/lookup.js.map +1 -1
- package/out/nsul/schema.d.ts +2 -5
- package/out/nsul/schema.d.ts.map +1 -1
- package/out/nsul/schema.js +2 -7
- package/out/nsul/schema.js.map +1 -1
- package/out/place-importance-schema.js +1 -1
- package/out/place-importance-schema.js.map +1 -1
- package/out/poi/lookup.d.ts +3 -3
- package/out/poi/lookup.d.ts.map +1 -1
- package/out/poi/lookup.js +9 -9
- package/out/poi/lookup.js.map +1 -1
- package/out/primary-preference.d.ts +10 -10
- package/out/primary-preference.d.ts.map +1 -1
- package/out/primary-preference.js +8 -9
- package/out/primary-preference.js.map +1 -1
- package/out/proximity-rerank.js +2 -2
- package/out/proximity-rerank.js.map +1 -1
- package/out/reverse.d.ts.map +1 -1
- package/out/reverse.js +7 -5
- package/out/reverse.js.map +1 -1
- package/out/schema.d.ts +0 -17
- package/out/schema.d.ts.map +1 -1
- package/out/search-fetch.js +3 -3
- package/out/search-fetch.js.map +1 -1
- package/out/sqlite/convention-source.js +1 -1
- package/out/sqlite/convention-source.js.map +1 -1
- package/out/street/centroid.js +2 -2
- package/out/street/centroid.js.map +1 -1
- package/out/street/morphology/fst/builder.d.ts +2 -2
- package/out/street/morphology/fst/builder.js +2 -2
- package/out/street/morphology/fst/builder.js.map +1 -1
- package/out/street/morphology/fst/loader.d.ts +2 -2
- package/out/street/morphology/fst/loader.js +2 -2
- package/out/street/morphology/fst/loader.js.map +1 -1
- package/out/street/normalize.d.ts +23 -0
- package/out/street/normalize.d.ts.map +1 -1
- package/out/street/normalize.js +126 -1
- package/out/street/normalize.js.map +1 -1
- package/out/types.d.ts +13 -2
- package/out/types.d.ts.map +1 -1
- package/out/uprn/existence.d.ts.map +1 -1
- package/out/uprn/existence.js.map +1 -1
- package/out/uprn/lookup.d.ts.map +1 -1
- package/out/uprn/lookup.js +3 -3
- package/out/uprn/lookup.js.map +1 -1
- package/out/uprn/schema.d.ts +1 -1
- package/out/uprn/schema.d.ts.map +1 -1
- package/out/uprn/schema.js +1 -1
- package/out/uprn/schema.js.map +1 -1
- package/out/weights-overlay-linker.js +4 -4
- package/out/weights-overlay-linker.js.map +1 -1
- package/package.json +16 -13
- package/lib/coincident-roles.ts +0 -347
- package/out/coincident-roles.d.ts +0 -121
- package/out/coincident-roles.d.ts.map +0 -1
- package/out/coincident-roles.js +0 -222
- package/out/coincident-roles.js.map +0 -1
package/lib/candidate/lookup.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { type Ancestor, type GazetteerArtifactCoverage, referentialFromPopulatio
|
|
|
10
10
|
import { allRows } from "@mailwoman/core/utils"
|
|
11
11
|
import { jaroWinkler, levenshteinSimilarity } from "@mailwoman/match/comparators"
|
|
12
12
|
import { partitionByContainment } from "@mailwoman/resolver"
|
|
13
|
-
import {
|
|
13
|
+
import { haversine } from "@mailwoman/spatial/geometries/point/distance"
|
|
14
14
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
15
15
|
import { SQLiteLookup, type SQLiteLookupOptions } from "@mailwoman/sqlite/lookup"
|
|
16
16
|
|
|
@@ -67,6 +67,18 @@ type CandidateRow = Pick<
|
|
|
67
67
|
// Optional because older artifacts may not include `importance`.
|
|
68
68
|
Partial<Pick<CandidateTable, "importance">>
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* What a containment re-rank needs beside the rows: the keys to inject contained
|
|
72
|
+
* misses from and the shape filters the initial probe applied.
|
|
73
|
+
*/
|
|
74
|
+
interface ContainmentOptions {
|
|
75
|
+
nameKey: NameKey
|
|
76
|
+
strippedKey: NameKey
|
|
77
|
+
shapeFilters: string[]
|
|
78
|
+
shapeParams: Array<string | number>
|
|
79
|
+
limit: number
|
|
80
|
+
}
|
|
81
|
+
|
|
70
82
|
const FUZZY_FETCH = 40
|
|
71
83
|
|
|
72
84
|
const WORD_FUZZY_MIN = 0.85
|
|
@@ -177,7 +189,7 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
177
189
|
|
|
178
190
|
const bandIDs = [...REGION_CLASS_PLACETYPES, "country"]
|
|
179
191
|
.map((placetype) => this.#placetypeToID.get(placetype))
|
|
180
|
-
.filter((id)
|
|
192
|
+
.filter((id) => typeof id === "number")
|
|
181
193
|
|
|
182
194
|
if (bandIDs.length) {
|
|
183
195
|
this.#qualifierProbe = this.database.prepare(
|
|
@@ -219,7 +231,7 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
219
231
|
|
|
220
232
|
const cached = this.#intervalCache.get(sprID)
|
|
221
233
|
|
|
222
|
-
if (cached
|
|
234
|
+
if (cached) return cached
|
|
223
235
|
|
|
224
236
|
const row = this.#intervalProbe.get(sprID) as { pre: number; post: number } | undefined
|
|
225
237
|
const label = row ? { pre: Number(row.pre), post: Number(row.post) } : null
|
|
@@ -260,23 +272,30 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
260
272
|
}
|
|
261
273
|
|
|
262
274
|
/**
|
|
263
|
-
* Apply admin-containment re-rank
|
|
264
|
-
* then
|
|
275
|
+
* Apply admin-containment re-rank for a parsed region qualifier: resolve the
|
|
276
|
+
* qualifier's region rows, then {@linkcode #applyContainment}.
|
|
265
277
|
*/
|
|
266
278
|
#applyAdminContainment(
|
|
267
279
|
rows: Array<RankedRow<CandidateRow>>,
|
|
268
280
|
qualifier: string,
|
|
269
281
|
country: string | null,
|
|
270
|
-
opts:
|
|
271
|
-
nameKey: NameKey
|
|
272
|
-
strippedKey: NameKey
|
|
273
|
-
shapeFilters: string[]
|
|
274
|
-
shapeParams: Array<string | number>
|
|
275
|
-
limit: number
|
|
276
|
-
}
|
|
282
|
+
opts: ContainmentOptions
|
|
277
283
|
): Array<RankedRow<CandidateRow>> {
|
|
278
|
-
|
|
284
|
+
return this.#applyContainment(rows, this.#qualifierRegionIDs(qualifier, country), opts)
|
|
285
|
+
}
|
|
279
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Stamp containment in the qualifier rows, inject contained misses, then partition contained-first.
|
|
289
|
+
*
|
|
290
|
+
* The qualifier rows are a parsed region's rows or one resolved parent place (#2804).
|
|
291
|
+
* The injection reads the same name key under the shape filters and under the sub-locality placetypes,
|
|
292
|
+
* so a dependent locality the locality band excludes re-enters the list when its parent contains it.
|
|
293
|
+
*/
|
|
294
|
+
#applyContainment(
|
|
295
|
+
rows: Array<RankedRow<CandidateRow>>,
|
|
296
|
+
qualifierIDs: ReadonlySet<number>,
|
|
297
|
+
opts: ContainmentOptions
|
|
298
|
+
): Array<RankedRow<CandidateRow>> {
|
|
280
299
|
if (!qualifierIDs.size) {
|
|
281
300
|
for (const row of rows) {
|
|
282
301
|
row.containedByQualifier = false
|
|
@@ -326,7 +345,7 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
326
345
|
// Also inject contained neighborhood-band namesakes (primary-keyed only).
|
|
327
346
|
const bandIDs = ["neighbourhood", "macrohood", "microhood"]
|
|
328
347
|
.map((placetype) => this.#placetypeToID.get(placetype))
|
|
329
|
-
.filter((id)
|
|
348
|
+
.filter((id) => typeof id === "number")
|
|
330
349
|
|
|
331
350
|
if (bandIDs.length) {
|
|
332
351
|
const bandSQL =
|
|
@@ -375,7 +394,7 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
375
394
|
#postcodeAnchor(postcode: string, country?: string): { lat: number; lon: number } | null {
|
|
376
395
|
const placetypeID = this.#placetypeToID.get("postalcode")
|
|
377
396
|
|
|
378
|
-
if (placetypeID
|
|
397
|
+
if (typeof placetypeID !== "number") return null
|
|
379
398
|
|
|
380
399
|
const conds = ["name_key = ?", "placetype_id = ?"]
|
|
381
400
|
const params: Array<string | number> = [postcode.replaceAll(/\s+/g, ""), placetypeID]
|
|
@@ -383,7 +402,7 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
383
402
|
if (country) {
|
|
384
403
|
const countryID = this.#countryToID.get(country.toUpperCase())
|
|
385
404
|
|
|
386
|
-
if (countryID
|
|
405
|
+
if (typeof countryID !== "number") return null // Unknown country in this artifact.
|
|
387
406
|
conds.push("country_id = ?")
|
|
388
407
|
params.push(countryID)
|
|
389
408
|
}
|
|
@@ -457,7 +476,7 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
457
476
|
if (query.country) {
|
|
458
477
|
const cid = this.#countryToID.get(query.country.toUpperCase())
|
|
459
478
|
|
|
460
|
-
if (cid
|
|
479
|
+
if (typeof cid !== "number") return [] // Unknown country in this artifact.
|
|
461
480
|
filters.push("country_id = ?")
|
|
462
481
|
filterParams.push(cid)
|
|
463
482
|
}
|
|
@@ -468,7 +487,7 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
468
487
|
|
|
469
488
|
const ids = expandPlacetypeFilter(want as readonly string[])
|
|
470
489
|
.map((t) => this.#placetypeToID.get(t))
|
|
471
|
-
.filter((v)
|
|
490
|
+
.filter((v) => typeof v === "number")
|
|
472
491
|
|
|
473
492
|
if (!ids.length) return []
|
|
474
493
|
shapeFilters.push(`placetype_id IN (${ids.map(() => "?").join(",")})`)
|
|
@@ -534,16 +553,19 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
534
553
|
}
|
|
535
554
|
}
|
|
536
555
|
|
|
537
|
-
// Typo-tolerant fallback for true name misses only, skipping postcodes
|
|
556
|
+
// Typo-tolerant fallback for true name misses only, skipping postcodes.
|
|
557
|
+
// With no stated country, locale hint or parent to search within,
|
|
558
|
+
// a correction keeps its first letter.
|
|
559
|
+
// `Aucklnad` reaches Auckland, and `Anal Island` stays away from `Capual Island` in the Philippines.
|
|
538
560
|
const fuzzyCountryID =
|
|
539
561
|
!query.country && query.fuzzyCountry ? this.#countryToID.get(query.fuzzyCountry.toUpperCase()) : undefined
|
|
540
562
|
|
|
541
|
-
const
|
|
563
|
+
const hasCountryEvidence = !!query.country || regionID !== null || typeof fuzzyCountryID === "number"
|
|
542
564
|
|
|
543
565
|
if (
|
|
544
566
|
!rows.length &&
|
|
545
567
|
!wantsPostcode &&
|
|
546
|
-
!
|
|
568
|
+
!(query.fuzzyCountry && !query.country && typeof fuzzyCountryID !== "number") &&
|
|
547
569
|
this.#ftsProbe &&
|
|
548
570
|
this.#nameKeyExistsProbe &&
|
|
549
571
|
!this.#nameKeyExistsProbe.get(nameKey)
|
|
@@ -555,7 +577,7 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
555
577
|
|
|
556
578
|
const ranked = hits
|
|
557
579
|
.map((h) => ({ nk: String(h.name_key), s: wordFuzzySimilarity(nameKey, String(h.name_key)) }))
|
|
558
|
-
.filter((h) => h.s >= WORD_FUZZY_MIN)
|
|
580
|
+
.filter((h) => h.s >= WORD_FUZZY_MIN && (hasCountryEvidence || h.nk[0] === nameKey[0]))
|
|
559
581
|
// oxlint-disable-next-line unicorn/no-array-sort -- sorts a freshly-built array. toSorted would double-allocate on a hot path
|
|
560
582
|
.sort((a, b) => b.s - a.s)
|
|
561
583
|
|
|
@@ -599,7 +621,7 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
599
621
|
const outsideThreshold: RankedRow<CandidateRow>[] = []
|
|
600
622
|
|
|
601
623
|
for (const row of rows) {
|
|
602
|
-
const distanceKm =
|
|
624
|
+
const distanceKm = haversine(anchor.lat, anchor.lon, Number(row.latitude), Number(row.longitude))
|
|
603
625
|
|
|
604
626
|
if (distanceKm <= POSTCODE_CONTAINMENT_THRESHOLD_KM) {
|
|
605
627
|
insideThreshold.push({ row, distanceKm })
|
|
@@ -617,14 +639,23 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
617
639
|
}
|
|
618
640
|
|
|
619
641
|
// Admin-containment re-rank runs last.
|
|
642
|
+
const containmentOptions: ContainmentOptions = {
|
|
643
|
+
nameKey,
|
|
644
|
+
strippedKey: normalizeLocalityForKey(stripLocalityQualifier(text)),
|
|
645
|
+
shapeFilters,
|
|
646
|
+
shapeParams,
|
|
647
|
+
limit,
|
|
648
|
+
}
|
|
649
|
+
|
|
620
650
|
if (query.regionQualifier?.trim() && this.#qualifierProbe && this.#wantsLocality(query.placetype)) {
|
|
621
|
-
rows = this.#applyAdminContainment(rows, query.regionQualifier.trim(), query.country ?? null,
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
651
|
+
rows = this.#applyAdminContainment(rows, query.regionQualifier.trim(), query.country ?? null, containmentOptions)
|
|
652
|
+
} else if (
|
|
653
|
+
typeof query.parentQualifierID === "number" &&
|
|
654
|
+
this.#ancestorsProbe &&
|
|
655
|
+
this.#wantsLocality(query.placetype)
|
|
656
|
+
) {
|
|
657
|
+
// A resolved parent qualifies by id, so no name probe runs (#2804).
|
|
658
|
+
rows = this.#applyContainment(rows, new Set([query.parentQualifierID]), containmentOptions)
|
|
628
659
|
}
|
|
629
660
|
|
|
630
661
|
const candidates = rows.map((row): PlaceCandidate => {
|
|
@@ -656,6 +687,8 @@ export class WOFCandidateTableLookup extends SQLiteLookup<CandidateDatabase> imp
|
|
|
656
687
|
|
|
657
688
|
variantAliasExempted: row.variantExempted ? true : null,
|
|
658
689
|
|
|
690
|
+
matchedAlias: row.is_primary === 0 ? true : null,
|
|
691
|
+
|
|
659
692
|
population: hasPopulation ? row.population : null,
|
|
660
693
|
referential: hasPopulation ? referentialFromPopulation(row.population) : null,
|
|
661
694
|
encyclopedic: null,
|
|
@@ -114,11 +114,7 @@ export function romanizeNameKey(key: string): string | null {
|
|
|
114
114
|
out += CYRILLIC_TO_LATIN[ch] ?? ch
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
out = out
|
|
118
|
-
.normalize("NFD")
|
|
119
|
-
.replaceAll(/[̀-ͯ]/g, "")
|
|
120
|
-
.replaceAll(/\s+/g, " ")
|
|
121
|
-
.trim()
|
|
117
|
+
out = out.normalize("NFD").replaceAll(/[̀-ͯ]/g, "").replaceAll(/\s+/g, " ").trim()
|
|
122
118
|
|
|
123
119
|
return /^[a-z0-9 ]*$/.test(out) ? out : null
|
|
124
120
|
}
|
package/lib/candidate/scoring.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { compareReferential, referentialFromPopulation } from "@mailwoman/core/resolver"
|
|
8
|
-
import {
|
|
8
|
+
import { haversine } from "@mailwoman/spatial/geometries/point/distance"
|
|
9
9
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
10
10
|
|
|
11
11
|
import { exactMatchIDs, officialNameIDs } from "#exact-match"
|
|
@@ -47,7 +47,7 @@ export function candidateFromSearchRow(
|
|
|
47
47
|
score += weights.countryMatchBoost
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
if (query.parentID
|
|
50
|
+
if (query.parentID) {
|
|
51
51
|
score += row.parent_id === query.parentID ? weights.directChildBoost : weights.descendantBoost
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -74,7 +74,7 @@ export function candidateFromSearchRow(
|
|
|
74
74
|
let scoreTerm = 0
|
|
75
75
|
|
|
76
76
|
for (const h of hints) {
|
|
77
|
-
const d =
|
|
77
|
+
const d = haversine(h.lat, h.lon, row.lat, row.lon)
|
|
78
78
|
const decay = h.weight / (1 + d / weights.proximityScaleKm)
|
|
79
79
|
const prom = decay * weights.biasBoost
|
|
80
80
|
|
|
@@ -184,7 +184,7 @@ export function rankCandidates<DB>(
|
|
|
184
184
|
.map((c) => c.id as number),
|
|
185
185
|
query.text
|
|
186
186
|
)
|
|
187
|
-
:
|
|
187
|
+
: null
|
|
188
188
|
|
|
189
189
|
const kind = (c: PlaceCandidate): number => {
|
|
190
190
|
if (!exactIDs.has(c.id as number)) return 0
|
package/lib/capitals.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* radius a centroid-drift allowance rather than a catchment.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import { haversine } from "@mailwoman/spatial/geometries/point/distance"
|
|
19
19
|
|
|
20
20
|
import { normalizeLocalityForKey } from "#street/normalize"
|
|
21
21
|
|
|
@@ -129,7 +129,7 @@ export class CapitalIndex {
|
|
|
129
129
|
if (
|
|
130
130
|
point.level > best &&
|
|
131
131
|
point.keys.has(key) &&
|
|
132
|
-
|
|
132
|
+
haversine(latitude, longitude, point.latitude, point.longitude) <= CAPITAL_MATCH_RADIUS_KM
|
|
133
133
|
) {
|
|
134
134
|
best = point.level
|
|
135
135
|
}
|
package/lib/currency-backfill.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { pathExists } from "@mailwoman/core/fs/readers/stat"
|
|
10
|
-
import { isStrictlyFiner } from "@mailwoman/
|
|
11
|
-
import {
|
|
10
|
+
import { isStrictlyFiner } from "@mailwoman/gazetteer/whosonfirst"
|
|
11
|
+
import { haversine } from "@mailwoman/spatial/geometries/point/distance"
|
|
12
12
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
13
13
|
import { type PathBuilderLike, resolvePath } from "path-ts"
|
|
14
14
|
import { TSVSpliterator } from "spliterator"
|
|
@@ -208,7 +208,7 @@ export async function resurrectCurrencyHoles(ctx: {
|
|
|
208
208
|
// A live row blocks unless it is strictly finer than the dead one.
|
|
209
209
|
// An unranked placetype also blocks because the failure mode is inventing a place.
|
|
210
210
|
const liveNear = liveStmt.all(cc, name).some((row) => {
|
|
211
|
-
if (
|
|
211
|
+
if (haversine(dLat, dLon, Number(row.latitude), Number(row.longitude)) > CURRENCY_BACKFILL_RADIUS_KM) {
|
|
212
212
|
return false
|
|
213
213
|
}
|
|
214
214
|
|
|
@@ -222,7 +222,7 @@ export async function resurrectCurrencyHoles(ctx: {
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
const near = (attestors.get(pkey) ?? []).filter(
|
|
225
|
-
(g) =>
|
|
225
|
+
(g) => haversine(dLat, dLon, g.lat, g.lon) <= CURRENCY_BACKFILL_RADIUS_KM
|
|
226
226
|
)
|
|
227
227
|
|
|
228
228
|
if (!near.length) {
|
package/lib/fst/autocomplete.ts
CHANGED
|
@@ -135,7 +135,7 @@ class FSTReader implements AncestrieReaderLike<PlaceEntry> {
|
|
|
135
135
|
|
|
136
136
|
entriesAt(stateID: number, limit?: number): AncestrieRecord<PlaceEntry>[] {
|
|
137
137
|
const places = this.#fst.accepting(stateID)
|
|
138
|
-
const selected = limit
|
|
138
|
+
const selected = typeof limit !== "number" ? places : topByReferential(places, limit)
|
|
139
139
|
|
|
140
140
|
return selected.map((entry) => {
|
|
141
141
|
this.#chains.set(entry.wofID, entry.parentChain)
|