@mailwoman/resolver-wof-sqlite 9.4.0 → 10.1.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/README.md +8 -8
- package/lib/address/{point-interpolation.ts → point/interpolation.ts} +40 -38
- package/lib/address/point/schema.ts +132 -0
- package/lib/address/point.ts +68 -81
- package/lib/{address/index.ts → address.ts} +2 -2
- package/lib/ancestry/backfill.ts +24 -88
- package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
- package/lib/build/candidate.ts +608 -0
- package/lib/build/population-corrections.ts +64 -0
- package/lib/build/slim.ts +338 -0
- package/lib/candidate/alias-bags.ts +74 -14
- package/lib/candidate/ancestors/schema.ts +211 -0
- package/lib/candidate/{ancestors-sidecar.ts → ancestors/sidecar.ts} +17 -24
- package/lib/candidate/country-display-names.ts +6 -20
- package/lib/candidate/extract-fold.ts +12 -57
- package/lib/candidate/fts.ts +28 -0
- package/lib/candidate/importance.ts +221 -0
- package/lib/candidate/lookup.ts +669 -0
- package/lib/candidate/name-roles.ts +16 -34
- package/lib/candidate/own-name.ts +34 -31
- package/lib/candidate/place-attrs.ts +20 -13
- package/lib/candidate/schema.ts +211 -0
- package/lib/candidate/scoring.ts +222 -0
- package/lib/capital-schema.ts +21 -17
- package/lib/capitals.ts +26 -24
- package/lib/coincident-roles.ts +74 -55
- package/lib/convention/schema.ts +13 -7
- package/lib/convention.ts +144 -0
- package/lib/coverage-manifest-schema.ts +34 -39
- package/lib/currency-backfill.ts +24 -69
- package/lib/env.ts +1 -1
- package/lib/exact-match.ts +31 -20
- package/lib/extracts.ts +96 -73
- package/lib/fst/autocomplete.ts +52 -35
- package/lib/fst/builder.ts +30 -36
- package/lib/fst/deserialize-web.ts +8 -9
- package/lib/fst/format.ts +41 -23
- package/lib/fst/freshness.ts +17 -95
- package/lib/fst/matcher.ts +2 -2
- package/lib/fst/serialize.ts +45 -60
- package/lib/fst/types.ts +71 -59
- package/lib/fts/query.ts +5 -36
- package/lib/fts.ts +280 -0
- package/lib/geonames/aliases.ts +72 -149
- package/lib/geonames/postal.ts +162 -77
- package/lib/index.ts +20 -27
- package/lib/interpolation.ts +43 -96
- package/lib/lookup.ts +137 -257
- package/lib/name-score.ts +9 -7
- package/lib/nsul/lookup.ts +36 -53
- package/lib/nsul/schema.ts +16 -43
- package/lib/paths.ts +159 -0
- package/lib/place-importance-schema.ts +57 -164
- package/lib/poi/lookup.ts +100 -109
- package/lib/poi/schema.ts +42 -26
- package/lib/polygon-schema.ts +10 -6
- package/lib/postal/city/alias/lookup.ts +79 -0
- package/lib/postal/{city-alias-schema.ts → city/alias/schema.ts} +14 -10
- package/lib/postal/{city-candidate-schema.ts → city/candidate-schema.ts} +26 -20
- package/lib/postal.ts +9 -0
- package/lib/postcode-point-lookup.ts +9 -19
- package/lib/primary-preference.ts +45 -125
- package/lib/proximity-rerank.ts +43 -32
- package/lib/ranking-weights.ts +98 -76
- package/lib/region-keys.ts +68 -40
- package/lib/reverse.ts +108 -72
- package/lib/schema.ts +74 -42
- package/lib/search-fetch.ts +90 -68
- package/lib/sqlite/convention-source.ts +57 -0
- package/lib/sqlite/utils.ts +79 -0
- package/lib/street/centroid/schema.ts +149 -0
- package/lib/street/centroid.ts +16 -25
- package/lib/street/{morphology-fst-builder.ts → morphology/fst/builder.ts} +56 -41
- package/lib/street/{morphology-fst-loader.ts → morphology/fst/loader.ts} +36 -29
- package/lib/street/name-lookup.ts +43 -35
- package/lib/street/normalize.ts +85 -216
- package/lib/street/segment-schema.ts +116 -43
- package/lib/{street/index.ts → street.ts} +3 -3
- package/lib/types.ts +94 -140
- package/lib/unified-schema.ts +29 -29
- package/lib/uprn/existence.ts +30 -19
- package/lib/uprn/lookup.ts +28 -67
- package/lib/uprn/schema.ts +18 -40
- package/lib/weights-overlay-linker.ts +204 -232
- package/out/address/point/interpolation.d.ts +45 -0
- package/out/address/point/interpolation.d.ts.map +1 -0
- package/out/address/{point-interpolation.js → point/interpolation.js} +38 -38
- package/out/address/point/interpolation.js.map +1 -0
- package/out/address/point/schema.d.ts +74 -0
- package/out/address/point/schema.d.ts.map +1 -0
- package/out/address/{point-schema.js → point/schema.js} +4 -20
- package/out/address/point/schema.js.map +1 -0
- package/out/address/point.d.ts +24 -21
- package/out/address/point.d.ts.map +1 -1
- package/out/address/point.js +60 -77
- package/out/address/point.js.map +1 -1
- package/out/{address/index.js → address.d.ts} +3 -3
- package/out/address.d.ts.map +1 -0
- package/out/{address/index.d.ts → address.js} +3 -3
- package/out/address.js.map +1 -0
- package/out/ancestry/backfill.d.ts +11 -64
- package/out/ancestry/backfill.d.ts.map +1 -1
- package/out/ancestry/backfill.js +18 -76
- package/out/ancestry/backfill.js.map +1 -1
- package/out/ancestry.d.ts +49 -0
- package/out/ancestry.d.ts.map +1 -0
- package/out/ancestry.js +59 -0
- package/out/ancestry.js.map +1 -0
- package/out/build/candidate.d.ts +170 -0
- package/out/build/candidate.d.ts.map +1 -0
- package/out/{build-candidate.js → build/candidate.js} +33 -102
- package/out/build/candidate.js.map +1 -0
- package/out/build/population-corrections.d.ts +52 -0
- package/out/build/population-corrections.d.ts.map +1 -0
- package/out/build/population-corrections.js +40 -0
- package/out/build/population-corrections.js.map +1 -0
- package/out/build/slim.d.ts +44 -0
- package/out/build/slim.d.ts.map +1 -0
- package/out/{build-slim.js → build/slim.js} +33 -94
- package/out/build/slim.js.map +1 -0
- package/out/candidate/alias-bags.d.ts +13 -5
- package/out/candidate/alias-bags.d.ts.map +1 -1
- package/out/candidate/alias-bags.js +55 -12
- package/out/candidate/alias-bags.js.map +1 -1
- package/out/candidate/ancestors/schema.d.ts +166 -0
- package/out/candidate/ancestors/schema.d.ts.map +1 -0
- package/out/candidate/ancestors/schema.js +131 -0
- package/out/candidate/ancestors/schema.js.map +1 -0
- package/out/candidate/ancestors/sidecar.d.ts +27 -0
- package/out/candidate/ancestors/sidecar.d.ts.map +1 -0
- package/out/candidate/{ancestors-sidecar.js → ancestors/sidecar.js} +17 -24
- package/out/candidate/ancestors/sidecar.js.map +1 -0
- package/out/candidate/country-display-names.d.ts +5 -15
- package/out/candidate/country-display-names.d.ts.map +1 -1
- package/out/candidate/country-display-names.js +6 -20
- package/out/candidate/country-display-names.js.map +1 -1
- package/out/candidate/extract-fold.d.ts +8 -16
- package/out/candidate/extract-fold.d.ts.map +1 -1
- package/out/candidate/extract-fold.js +6 -48
- package/out/candidate/extract-fold.js.map +1 -1
- package/out/candidate/fts.d.ts +19 -0
- package/out/candidate/fts.d.ts.map +1 -0
- package/out/candidate/fts.js +22 -0
- package/out/candidate/fts.js.map +1 -0
- package/out/candidate/importance.d.ts +127 -0
- package/out/candidate/importance.d.ts.map +1 -0
- package/out/candidate/importance.js +162 -0
- package/out/candidate/importance.js.map +1 -0
- package/out/candidate/lookup.d.ts +36 -0
- package/out/candidate/lookup.d.ts.map +1 -0
- package/out/candidate/lookup.js +456 -0
- package/out/candidate/lookup.js.map +1 -0
- package/out/candidate/name-roles.d.ts +10 -22
- package/out/candidate/name-roles.d.ts.map +1 -1
- package/out/candidate/name-roles.js +15 -33
- package/out/candidate/name-roles.js.map +1 -1
- package/out/candidate/own-name.d.ts +23 -25
- package/out/candidate/own-name.d.ts.map +1 -1
- package/out/candidate/own-name.js +34 -31
- package/out/candidate/own-name.js.map +1 -1
- package/out/candidate/place-attrs.d.ts +20 -13
- package/out/candidate/place-attrs.d.ts.map +1 -1
- package/out/candidate/place-attrs.js +4 -5
- package/out/candidate/place-attrs.js.map +1 -1
- package/out/candidate/schema.d.ts +120 -0
- package/out/candidate/schema.d.ts.map +1 -0
- package/out/{candidate-schema.js → candidate/schema.js} +11 -23
- package/out/candidate/schema.js.map +1 -0
- package/out/{candidate-scoring.d.ts → candidate/scoring.d.ts} +8 -9
- package/out/candidate/scoring.d.ts.map +1 -0
- package/out/candidate/scoring.js +157 -0
- package/out/candidate/scoring.js.map +1 -0
- package/out/capital-schema.d.ts +21 -17
- package/out/capital-schema.d.ts.map +1 -1
- package/out/capital-schema.js +17 -15
- package/out/capital-schema.js.map +1 -1
- package/out/capitals.d.ts +26 -24
- package/out/capitals.d.ts.map +1 -1
- package/out/capitals.js +23 -21
- package/out/capitals.js.map +1 -1
- package/out/coincident-roles.d.ts +60 -43
- package/out/coincident-roles.d.ts.map +1 -1
- package/out/coincident-roles.js +61 -50
- package/out/coincident-roles.js.map +1 -1
- package/out/convention/schema.d.ts +12 -6
- package/out/convention/schema.d.ts.map +1 -1
- package/out/convention/schema.js +6 -4
- package/out/convention/schema.js.map +1 -1
- package/out/convention.d.ts +97 -0
- package/out/convention.d.ts.map +1 -0
- package/out/convention.js +81 -0
- package/out/convention.js.map +1 -0
- package/out/coverage-manifest-schema.d.ts +33 -38
- package/out/coverage-manifest-schema.d.ts.map +1 -1
- package/out/coverage-manifest-schema.js +20 -29
- package/out/coverage-manifest-schema.js.map +1 -1
- package/out/currency-backfill.d.ts +13 -35
- package/out/currency-backfill.d.ts.map +1 -1
- package/out/currency-backfill.js +12 -39
- package/out/currency-backfill.js.map +1 -1
- package/out/env.d.ts +2 -2
- package/out/env.js +1 -1
- package/out/exact-match.d.ts +19 -10
- package/out/exact-match.d.ts.map +1 -1
- package/out/exact-match.js +31 -20
- package/out/exact-match.js.map +1 -1
- package/out/extracts.d.ts +65 -54
- package/out/extracts.d.ts.map +1 -1
- package/out/extracts.js +63 -55
- package/out/extracts.js.map +1 -1
- package/out/fst/autocomplete.d.ts +28 -20
- package/out/fst/autocomplete.d.ts.map +1 -1
- package/out/fst/autocomplete.js +38 -27
- package/out/fst/autocomplete.js.map +1 -1
- package/out/fst/builder.d.ts.map +1 -1
- package/out/fst/builder.js +30 -36
- package/out/fst/builder.js.map +1 -1
- package/out/fst/deserialize-web.d.ts +3 -3
- package/out/fst/deserialize-web.d.ts.map +1 -1
- package/out/fst/deserialize-web.js +8 -9
- package/out/fst/deserialize-web.js.map +1 -1
- package/out/fst/format.d.ts +41 -23
- package/out/fst/format.d.ts.map +1 -1
- package/out/fst/format.js +41 -23
- package/out/fst/format.js.map +1 -1
- package/out/fst/freshness.d.ts +11 -73
- package/out/fst/freshness.d.ts.map +1 -1
- package/out/fst/freshness.js +12 -76
- package/out/fst/freshness.js.map +1 -1
- package/out/fst/matcher.d.ts +1 -1
- package/out/fst/matcher.d.ts.map +1 -1
- package/out/fst/matcher.js +2 -2
- package/out/fst/matcher.js.map +1 -1
- package/out/fst/serialize.d.ts +30 -26
- package/out/fst/serialize.d.ts.map +1 -1
- package/out/fst/serialize.js +44 -58
- package/out/fst/serialize.js.map +1 -1
- package/out/fst/types.d.ts +71 -59
- package/out/fst/types.d.ts.map +1 -1
- package/out/fst/types.js +1 -4
- package/out/fst/types.js.map +1 -1
- package/out/{fst/index.js → fst.d.ts} +1 -1
- package/out/fst.d.ts.map +1 -0
- package/out/{fst/index.d.ts → fst.js} +1 -1
- package/out/fst.js.map +1 -0
- package/out/fts/query.d.ts +2 -27
- package/out/fts/query.d.ts.map +1 -1
- package/out/fts/query.js +5 -36
- package/out/fts/query.js.map +1 -1
- package/out/fts.d.ts +126 -0
- package/out/fts.d.ts.map +1 -0
- package/out/fts.js +188 -0
- package/out/fts.js.map +1 -0
- package/out/geonames/aliases.d.ts +26 -67
- package/out/geonames/aliases.d.ts.map +1 -1
- package/out/geonames/aliases.js +44 -121
- package/out/geonames/aliases.js.map +1 -1
- package/out/geonames/postal.d.ts +84 -50
- package/out/geonames/postal.d.ts.map +1 -1
- package/out/geonames/postal.js +115 -78
- package/out/geonames/postal.js.map +1 -1
- package/out/{geonames/index.js → geonames.d.ts} +1 -1
- package/out/geonames.d.ts.map +1 -0
- package/out/{geonames/index.d.ts → geonames.js} +1 -1
- package/out/geonames.js.map +1 -0
- package/out/index.d.ts +20 -21
- package/out/index.d.ts.map +1 -1
- package/out/index.js +14 -15
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts +15 -52
- package/out/interpolation.d.ts.map +1 -1
- package/out/interpolation.js +25 -68
- package/out/interpolation.js.map +1 -1
- package/out/lookup.d.ts +24 -53
- package/out/lookup.d.ts.map +1 -1
- package/out/lookup.js +117 -223
- package/out/lookup.js.map +1 -1
- package/out/name-score.d.ts +2 -2
- package/out/name-score.d.ts.map +1 -1
- package/out/name-score.js +9 -7
- package/out/name-score.js.map +1 -1
- package/out/nsul/lookup.d.ts +33 -34
- package/out/nsul/lookup.d.ts.map +1 -1
- package/out/nsul/lookup.js +26 -40
- package/out/nsul/lookup.js.map +1 -1
- package/out/nsul/schema.d.ts +16 -43
- package/out/nsul/schema.d.ts.map +1 -1
- package/out/nsul/schema.js +9 -35
- package/out/nsul/schema.js.map +1 -1
- package/out/{nsul/index.d.ts → nsul.d.ts} +1 -1
- package/out/nsul.d.ts.map +1 -0
- package/out/{nsul/index.js → nsul.js} +1 -1
- package/out/nsul.js.map +1 -0
- package/out/paths.d.ts +124 -0
- package/out/paths.d.ts.map +1 -0
- package/out/paths.js +104 -0
- package/out/paths.js.map +1 -0
- package/out/place-importance-schema.d.ts +49 -141
- package/out/place-importance-schema.d.ts.map +1 -1
- package/out/place-importance-schema.js +25 -140
- package/out/place-importance-schema.js.map +1 -1
- package/out/poi/lookup.d.ts +38 -42
- package/out/poi/lookup.d.ts.map +1 -1
- package/out/poi/lookup.js +67 -90
- package/out/poi/lookup.js.map +1 -1
- package/out/poi/schema.d.ts +41 -25
- package/out/poi/schema.d.ts.map +1 -1
- package/out/poi/schema.js +25 -17
- package/out/poi/schema.js.map +1 -1
- package/out/{poi/index.js → poi.d.ts} +1 -1
- package/out/poi.d.ts.map +1 -0
- package/out/{poi/index.d.ts → poi.js} +1 -1
- package/out/poi.js.map +1 -0
- package/out/polygon-schema.d.ts +10 -6
- package/out/polygon-schema.d.ts.map +1 -1
- package/out/polygon-schema.js +2 -2
- package/out/polygon-schema.js.map +1 -1
- package/out/postal/{city-alias-lookup.d.ts → city/alias/lookup.d.ts} +22 -25
- package/out/postal/city/alias/lookup.d.ts.map +1 -0
- package/out/postal/city/alias/lookup.js +49 -0
- package/out/postal/city/alias/lookup.js.map +1 -0
- package/out/postal/{city-alias-schema.d.ts → city/alias/schema.d.ts} +15 -11
- package/out/postal/city/alias/schema.d.ts.map +1 -0
- package/out/postal/{city-alias-schema.js → city/alias/schema.js} +11 -9
- package/out/postal/city/alias/schema.js.map +1 -0
- package/out/postal/city/candidate-schema.d.ts +72 -0
- package/out/postal/city/candidate-schema.d.ts.map +1 -0
- package/out/postal/city/candidate-schema.js +60 -0
- package/out/postal/city/candidate-schema.js.map +1 -0
- package/out/postal.d.ts +9 -0
- package/out/postal.d.ts.map +1 -0
- package/out/postal.js +9 -0
- package/out/postal.js.map +1 -0
- package/out/postcode-point-lookup.d.ts +9 -19
- package/out/postcode-point-lookup.d.ts.map +1 -1
- package/out/postcode-point-lookup.js +5 -17
- package/out/postcode-point-lookup.js.map +1 -1
- package/out/primary-preference.d.ts +39 -93
- package/out/primary-preference.d.ts.map +1 -1
- package/out/primary-preference.js +13 -97
- package/out/primary-preference.js.map +1 -1
- package/out/proximity-rerank.d.ts +40 -29
- package/out/proximity-rerank.d.ts.map +1 -1
- package/out/proximity-rerank.js +38 -29
- package/out/proximity-rerank.js.map +1 -1
- package/out/ranking-weights.d.ts +91 -62
- package/out/ranking-weights.d.ts.map +1 -1
- package/out/ranking-weights.js +26 -25
- package/out/ranking-weights.js.map +1 -1
- package/out/region-keys.d.ts +48 -29
- package/out/region-keys.d.ts.map +1 -1
- package/out/region-keys.js +68 -40
- package/out/region-keys.js.map +1 -1
- package/out/reverse.d.ts +75 -50
- package/out/reverse.d.ts.map +1 -1
- package/out/reverse.js +61 -49
- package/out/reverse.js.map +1 -1
- package/out/schema.d.ts +74 -42
- package/out/schema.d.ts.map +1 -1
- package/out/schema.js +4 -5
- package/out/schema.js.map +1 -1
- package/out/search-fetch.d.ts +16 -12
- package/out/search-fetch.d.ts.map +1 -1
- package/out/search-fetch.js +83 -63
- package/out/search-fetch.js.map +1 -1
- package/out/sqlite/convention-source.d.ts +25 -0
- package/out/sqlite/convention-source.d.ts.map +1 -0
- package/out/sqlite/convention-source.js +48 -0
- package/out/sqlite/convention-source.js.map +1 -0
- package/out/sqlite/utils.d.ts +45 -0
- package/out/sqlite/utils.d.ts.map +1 -0
- package/out/sqlite/utils.js +56 -0
- package/out/sqlite/utils.js.map +1 -0
- package/out/street/centroid/schema.d.ts +100 -0
- package/out/street/centroid/schema.d.ts.map +1 -0
- package/out/street/centroid/schema.js +66 -0
- package/out/street/centroid/schema.js.map +1 -0
- package/out/street/centroid.d.ts +9 -19
- package/out/street/centroid.d.ts.map +1 -1
- package/out/street/centroid.js +11 -22
- package/out/street/centroid.js.map +1 -1
- package/out/street/{morphology-fst-builder.d.ts → morphology/fst/builder.d.ts} +21 -11
- package/out/street/morphology/fst/builder.d.ts.map +1 -0
- package/out/street/{morphology-fst-builder.js → morphology/fst/builder.js} +40 -34
- package/out/street/morphology/fst/builder.js.map +1 -0
- package/out/street/morphology/fst/loader.d.ts +74 -0
- package/out/street/morphology/fst/loader.d.ts.map +1 -0
- package/out/street/{morphology-fst-loader.js → morphology/fst/loader.js} +21 -21
- package/out/street/morphology/fst/loader.js.map +1 -0
- package/out/street/name-lookup.d.ts +24 -18
- package/out/street/name-lookup.d.ts.map +1 -1
- package/out/street/name-lookup.js +32 -31
- package/out/street/name-lookup.js.map +1 -1
- package/out/street/normalize.d.ts +35 -136
- package/out/street/normalize.d.ts.map +1 -1
- package/out/street/normalize.js +74 -191
- package/out/street/normalize.js.map +1 -1
- package/out/street/segment-schema.d.ts +79 -39
- package/out/street/segment-schema.d.ts.map +1 -1
- package/out/street/segment-schema.js +71 -21
- package/out/street/segment-schema.js.map +1 -1
- package/out/{street/index.d.ts → street.d.ts} +4 -4
- package/out/street.d.ts.map +1 -0
- package/out/{street/index.js → street.js} +4 -4
- package/out/street.js.map +1 -0
- package/out/types.d.ts +69 -138
- package/out/types.d.ts.map +1 -1
- package/out/types.js +0 -6
- package/out/types.js.map +1 -1
- package/out/unified-schema.d.ts +8 -12
- package/out/unified-schema.d.ts.map +1 -1
- package/out/unified-schema.js +29 -29
- package/out/unified-schema.js.map +1 -1
- package/out/uprn/existence.d.ts +28 -17
- package/out/uprn/existence.d.ts.map +1 -1
- package/out/uprn/existence.js +26 -16
- package/out/uprn/existence.js.map +1 -1
- package/out/uprn/lookup.d.ts +14 -44
- package/out/uprn/lookup.d.ts.map +1 -1
- package/out/uprn/lookup.js +20 -56
- package/out/uprn/lookup.js.map +1 -1
- package/out/uprn/schema.d.ts +18 -40
- package/out/uprn/schema.d.ts.map +1 -1
- package/out/uprn/schema.js +10 -32
- package/out/uprn/schema.js.map +1 -1
- package/out/{uprn/index.d.ts → uprn.d.ts} +1 -1
- package/out/uprn.d.ts.map +1 -0
- package/out/{uprn/index.js → uprn.js} +1 -1
- package/out/uprn.js.map +1 -0
- package/out/weights-overlay-linker.d.ts +132 -141
- package/out/weights-overlay-linker.d.ts.map +1 -1
- package/out/weights-overlay-linker.js +86 -154
- package/out/weights-overlay-linker.js.map +1 -1
- package/package.json +94 -599
- package/lib/address/point-schema.ts +0 -155
- package/lib/build-candidate.ts +0 -632
- package/lib/build-slim.ts +0 -408
- package/lib/candidate-ancestors-schema.ts +0 -195
- package/lib/candidate-fts.ts +0 -46
- package/lib/candidate-importance.ts +0 -224
- package/lib/candidate-lookup.ts +0 -977
- package/lib/candidate-schema.ts +0 -247
- package/lib/candidate-scoring.ts +0 -261
- package/lib/convention/index.ts +0 -154
- package/lib/fts/index.ts +0 -361
- package/lib/postal/city-alias-lookup.ts +0 -97
- package/lib/postal/index.ts +0 -9
- package/lib/sqlite-convention-source.ts +0 -65
- package/lib/sqlite-utils.ts +0 -82
- package/lib/street/centroid-schema.ts +0 -154
- package/out/address/index.d.ts.map +0 -1
- package/out/address/index.js.map +0 -1
- package/out/address/point-interpolation.d.ts +0 -49
- package/out/address/point-interpolation.d.ts.map +0 -1
- package/out/address/point-interpolation.js.map +0 -1
- package/out/address/point-schema.d.ts +0 -97
- package/out/address/point-schema.d.ts.map +0 -1
- package/out/address/point-schema.js.map +0 -1
- package/out/ancestry/index.d.ts +0 -45
- package/out/ancestry/index.d.ts.map +0 -1
- package/out/ancestry/index.js +0 -55
- package/out/ancestry/index.js.map +0 -1
- package/out/build-candidate.d.ts +0 -184
- package/out/build-candidate.d.ts.map +0 -1
- package/out/build-candidate.js.map +0 -1
- package/out/build-slim.d.ts +0 -80
- package/out/build-slim.d.ts.map +0 -1
- package/out/build-slim.js.map +0 -1
- package/out/candidate/ancestors-sidecar.d.ts +0 -32
- package/out/candidate/ancestors-sidecar.d.ts.map +0 -1
- package/out/candidate/ancestors-sidecar.js.map +0 -1
- package/out/candidate-ancestors-schema.d.ts +0 -150
- package/out/candidate-ancestors-schema.d.ts.map +0 -1
- package/out/candidate-ancestors-schema.js +0 -123
- package/out/candidate-ancestors-schema.js.map +0 -1
- package/out/candidate-fts.d.ts +0 -37
- package/out/candidate-fts.d.ts.map +0 -1
- package/out/candidate-fts.js +0 -40
- package/out/candidate-fts.js.map +0 -1
- package/out/candidate-importance.d.ts +0 -132
- package/out/candidate-importance.d.ts.map +0 -1
- package/out/candidate-importance.js +0 -169
- package/out/candidate-importance.js.map +0 -1
- package/out/candidate-lookup.d.ts +0 -76
- package/out/candidate-lookup.d.ts.map +0 -1
- package/out/candidate-lookup.js +0 -768
- package/out/candidate-lookup.js.map +0 -1
- package/out/candidate-schema.d.ts +0 -155
- package/out/candidate-schema.d.ts.map +0 -1
- package/out/candidate-schema.js.map +0 -1
- package/out/candidate-scoring.d.ts.map +0 -1
- package/out/candidate-scoring.js +0 -196
- package/out/candidate-scoring.js.map +0 -1
- package/out/convention/index.d.ts +0 -106
- package/out/convention/index.d.ts.map +0 -1
- package/out/convention/index.js +0 -93
- package/out/convention/index.js.map +0 -1
- package/out/fst/index.d.ts.map +0 -1
- package/out/fst/index.js.map +0 -1
- package/out/fts/index.d.ts +0 -184
- package/out/fts/index.d.ts.map +0 -1
- package/out/fts/index.js +0 -277
- package/out/fts/index.js.map +0 -1
- package/out/geonames/index.d.ts.map +0 -1
- package/out/geonames/index.js.map +0 -1
- package/out/nsul/index.d.ts.map +0 -1
- package/out/nsul/index.js.map +0 -1
- package/out/poi/index.d.ts.map +0 -1
- package/out/poi/index.js.map +0 -1
- package/out/postal/city-alias-lookup.d.ts.map +0 -1
- package/out/postal/city-alias-lookup.js +0 -62
- package/out/postal/city-alias-lookup.js.map +0 -1
- package/out/postal/city-alias-schema.d.ts.map +0 -1
- package/out/postal/city-alias-schema.js.map +0 -1
- package/out/postal/city-candidate-schema.d.ts +0 -66
- package/out/postal/city-candidate-schema.d.ts.map +0 -1
- package/out/postal/city-candidate-schema.js +0 -56
- package/out/postal/city-candidate-schema.js.map +0 -1
- package/out/postal/index.d.ts +0 -9
- package/out/postal/index.d.ts.map +0 -1
- package/out/postal/index.js +0 -9
- package/out/postal/index.js.map +0 -1
- package/out/sqlite-convention-source.d.ts +0 -29
- package/out/sqlite-convention-source.d.ts.map +0 -1
- package/out/sqlite-convention-source.js +0 -57
- package/out/sqlite-convention-source.js.map +0 -1
- package/out/sqlite-utils.d.ts +0 -45
- package/out/sqlite-utils.d.ts.map +0 -1
- package/out/sqlite-utils.js +0 -57
- package/out/sqlite-utils.js.map +0 -1
- package/out/street/centroid-schema.d.ts +0 -105
- package/out/street/centroid-schema.d.ts.map +0 -1
- package/out/street/centroid-schema.js +0 -78
- package/out/street/centroid-schema.js.map +0 -1
- package/out/street/index.d.ts.map +0 -1
- package/out/street/index.js.map +0 -1
- package/out/street/morphology-fst-builder.d.ts.map +0 -1
- package/out/street/morphology-fst-builder.js.map +0 -1
- package/out/street/morphology-fst-loader.d.ts +0 -67
- package/out/street/morphology-fst-loader.d.ts.map +0 -1
- package/out/street/morphology-fst-loader.js.map +0 -1
- package/out/uprn/index.d.ts.map +0 -1
- package/out/uprn/index.js.map +0 -1
- /package/lib/{fst/index.ts → fst.ts} +0 -0
- /package/lib/{geonames/index.ts → geonames.ts} +0 -0
- /package/lib/{nsul/index.ts → nsul.ts} +0 -0
- /package/lib/{poi/index.ts → poi.ts} +0 -0
- /package/lib/{uprn/index.ts → uprn.ts} +0 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { compareReferential, referentialFromPopulation } from "@mailwoman/core/resolver"
|
|
8
|
+
import { haversineKm } from "@mailwoman/spatial"
|
|
9
|
+
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
10
|
+
|
|
11
|
+
import { exactMatchIDs, officialNameIDs } from "#exact-match"
|
|
12
|
+
import { foldQueryText } from "#fts"
|
|
13
|
+
import { populationBoostTerm, type RankingWeights } from "#ranking-weights"
|
|
14
|
+
import type { RawSearchRow } from "#search-fetch"
|
|
15
|
+
import type { FindPlaceQuery, PlaceCandidate, WOFPlacetype } from "#types"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Score one raw FTS row into a `PlaceCandidate`: the weighted sum over the negated
|
|
19
|
+
* BM25 baseline plus placetype, country and parent boosts.
|
|
20
|
+
*
|
|
21
|
+
* It also applies the length penalty, proximity and population terms,
|
|
22
|
+
* then returns the fields consumers read.
|
|
23
|
+
*/
|
|
24
|
+
export function candidateFromSearchRow(
|
|
25
|
+
row: RawSearchRow,
|
|
26
|
+
context: {
|
|
27
|
+
query: FindPlaceQuery
|
|
28
|
+
placetypes: WOFPlacetype[] | null
|
|
29
|
+
queryLen: number
|
|
30
|
+
weights: RankingWeights
|
|
31
|
+
}
|
|
32
|
+
): PlaceCandidate {
|
|
33
|
+
const { query, placetypes, queryLen, weights } = context
|
|
34
|
+
|
|
35
|
+
// SQLite's bm25() is lower-is-better, so negate it to get a higher-is-better baseline.
|
|
36
|
+
let score = -row.rank
|
|
37
|
+
|
|
38
|
+
if (placetypes && placetypes.length && placetypes.includes(row.placetype as WOFPlacetype)) {
|
|
39
|
+
score += weights.placetypeMatchBoost
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!placetypes && row.placetype === "locality") {
|
|
43
|
+
score += weights.localityImplicitBoost
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (query.country && row.country === query.country) {
|
|
47
|
+
score += weights.countryMatchBoost
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (query.parentID !== undefined) {
|
|
51
|
+
score += row.parent_id === query.parentID ? weights.directChildBoost : weights.descendantBoost
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const extraLen = Math.max(0, row.name.length - queryLen - 3)
|
|
55
|
+
score -= (weights.lengthPenaltyWeight * extraLen) / 10
|
|
56
|
+
|
|
57
|
+
// Proximity boost applies only when the query includes `near` and the candidate has real coordinates.
|
|
58
|
+
// The decay is tunable via proximityBoost + proximityScaleKm.
|
|
59
|
+
let distanceKm: number | undefined
|
|
60
|
+
// The best decayed-distance term over `near` and every `bias` point wins, each scaled by its weight.
|
|
61
|
+
let proximityTerm = 0
|
|
62
|
+
|
|
63
|
+
if (row.lat !== null && row.lon !== null && !(row.lat === 0 && row.lon === 0)) {
|
|
64
|
+
const hints: Array<{ lat: number; lon: number; weight: number }> = []
|
|
65
|
+
|
|
66
|
+
if (query.near) {
|
|
67
|
+
hints.push({ lat: query.near.lat, lon: query.near.lon, weight: 1 })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const b of query.bias ?? []) {
|
|
71
|
+
hints.push({ lat: b.lat, lon: b.lon, weight: b.weight ?? 1 })
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let scoreTerm = 0
|
|
75
|
+
|
|
76
|
+
for (const h of hints) {
|
|
77
|
+
const d = haversineKm(h.lat, h.lon, row.lat, row.lon)
|
|
78
|
+
const decay = h.weight / (1 + d / weights.proximityScaleKm)
|
|
79
|
+
const prom = decay * weights.biasBoost
|
|
80
|
+
|
|
81
|
+
if (prom > proximityTerm) {
|
|
82
|
+
proximityTerm = prom
|
|
83
|
+
distanceKm = d
|
|
84
|
+
scoreTerm = decay * weights.proximityBoost
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
score += scoreTerm
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Population boost caps at `populationBoost` magnitude at `10^populationScaleLog10` people and never penalizes.
|
|
92
|
+
const popTerm = populationBoostTerm(row.population, weights)
|
|
93
|
+
score += popTerm
|
|
94
|
+
|
|
95
|
+
// Combined prominence for the exact-tier sort: population and nearness in the same
|
|
96
|
+
// additive units, so hints can win a cross-country postcode tie without a hard filter.
|
|
97
|
+
const prominence = popTerm + proximityTerm
|
|
98
|
+
|
|
99
|
+
const candidate: PlaceCandidate = {
|
|
100
|
+
id: row.id,
|
|
101
|
+
prominence,
|
|
102
|
+
name: row.name,
|
|
103
|
+
placetype: row.placetype as WOFPlacetype,
|
|
104
|
+
country: row.country ?? "",
|
|
105
|
+
lat: row.lat ?? 0,
|
|
106
|
+
lon: row.lon ?? 0,
|
|
107
|
+
parent_id: row.parent_id ?? undefined,
|
|
108
|
+
score,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (distanceKm !== undefined) {
|
|
112
|
+
candidate.distanceKm = distanceKm
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (row.population !== null && row.population > 0) {
|
|
116
|
+
candidate.population = row.population
|
|
117
|
+
// The referential ranking key is derived as a pure function of this row's population,
|
|
118
|
+
// so it cannot drift from the ordering.
|
|
119
|
+
candidate.referential = referentialFromPopulation(row.population)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Returned for consumers (annotations / API surfaces); no ranking site reads it.
|
|
123
|
+
if (row.encyclopedic !== null) {
|
|
124
|
+
candidate.encyclopedic = row.encyclopedic
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Candidate bbox for parity with the wasm lookup. Without it the Node backend's
|
|
128
|
+
// region→bbox constraint is dead and disambiguation falls to population ranking.
|
|
129
|
+
if (row.min_latitude != null && row.max_latitude != null && row.min_longitude != null && row.max_longitude != null) {
|
|
130
|
+
candidate.bbox = {
|
|
131
|
+
minLat: row.min_latitude,
|
|
132
|
+
maxLat: row.max_latitude,
|
|
133
|
+
minLon: row.min_longitude,
|
|
134
|
+
maxLon: row.max_longitude,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return candidate
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Order `candidates` in place with the exact-match tier first when the extract can answer name probes.
|
|
143
|
+
* Stamp every candidate's `exactMatch` flag.
|
|
144
|
+
*/
|
|
145
|
+
export function rankCandidates<DB>(
|
|
146
|
+
candidates: PlaceCandidate[],
|
|
147
|
+
options: {
|
|
148
|
+
db: DatabaseClient<DB>
|
|
149
|
+
schemaName: string
|
|
150
|
+
query: FindPlaceQuery
|
|
151
|
+
weights: RankingWeights
|
|
152
|
+
}
|
|
153
|
+
): void {
|
|
154
|
+
const { db, schemaName, query, weights } = options
|
|
155
|
+
|
|
156
|
+
// Exact-match tiering ranks a case-folded full match above a partial one.
|
|
157
|
+
// It also runs for a single candidate so `exactMatch` is stamped consistently.
|
|
158
|
+
if (weights.exactMatchTiering && candidates.length) {
|
|
159
|
+
const exactIDs = exactMatchIDs(
|
|
160
|
+
db,
|
|
161
|
+
schemaName,
|
|
162
|
+
candidates.map((c) => c.id as number),
|
|
163
|
+
query.text
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
// Stamp the tier onto every candidate so a downstream re-rank's country pin
|
|
167
|
+
// cannot cross the exact/partial boundary.
|
|
168
|
+
for (const c of candidates) {
|
|
169
|
+
c.exactMatch = exactIDs.has(c.id as number)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (exactIDs.size) {
|
|
173
|
+
// Within the exact tier, population is the primary key and the score only breaks
|
|
174
|
+
// ties. a name-exact candidate outranks an alias-exact one.
|
|
175
|
+
const needle = foldQueryText(query.text)
|
|
176
|
+
|
|
177
|
+
// An official name counts as the place's own name for the sub-tier, floor-conditioned on the holder's population.
|
|
178
|
+
const officialIDs = weights.officialNameExact
|
|
179
|
+
? officialNameIDs(
|
|
180
|
+
db,
|
|
181
|
+
schemaName,
|
|
182
|
+
candidates
|
|
183
|
+
.filter((c) => exactIDs.has(c.id as number) && (c.population ?? 0) >= weights.officialNameExactFloor)
|
|
184
|
+
.map((c) => c.id as number),
|
|
185
|
+
query.text
|
|
186
|
+
)
|
|
187
|
+
: undefined
|
|
188
|
+
|
|
189
|
+
const kind = (c: PlaceCandidate): number => {
|
|
190
|
+
if (!exactIDs.has(c.id as number)) return 0
|
|
191
|
+
|
|
192
|
+
if (foldQueryText(String(c.name ?? "")) === needle) return 2
|
|
193
|
+
|
|
194
|
+
return officialIDs?.has(c.id as number) ? 2 : 1
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// With proximity hints, prominence replaces raw population as the within-tier key.
|
|
198
|
+
// Without them, referential ordering decides.
|
|
199
|
+
// Encyclopedic importance does not affect this order.
|
|
200
|
+
const hasHints = !!query.near || (query.bias?.length ?? 0) > 0
|
|
201
|
+
|
|
202
|
+
candidates.sort((a, b) => {
|
|
203
|
+
const ax = kind(a)
|
|
204
|
+
const bx = kind(b)
|
|
205
|
+
|
|
206
|
+
if (bx !== ax) return bx - ax
|
|
207
|
+
|
|
208
|
+
if (ax >= 1) {
|
|
209
|
+
if (hasHints) return (b.prominence ?? 0) - (a.prominence ?? 0) || b.score - a.score
|
|
210
|
+
|
|
211
|
+
return compareReferential(a, b) || b.score - a.score
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return b.score - a.score
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
candidates.sort((a, b) => b.score - a.score)
|
|
222
|
+
}
|
package/lib/capital-schema.ts
CHANGED
|
@@ -3,15 +3,14 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* The `capital` table
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* per-probe query and no index beyond the rowid.
|
|
6
|
+
* The `capital` table: the capital-status reference included inside `candidate.db`, so an npm
|
|
7
|
+
* consumer who pulled the artifact can run `capital_tier` without the repo's
|
|
8
|
+
* `data/gazetteer/capitals-v1.json`, which published packages do not ship. One row per reference
|
|
9
|
+
* entry. The loader reads the whole table once into a `CapitalIndex` at session construction, so
|
|
10
|
+
* there is no per-probe query and no index beyond the rowid.
|
|
12
11
|
*
|
|
13
|
-
* `keys` holds the entry's folded name set as a JSON array
|
|
14
|
-
*
|
|
12
|
+
* `keys` holds the entry's folded name set as a JSON array, the name-membership conjunct that keeps
|
|
13
|
+
* the coordinate radius from promoting a capital's same-name neighbors (`capitals.ts`).
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
import { tryParsingJSON } from "@mailwoman/core/json"
|
|
@@ -22,14 +21,16 @@ import type { CapitalPoint } from "#capitals"
|
|
|
22
21
|
import { hasTable } from "#sqlite-utils"
|
|
23
22
|
|
|
24
23
|
/**
|
|
25
|
-
* The table name the builder writes and the reader probes
|
|
26
|
-
* tables (`candidate`, `country_codes`).
|
|
24
|
+
* The table name the builder writes and the reader probes, one word and singular,
|
|
25
|
+
* matching the artifact's other reference tables (`candidate`, `country_codes`).
|
|
27
26
|
*/
|
|
28
27
|
export const CAPITAL_TABLE = "capital"
|
|
29
28
|
|
|
30
29
|
/**
|
|
31
|
-
* One reference entry as the artifact stores it.
|
|
32
|
-
*
|
|
30
|
+
* One reference entry as the artifact stores it.
|
|
31
|
+
*
|
|
32
|
+
* `level` is the reference vocabulary (`national` | `admin1`) kept as text.
|
|
33
|
+
* and the reader validates on load rather than trusting bytes.
|
|
33
34
|
*/
|
|
34
35
|
export interface CapitalTable {
|
|
35
36
|
country: string
|
|
@@ -43,8 +44,10 @@ export interface CapitalTable {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
/**
|
|
46
|
-
* Create the table on a build in progress.
|
|
47
|
-
*
|
|
47
|
+
* Create the table on a build in progress.
|
|
48
|
+
*
|
|
49
|
+
* Async because Kysely's schema-builder is.
|
|
50
|
+
* Called from the candidate build's DDL phase alongside the other typed builders.
|
|
48
51
|
*/
|
|
49
52
|
export async function createCapitalTable<DB extends { capital: CapitalTable }>(db: Kysely<DB>): Promise<void> {
|
|
50
53
|
await db.schema
|
|
@@ -58,9 +61,10 @@ export async function createCapitalTable<DB extends { capital: CapitalTable }>(d
|
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
/**
|
|
61
|
-
* Read the whole reference out of an artifact, or `null` when the artifact predates the table
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
+
* Read the whole reference out of an artifact, or `null` when the artifact predates the table.
|
|
65
|
+
*
|
|
66
|
+
* The caller then falls back to its next source rather than treating an old artifact as "no
|
|
67
|
+
* capitals" (the meaning-of-zero rule: a missing table is unmeasured, an empty one is a finding).
|
|
64
68
|
*/
|
|
65
69
|
export function readCapitalPoints<DB>(db: DatabaseClient<DB>): CapitalPoint[] | null {
|
|
66
70
|
if (!hasTable(db, CAPITAL_TABLE)) return null
|
package/lib/capitals.ts
CHANGED
|
@@ -3,19 +3,15 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* The
|
|
7
|
-
*
|
|
6
|
+
* The capital-status reference index answers whether a resolved candidate is its country's national capital or admin-1 seat.
|
|
7
|
+
* The consumer is the resolver's
|
|
8
8
|
* bounded capital promotion (`@mailwoman/resolver`'s `promoteCapitals`, applied after the fame
|
|
9
|
-
* key on the bare-toponym class)
|
|
10
|
-
* platform-free
|
|
9
|
+
* key on the bare-toponym class). This module only matches and never ranks. It is pure and
|
|
10
|
+
* platform-free.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* names, so exonym rows — "Vienna" for Wien — still match). All three are required. The
|
|
16
|
-
* iteration-1 board run matched on country + coordinate alone, and the 25 km radius promoted
|
|
17
|
-
* capital-ADJACENT namesakes instead of capitals: North Salt Lake beside the Utah seat, a Gujarat
|
|
18
|
-
* Indiranagar beside Gandhinagar, Via delle Parti beside Perugia. The name set is what makes the
|
|
12
|
+
* A match requires the same country and a distance within {@link CAPITAL_MATCH_RADIUS_KM} of the reference point.
|
|
13
|
+
* The candidate's folded name must also appear in the reference entry's folded name set (name + romanization + the source's alternate
|
|
14
|
+
* names, so exonym rows such as "Vienna" for Wien still match). The name set is what makes the
|
|
19
15
|
* radius a centroid-drift allowance rather than a catchment.
|
|
20
16
|
*/
|
|
21
17
|
|
|
@@ -24,8 +20,9 @@ import { haversineKm } from "@mailwoman/spatial"
|
|
|
24
20
|
import { normalizeLocalityForKey } from "#street/normalize"
|
|
25
21
|
|
|
26
22
|
/**
|
|
27
|
-
* Capital status of one candidate: national capital, admin-1 seat, or neither.
|
|
28
|
-
*
|
|
23
|
+
* Capital status of one candidate: national capital, admin-1 seat, or neither.
|
|
24
|
+
*
|
|
25
|
+
* Numeric so the resolver's promotion can compare levels.
|
|
29
26
|
*/
|
|
30
27
|
export const CAPITAL_LEVEL = {
|
|
31
28
|
none: 0,
|
|
@@ -36,14 +33,15 @@ export const CAPITAL_LEVEL = {
|
|
|
36
33
|
export type CapitalLevel = (typeof CAPITAL_LEVEL)[keyof typeof CAPITAL_LEVEL]
|
|
37
34
|
|
|
38
35
|
/**
|
|
39
|
-
* How far a candidate row may sit from the reference point and still read as the same place
|
|
40
|
-
*
|
|
41
|
-
*
|
|
36
|
+
* How far a candidate row may sit from the reference point and still read as the same place.
|
|
37
|
+
*
|
|
38
|
+
* A centroid-convention allowance for a GeoNames point against a WOF centroid on a metro-scale city.
|
|
39
|
+
* The name-membership conjunct excludes neighbors inside the radius.
|
|
42
40
|
*/
|
|
43
41
|
export const CAPITAL_MATCH_RADIUS_KM = 25
|
|
44
42
|
|
|
45
43
|
/**
|
|
46
|
-
* One reference entry, as `data/gazetteer/capitals-v1.json`
|
|
44
|
+
* One reference entry, as recorded in `data/gazetteer/capitals-v1.json` (`entries[]`).
|
|
47
45
|
*/
|
|
48
46
|
export interface CapitalPoint {
|
|
49
47
|
/**
|
|
@@ -54,8 +52,8 @@ export interface CapitalPoint {
|
|
|
54
52
|
longitude: number
|
|
55
53
|
level: "national" | "admin1"
|
|
56
54
|
/**
|
|
57
|
-
* Folded name keys (name + romanization + alternate names, `normalizeLocalityForKey` fold)
|
|
58
|
-
* the name conjunct.
|
|
55
|
+
* Folded name keys (name + romanization + alternate names, `normalizeLocalityForKey` fold).
|
|
56
|
+
* The membership set for the name conjunct.
|
|
59
57
|
*/
|
|
60
58
|
k: string[]
|
|
61
59
|
}
|
|
@@ -73,9 +71,11 @@ interface IndexedPoint {
|
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
/**
|
|
76
|
-
* Country-bucketed capital points with the three-conjunct identity probe.
|
|
77
|
-
*
|
|
78
|
-
*
|
|
74
|
+
* Country-bucketed capital points with the three-conjunct identity probe.
|
|
75
|
+
*
|
|
76
|
+
* Construct from the parsed reference file's `entries`.
|
|
77
|
+
* The loader that reads the file off disk lives with the path owners
|
|
78
|
+
* (`mailwoman`'s resolver backend), keeping this module platform-free.
|
|
79
79
|
*/
|
|
80
80
|
export class CapitalIndex {
|
|
81
81
|
readonly #byCountry = new Map<string, IndexedPoint[]>()
|
|
@@ -102,8 +102,10 @@ export class CapitalIndex {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* The highest capital level whose entry passes all three conjuncts for this place.
|
|
106
|
-
*
|
|
105
|
+
* The highest capital level whose entry passes all three conjuncts for this place.
|
|
106
|
+
*
|
|
107
|
+
* `none`, never a throw, for a missing name, country, or coordinate,
|
|
108
|
+
* an unknown country, or no matching entry.
|
|
107
109
|
*/
|
|
108
110
|
levelOfPlace(
|
|
109
111
|
name: string | null | undefined,
|
package/lib/coincident-roles.ts
CHANGED
|
@@ -3,39 +3,37 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* `buildCoincidentRoles`
|
|
7
|
-
* unified gazetteer.
|
|
6
|
+
* `buildCoincidentRoles` derives the coincident-roles relation into the unified gazetteer.
|
|
8
7
|
*
|
|
9
|
-
* Many places occupy
|
|
10
|
-
*
|
|
11
|
-
* provinces-after-capitals, UK unitary authorities, JP prefectures, NL province-capitals
|
|
12
|
-
*
|
|
13
|
-
* the locality span
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* distance math.
|
|
8
|
+
* Many places occupy multiple admin tiers under one name: German city-states (Berlin, Hamburg,
|
|
9
|
+
* Bremen), Italian provinces whose names match their capital (Milano, Varese), Spanish
|
|
10
|
+
* provinces-after-capitals, UK unitary authorities, JP prefectures, NL province-capitals (Utrecht,
|
|
11
|
+
* Groningen), and Shanghai. When an address surfaces only the admin role because the parser
|
|
12
|
+
* dropped the locality span, the resolver has no locality to place. The hierarchy-completion step
|
|
13
|
+
* consults this relation. The table preserves the gazetteer's own structure, so the runtime is an
|
|
14
|
+
* O(1) membership lookup with no distance math.
|
|
17
15
|
*
|
|
18
|
-
* V1 is
|
|
19
|
-
* countries
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* filter.
|
|
16
|
+
* V1 is region-tier only (admin.placetype = `region`), the ~124 places matching the census across
|
|
17
|
+
* nine countries. County-tier same-name coincidences are excluded because they are dominated by
|
|
18
|
+
* French cantons and JP counties, admin subdivisions whose names match a seat town that do not hit the
|
|
19
|
+
* parser-drops-locality failure. Consolidated city-counties (US SF, Denver) are a separate
|
|
20
|
+
* follow-up needing a relative-size filter.
|
|
24
21
|
*
|
|
25
|
-
* A pair `(admin, locality)` is recorded when
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
22
|
+
* A pair `(admin, locality)` is recorded when the names match case-insensitively, the locality descends from the admin in `ancestors`,
|
|
23
|
+
* and their centroids fall within a
|
|
24
|
+
* relative tolerance of `toleranceFraction × admin-bbox-diagonal`, floored at `minToleranceKm`.
|
|
25
|
+
* The relative term lets a large Italian province admit a city tens of km from its centroid while
|
|
26
|
+
* a tiny city-state stays tight. The floor catches city-states whose bbox is small (Bremen's
|
|
27
|
+
* centroids sit 9.3 km apart). The tolerance lives only here at build time and never enters the
|
|
28
|
+
* resolver hot path.
|
|
32
29
|
*
|
|
33
|
-
* `relationship_type` is recorded for debuggability
|
|
34
|
-
* uniform
|
|
30
|
+
* `relationship_type` is recorded for debuggability and deferred per-type behavior. V1 completion
|
|
31
|
+
* is uniform. It is a coarse classification.
|
|
35
32
|
*
|
|
36
|
-
*
|
|
37
|
-
* against an existing `admin-global-priority.db` via
|
|
38
|
-
* be wired as a post-step of the main
|
|
33
|
+
* This mirrors the derived-table builder pattern in `fts.ts` (`buildPlaceSearchFTS`). It runs
|
|
34
|
+
* incrementally against an existing `admin-global-priority.db` via
|
|
35
|
+
* `build-coincident-roles-cli.ts`, and it should also be wired as a post-step of the main
|
|
36
|
+
* `scripts/build-unified-wof.ts`.
|
|
39
37
|
*/
|
|
40
38
|
|
|
41
39
|
import type { CoincidentLocality } from "@mailwoman/core/resolver"
|
|
@@ -46,13 +44,15 @@ import { tableExists } from "@mailwoman/sqlite/introspection"
|
|
|
46
44
|
|
|
47
45
|
import type { WOFDatabase } from "#schema"
|
|
48
46
|
/**
|
|
49
|
-
* Table of places that hold more than one admin role
|
|
50
|
-
*
|
|
47
|
+
* Table of places that hold more than one admin role.
|
|
48
|
+
* A locality that is also its county seat.
|
|
49
|
+
*
|
|
50
|
+
* Written by the gazetteer build, read by the resolver when a coincident locality has to be chosen.
|
|
51
51
|
*/
|
|
52
52
|
export const COINCIDENT_ROLES_TABLE = "coincident_roles"
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
* A place that plays multiple admin roles
|
|
55
|
+
* A place that plays multiple admin roles, one row of the relation keyed by `admin_id`.
|
|
56
56
|
*/
|
|
57
57
|
export interface CoincidentRole {
|
|
58
58
|
localityID: number
|
|
@@ -64,19 +64,27 @@ export interface CoincidentRole {
|
|
|
64
64
|
|
|
65
65
|
export interface BuildCoincidentRolesOpts {
|
|
66
66
|
/**
|
|
67
|
-
* Drop + rebuild the table if it already exists.
|
|
67
|
+
* Drop + rebuild the table if it already exists.
|
|
68
|
+
*
|
|
69
|
+
* Default true (the build is cheap + idempotent).
|
|
68
70
|
*/
|
|
69
71
|
drop?: boolean
|
|
70
72
|
/**
|
|
71
|
-
* Relative tolerance: a pair is kept when centroid distance ≤ `toleranceFraction × bbox-diagonal`.
|
|
73
|
+
* Relative tolerance: a pair is kept when centroid distance ≤ `toleranceFraction × bbox-diagonal`.
|
|
74
|
+
*
|
|
75
|
+
* Default 0.15.
|
|
72
76
|
*/
|
|
73
77
|
toleranceFraction?: number
|
|
74
78
|
/**
|
|
75
|
-
* Floor (km) under the relative tolerance, so small-bbox city-states still qualify.
|
|
79
|
+
* Floor (km) under the relative tolerance, so small-bbox city-states still qualify.
|
|
80
|
+
*
|
|
81
|
+
* Default 12.
|
|
76
82
|
*/
|
|
77
83
|
minToleranceKm?: number
|
|
78
84
|
/**
|
|
79
|
-
* Centroid distance (km) below which a region-tier pair is classed `city-state` (metadata only).
|
|
85
|
+
* Centroid distance (km) below which a region-tier pair is classed `city-state` (metadata only).
|
|
86
|
+
*
|
|
87
|
+
* Default 2.
|
|
80
88
|
*/
|
|
81
89
|
cityStateMaxKm?: number
|
|
82
90
|
onProgress?: (phase: string, detail?: string) => void
|
|
@@ -106,8 +114,11 @@ interface CandidateRow {
|
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
/**
|
|
109
|
-
* Derive the coincident-roles relation into `db`.
|
|
110
|
-
*
|
|
117
|
+
* Derive the coincident-roles relation into `db`.
|
|
118
|
+
*
|
|
119
|
+
* Additive, only creating or replacing the `coincident_roles` table.
|
|
120
|
+
* It never touches `spr`, `names`, or `ancestors`.
|
|
121
|
+
* Idempotent.
|
|
111
122
|
*/
|
|
112
123
|
export function buildCoincidentRoles(
|
|
113
124
|
db: DatabaseClient<WOFDatabase>,
|
|
@@ -127,10 +138,9 @@ export function buildCoincidentRoles(
|
|
|
127
138
|
|
|
128
139
|
onProgress("creating", COINCIDENT_ROLES_TABLE)
|
|
129
140
|
|
|
130
|
-
// Raw DDL by design: this is a sync builder consumed by a sync CLI
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
// likewise the raw hot path.)
|
|
141
|
+
// Raw DDL by design: this is a sync builder consumed by a sync CLI and six sync unit tests,
|
|
142
|
+
// so routing one table through async Kysely would cascade async through all of them.
|
|
143
|
+
// See agents.md "Database / inline SQL".
|
|
134
144
|
db.exec(`
|
|
135
145
|
CREATE TABLE IF NOT EXISTS ${COINCIDENT_ROLES_TABLE} (
|
|
136
146
|
admin_id INTEGER NOT NULL,
|
|
@@ -145,9 +155,11 @@ export function buildCoincidentRoles(
|
|
|
145
155
|
|
|
146
156
|
onProgress("scanning")
|
|
147
157
|
|
|
148
|
-
// Admin (region
|
|
149
|
-
//
|
|
150
|
-
//
|
|
158
|
+
// Admin (region or county tier) joined to a same-name descendant locality.
|
|
159
|
+
// `place_population` is optional, so the left join yields 0 when absent.
|
|
160
|
+
// JavaScript applies the relative-tolerance filter and classifies the relationship.
|
|
161
|
+
// SQL stays a plain join.
|
|
162
|
+
// `spr` exposes the bbox columns needed for the diagonal.
|
|
151
163
|
const candidates = allRows<CandidateRow>(
|
|
152
164
|
db.prepare(
|
|
153
165
|
`SELECT r.id AS admin_id, r.placetype AS admin_placetype, r.country AS country, l.id AS locality_id,
|
|
@@ -183,11 +195,12 @@ export function buildCoincidentRoles(
|
|
|
183
195
|
const tolerance = Math.max(toleranceFraction * diag, minToleranceKm)
|
|
184
196
|
|
|
185
197
|
if (dist > tolerance) continue
|
|
186
|
-
// v1 is region-tier only: a place is
|
|
187
|
-
// region's (Berlin
|
|
188
|
-
// Milano province
|
|
189
|
-
// pass (US SF
|
|
190
|
-
// dominated by French cantons
|
|
198
|
+
// v1 is region-tier only: a place is `city-state` when its centroid coincides with the
|
|
199
|
+
// region's (Berlin, Hamburg), otherwise `capital-seat` (a region whose name matches its
|
|
200
|
+
// principal city, such as Milano province and Milano comune). `consolidated-county` is
|
|
201
|
+
// reserved for a future county-tier pass (US SF, Denver), excluded from v1 because
|
|
202
|
+
// county-tier same-name coincidences are dominated by French cantons and JP counties
|
|
203
|
+
// that do not hit the parser-drops-locality failure.
|
|
191
204
|
const relationshipType = dist <= cityStateMaxKm ? "city-state" : "capital-seat"
|
|
192
205
|
insert.run(c.admin_id, c.locality_id, relationshipType, c.admin_placetype, dist, c.pop)
|
|
193
206
|
|
|
@@ -209,16 +222,20 @@ export function buildCoincidentRoles(
|
|
|
209
222
|
}
|
|
210
223
|
|
|
211
224
|
/**
|
|
212
|
-
* True iff the relation table exists.
|
|
225
|
+
* True iff the relation table exists.
|
|
226
|
+
*
|
|
227
|
+
* Used by the resolver to decide whether completion can run.
|
|
213
228
|
*/
|
|
214
229
|
export function coincidentRolesExists<DB>(db: DatabaseClient<DB>): boolean {
|
|
215
230
|
return tableExists(db, COINCIDENT_ROLES_TABLE)
|
|
216
231
|
}
|
|
217
232
|
|
|
218
233
|
/**
|
|
219
|
-
* Load the relation into an in-memory map keyed by `admin_id` for O(1) runtime lookup
|
|
220
|
-
*
|
|
221
|
-
* map
|
|
234
|
+
* Load the relation into an in-memory map keyed by `admin_id` for an O(1) runtime lookup.
|
|
235
|
+
*
|
|
236
|
+
* Each admin may map to multiple same-name descendants.
|
|
237
|
+
* The consumer disambiguates by minimum distance, then population, then abstains.
|
|
238
|
+
* Returns an empty map when the table is absent.
|
|
222
239
|
*/
|
|
223
240
|
export function loadCoincidentRoles<DB>(db: DatabaseClient<DB>): Map<number, CoincidentRole[]> {
|
|
224
241
|
const map = new Map<number, CoincidentRole[]>()
|
|
@@ -261,8 +278,10 @@ export function loadCoincidentRoles<DB>(db: DatabaseClient<DB>): Map<number, Coi
|
|
|
261
278
|
}
|
|
262
279
|
|
|
263
280
|
/**
|
|
264
|
-
* The relation joined with `spr`, as an in-memory map keyed by `admin_id
|
|
265
|
-
*
|
|
281
|
+
* The relation joined with `spr`, as an in-memory map keyed by `admin_id`.
|
|
282
|
+
*
|
|
283
|
+
* The resolver-facing view of {@link loadCoincidentRoles}, carrying the canonical name
|
|
284
|
+
* and coordinates each coincident locality resolves to.
|
|
266
285
|
* Returns an empty map when the relation table is absent.
|
|
267
286
|
*/
|
|
268
287
|
export function loadCoincidentLocalities<DB>(db: DatabaseClient<DB>): Map<number, CoincidentLocality[]> {
|
package/lib/convention/schema.ts
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
6
|
* Typed schema for the convention asset — the `address_convention` table `SqliteConventionSource` probes, plus the
|
|
7
|
-
* `meta` provenance row every sealed artifact here
|
|
7
|
+
* `meta` provenance row every sealed artifact here includes. The interface is the read/write interface and
|
|
8
8
|
* {@link createAddressConventionTable} creates the table, so a column added to one is a compile error against the
|
|
9
9
|
* other.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type { Kysely } from "kysely"
|
|
13
13
|
|
|
14
|
-
import { ADDRESS_CONVENTION_TABLE } from "#convention
|
|
14
|
+
import { ADDRESS_CONVENTION_TABLE } from "#convention"
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* One convention profile, keyed by the WOF admin polygon it attaches to.
|
|
@@ -19,7 +19,9 @@ import { ADDRESS_CONVENTION_TABLE } from "#convention/index"
|
|
|
19
19
|
export interface AddressConventionTable {
|
|
20
20
|
wof_id: number
|
|
21
21
|
/**
|
|
22
|
-
* The `Convention` record, JSON-encoded.
|
|
22
|
+
* The `Convention` record, JSON-encoded.
|
|
23
|
+
*
|
|
24
|
+
* Parsed at read time by `SqliteConventionSource`.
|
|
23
25
|
*/
|
|
24
26
|
convention: string
|
|
25
27
|
/**
|
|
@@ -42,14 +44,18 @@ export interface ConventionDatabase {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
/**
|
|
45
|
-
* The
|
|
46
|
-
*
|
|
47
|
+
* The subset of a Kysely handle the convention DDL touches.
|
|
48
|
+
*
|
|
49
|
+
* Kysely is invariant in its schema parameter, so naming only `schema` lets a
|
|
50
|
+
* builder holding a wider handle pass it without a cast.
|
|
47
51
|
*/
|
|
48
52
|
export type ConventionSchemaHandle = Pick<Kysely<ConventionDatabase>, "schema">
|
|
49
53
|
|
|
50
54
|
/**
|
|
51
|
-
* Create `address_convention`.
|
|
52
|
-
*
|
|
55
|
+
* Create `address_convention`.
|
|
56
|
+
*
|
|
57
|
+
* The table name comes from {@link ADDRESS_CONVENTION_TABLE} so the build script, the runtime source.
|
|
58
|
+
* The extract auto-detect stays aligned with it.
|
|
53
59
|
*/
|
|
54
60
|
export async function createAddressConventionTable(db: ConventionSchemaHandle): Promise<void> {
|
|
55
61
|
await db.schema
|