@mailwoman/resolver-wof-sqlite 10.0.0 → 10.2.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 +9 -8
- package/lib/address/point/interpolation.ts +45 -39
- package/lib/address/point/schema.ts +17 -40
- package/lib/address/point.ts +316 -0
- package/lib/{address/index.ts → address.ts} +1 -1
- package/lib/ancestry/backfill.ts +20 -86
- package/lib/{ancestry/index.ts → ancestry.ts} +19 -15
- package/lib/build/candidate.ts +122 -174
- package/lib/build/population-corrections.ts +64 -0
- package/lib/build/slim.ts +44 -114
- package/lib/candidate/alias-bags.ts +16 -17
- package/lib/candidate/ancestors/schema.ts +77 -61
- package/lib/candidate/ancestors/sidecar.ts +15 -22
- package/lib/candidate/country-display-names.ts +6 -20
- package/lib/candidate/extract-fold.ts +10 -55
- package/lib/candidate/fts.ts +5 -23
- package/lib/candidate/importance.ts +65 -68
- package/lib/candidate/lookup.ts +124 -415
- package/lib/candidate/name-roles.ts +15 -33
- package/lib/candidate/own-name.ts +35 -32
- package/lib/candidate/place-attrs.ts +20 -13
- package/lib/candidate/schema.ts +29 -65
- package/lib/candidate/scoring.ts +54 -93
- package/lib/capital-schema.ts +21 -17
- package/lib/capitals.ts +29 -27
- package/lib/coincident-roles.ts +85 -55
- package/lib/convention/schema.ts +15 -16
- package/lib/convention.ts +144 -0
- package/lib/coverage-manifest-schema.ts +40 -45
- package/lib/currency-backfill.ts +34 -79
- package/lib/env.ts +1 -1
- package/lib/exact-match.ts +32 -21
- package/lib/extracts.ts +97 -74
- package/lib/fst/autocomplete.ts +54 -37
- package/lib/fst/builder.ts +37 -41
- package/lib/fst/deserialize-web.ts +16 -17
- package/lib/fst/format.ts +41 -23
- package/lib/fst/freshness.ts +43 -120
- package/lib/fst/matcher.ts +7 -5
- package/lib/fst/serialize.ts +54 -74
- package/lib/fst/types.ts +78 -75
- package/lib/{fst/index.ts → fst.ts} +1 -0
- package/lib/fts/query.ts +5 -36
- package/lib/fts.ts +280 -0
- package/lib/geonames/aliases.ts +73 -144
- package/lib/geonames/postal.ts +162 -71
- package/lib/index.ts +9 -8
- package/lib/interpolation.ts +48 -100
- package/lib/lookup.ts +155 -270
- package/lib/name-score.ts +9 -7
- package/lib/nsul/lookup.ts +37 -54
- package/lib/nsul/schema.ts +16 -43
- package/lib/paths.ts +159 -0
- package/lib/place-importance-schema.ts +62 -169
- package/lib/poi/lookup.ts +104 -121
- package/lib/poi/schema.ts +42 -26
- package/lib/polygon-schema.ts +10 -6
- package/lib/postal/city/alias/lookup.ts +22 -40
- package/lib/postal/city/alias/schema.ts +14 -10
- package/lib/postal/city/candidate-schema.ts +26 -20
- package/lib/postcode-point-lookup.ts +8 -27
- package/lib/primary-preference.ts +46 -126
- package/lib/proximity-rerank.ts +44 -33
- package/lib/ranking-weights.ts +105 -78
- package/lib/region-keys.ts +70 -42
- package/lib/reverse.ts +149 -86
- package/lib/schema.ts +73 -50
- package/lib/search-fetch.ts +90 -68
- package/lib/sqlite/convention-source.ts +15 -23
- package/lib/sqlite/utils.ts +19 -22
- package/lib/street/centroid/schema.ts +37 -42
- package/lib/street/{centroid/index.ts → centroid.ts} +16 -25
- package/lib/street/morphology/fst/builder.ts +60 -42
- package/lib/street/morphology/fst/loader.ts +40 -32
- package/lib/street/name-lookup.ts +45 -41
- package/lib/street/normalize.ts +88 -219
- package/lib/street/segment-schema.ts +116 -43
- package/lib/{street/index.ts → street.ts} +1 -1
- package/lib/types.ts +109 -150
- package/lib/unified-schema.ts +29 -29
- package/lib/uprn/existence.ts +30 -19
- package/lib/uprn/lookup.ts +29 -68
- package/lib/uprn/schema.ts +18 -40
- package/lib/weights-overlay-linker.ts +236 -252
- package/out/address/point/interpolation.d.ts +20 -23
- package/out/address/point/interpolation.d.ts.map +1 -1
- package/out/address/point/interpolation.js +42 -39
- package/out/address/point/interpolation.js.map +1 -1
- package/out/address/point/schema.d.ts +12 -35
- package/out/address/point/schema.d.ts.map +1 -1
- package/out/address/point/schema.js +3 -19
- package/out/address/point/schema.js.map +1 -1
- package/out/address/point.d.ts +57 -0
- package/out/address/point.d.ts.map +1 -0
- package/out/address/point.js +206 -0
- package/out/address/point.js.map +1 -0
- package/out/{address/index.js → address.d.ts} +2 -2
- package/out/address.d.ts.map +1 -0
- package/out/{address/index.d.ts → address.js} +2 -2
- 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 +14 -74
- 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 +82 -112
- package/out/build/candidate.d.ts.map +1 -1
- package/out/build/candidate.js +17 -100
- package/out/build/candidate.js.map +1 -1
- 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 +8 -44
- package/out/build/slim.d.ts.map +1 -1
- package/out/build/slim.js +33 -94
- package/out/build/slim.js.map +1 -1
- package/out/candidate/alias-bags.d.ts +6 -6
- package/out/candidate/alias-bags.d.ts.map +1 -1
- package/out/candidate/alias-bags.js +16 -17
- package/out/candidate/alias-bags.js.map +1 -1
- package/out/candidate/ancestors/schema.d.ts +73 -57
- package/out/candidate/ancestors/schema.d.ts.map +1 -1
- package/out/candidate/ancestors/schema.js +56 -48
- package/out/candidate/ancestors/schema.js.map +1 -1
- package/out/candidate/ancestors/sidecar.d.ts +3 -8
- package/out/candidate/ancestors/sidecar.d.ts.map +1 -1
- package/out/candidate/ancestors/sidecar.js +15 -22
- package/out/candidate/ancestors/sidecar.js.map +1 -1
- 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 +7 -15
- package/out/candidate/extract-fold.d.ts.map +1 -1
- package/out/candidate/extract-fold.js +5 -47
- package/out/candidate/extract-fold.js.map +1 -1
- package/out/candidate/fts.d.ts +5 -23
- package/out/candidate/fts.d.ts.map +1 -1
- package/out/candidate/fts.js +5 -23
- package/out/candidate/fts.js.map +1 -1
- package/out/candidate/importance.d.ts +60 -65
- package/out/candidate/importance.d.ts.map +1 -1
- package/out/candidate/importance.js +56 -63
- package/out/candidate/importance.js.map +1 -1
- package/out/candidate/lookup.d.ts +18 -55
- package/out/candidate/lookup.d.ts.map +1 -1
- package/out/candidate/lookup.js +82 -385
- package/out/candidate/lookup.js.map +1 -1
- package/out/candidate/name-roles.d.ts +9 -21
- 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 +35 -32
- 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 +27 -62
- package/out/candidate/schema.d.ts.map +1 -1
- package/out/candidate/schema.js +10 -22
- package/out/candidate/schema.js.map +1 -1
- package/out/candidate/scoring.d.ts +7 -8
- package/out/candidate/scoring.d.ts.map +1 -1
- package/out/candidate/scoring.js +52 -89
- package/out/candidate/scoring.js.map +1 -1
- 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 +27 -25
- 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 +72 -50
- package/out/coincident-roles.js.map +1 -1
- package/out/convention/schema.d.ts +14 -14
- 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 +34 -39
- package/out/coverage-manifest-schema.d.ts.map +1 -1
- package/out/coverage-manifest-schema.js +25 -34
- package/out/coverage-manifest-schema.js.map +1 -1
- package/out/currency-backfill.d.ts +16 -38
- package/out/currency-backfill.d.ts.map +1 -1
- package/out/currency-backfill.js +19 -46
- package/out/currency-backfill.js.map +1 -1
- package/out/env.d.ts +1 -1
- 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 +32 -21
- package/out/exact-match.js.map +1 -1
- package/out/extracts.d.ts +66 -55
- 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 +29 -21
- package/out/fst/autocomplete.d.ts.map +1 -1
- package/out/fst/autocomplete.js +39 -28
- package/out/fst/autocomplete.js.map +1 -1
- package/out/fst/builder.d.ts.map +1 -1
- package/out/fst/builder.js +37 -41
- package/out/fst/builder.js.map +1 -1
- package/out/fst/deserialize-web.d.ts +4 -4
- package/out/fst/deserialize-web.d.ts.map +1 -1
- package/out/fst/deserialize-web.js +15 -16
- 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 +19 -81
- package/out/fst/freshness.d.ts.map +1 -1
- package/out/fst/freshness.js +31 -94
- package/out/fst/freshness.js.map +1 -1
- package/out/fst/matcher.d.ts +5 -4
- 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 +28 -28
- package/out/fst/serialize.d.ts.map +1 -1
- package/out/fst/serialize.js +53 -71
- package/out/fst/serialize.js.map +1 -1
- package/out/fst/types.d.ts +78 -74
- 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} +2 -1
- package/out/fst.d.ts.map +1 -0
- package/out/{fst/index.d.ts → fst.js} +2 -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 -62
- package/out/geonames/aliases.d.ts.map +1 -1
- package/out/geonames/aliases.js +45 -117
- package/out/geonames/aliases.js.map +1 -1
- package/out/geonames/postal.d.ts +84 -44
- package/out/geonames/postal.d.ts.map +1 -1
- package/out/geonames/postal.js +115 -73
- 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 +9 -8
- package/out/index.d.ts.map +1 -1
- package/out/index.js +6 -5
- package/out/index.js.map +1 -1
- package/out/interpolation.d.ts +18 -55
- package/out/interpolation.d.ts.map +1 -1
- package/out/interpolation.js +27 -69
- package/out/interpolation.js.map +1 -1
- package/out/lookup.d.ts +23 -51
- package/out/lookup.d.ts.map +1 -1
- package/out/lookup.js +136 -234
- 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 +51 -143
- package/out/place-importance-schema.d.ts.map +1 -1
- package/out/place-importance-schema.js +28 -143
- package/out/place-importance-schema.js.map +1 -1
- package/out/poi/lookup.d.ts +40 -47
- package/out/poi/lookup.d.ts.map +1 -1
- package/out/poi/lookup.js +69 -96
- 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 +20 -23
- package/out/postal/city/alias/lookup.d.ts.map +1 -1
- package/out/postal/city/alias/lookup.js +17 -30
- package/out/postal/city/alias/lookup.js.map +1 -1
- package/out/postal/city/alias/schema.d.ts +14 -10
- package/out/postal/city/alias/schema.d.ts.map +1 -1
- package/out/postal/city/alias/schema.js +10 -8
- package/out/postal/city/alias/schema.js.map +1 -1
- package/out/postal/city/candidate-schema.d.ts +25 -19
- package/out/postal/city/candidate-schema.d.ts.map +1 -1
- package/out/postal/city/candidate-schema.js +21 -17
- package/out/postal/city/candidate-schema.js.map +1 -1
- package/out/{postal/index.d.ts → postal.d.ts} +1 -1
- package/out/postal.d.ts.map +1 -0
- package/out/{postal/index.js → postal.js} +1 -1
- package/out/postal.js.map +1 -0
- package/out/postcode-point-lookup.d.ts +6 -24
- 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 +38 -92
- package/out/primary-preference.d.ts.map +1 -1
- package/out/primary-preference.js +15 -99
- package/out/primary-preference.js.map +1 -1
- package/out/proximity-rerank.d.ts +41 -30
- 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 +98 -64
- 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 +50 -31
- 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 +78 -50
- package/out/reverse.d.ts.map +1 -1
- package/out/reverse.js +98 -61
- package/out/reverse.js.map +1 -1
- package/out/schema.d.ts +72 -49
- 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 +12 -16
- package/out/sqlite/convention-source.d.ts.map +1 -1
- package/out/sqlite/convention-source.js +13 -22
- package/out/sqlite/convention-source.js.map +1 -1
- package/out/sqlite/utils.d.ts +19 -19
- package/out/sqlite/utils.d.ts.map +1 -1
- package/out/sqlite/utils.js +14 -15
- package/out/sqlite/utils.js.map +1 -1
- package/out/street/centroid/schema.d.ts +37 -42
- package/out/street/centroid/schema.d.ts.map +1 -1
- package/out/street/centroid/schema.js +9 -21
- package/out/street/centroid/schema.js.map +1 -1
- package/out/street/centroid.d.ts +30 -0
- package/out/street/centroid.d.ts.map +1 -0
- package/out/street/{centroid/index.js → centroid.js} +13 -24
- package/out/street/centroid.js.map +1 -0
- package/out/street/morphology/fst/builder.d.ts +20 -10
- package/out/street/morphology/fst/builder.d.ts.map +1 -1
- package/out/street/morphology/fst/builder.js +44 -34
- package/out/street/morphology/fst/builder.js.map +1 -1
- package/out/street/morphology/fst/loader.d.ts +33 -26
- package/out/street/morphology/fst/loader.d.ts.map +1 -1
- package/out/street/morphology/fst/loader.js +23 -22
- package/out/street/morphology/fst/loader.js.map +1 -1
- 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 +34 -35
- 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 +77 -194
- 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.js → street.d.ts} +2 -2
- package/out/street.d.ts.map +1 -0
- package/out/{street/index.d.ts → street.js} +2 -2
- package/out/street.js.map +1 -0
- package/out/types.d.ts +84 -149
- 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 +21 -57
- 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 +138 -144
- package/out/weights-overlay-linker.d.ts.map +1 -1
- package/out/weights-overlay-linker.js +105 -159
- package/out/weights-overlay-linker.js.map +1 -1
- package/package.json +91 -596
- package/lib/address/point/index.ts +0 -328
- package/lib/convention/index.ts +0 -154
- package/lib/fts/index.ts +0 -361
- package/out/address/index.d.ts.map +0 -1
- package/out/address/index.js.map +0 -1
- package/out/address/point/index.d.ts +0 -53
- package/out/address/point/index.d.ts.map +0 -1
- package/out/address/point/index.js +0 -222
- package/out/address/point/index.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/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/index.d.ts.map +0 -1
- package/out/postal/index.js.map +0 -1
- package/out/street/centroid/index.d.ts +0 -40
- package/out/street/centroid/index.d.ts.map +0 -1
- package/out/street/centroid/index.js.map +0 -1
- package/out/street/index.d.ts.map +0 -1
- package/out/street/index.js.map +0 -1
- package/out/uprn/index.d.ts.map +0 -1
- package/out/uprn/index.js.map +0 -1
- /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/{postal/index.ts → postal.ts} +0 -0
- /package/lib/{uprn/index.ts → uprn.ts} +0 -0
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
|
+
* @defaultValue `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
|
+
* @defaultValue `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
|
+
* @defaultValue `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
|
+
* @defaultValue `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[]> {
|
|
@@ -301,6 +320,17 @@ export function loadCoincidentLocalities<DB>(db: DatabaseClient<DB>): Map<number
|
|
|
301
320
|
relationshipType: r.relationshipType,
|
|
302
321
|
population: r.population,
|
|
303
322
|
distanceKm: r.distanceKm,
|
|
323
|
+
parent_id: null,
|
|
324
|
+
prominence: null,
|
|
325
|
+
referential: null,
|
|
326
|
+
encyclopedic: null,
|
|
327
|
+
importance: null,
|
|
328
|
+
exactMatch: null,
|
|
329
|
+
mismatch: null,
|
|
330
|
+
resolutionQuality: null,
|
|
331
|
+
containedByQualifier: null,
|
|
332
|
+
regionScopeMiss: null,
|
|
333
|
+
variantAliasExempted: null,
|
|
304
334
|
}
|
|
305
335
|
|
|
306
336
|
const list = map.get(r.adminID)
|
package/lib/convention/schema.ts
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
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
|
+
import type { KeyValueMetaTable } from "@mailwoman/sqlite/database-schema"
|
|
12
13
|
import type { Kysely } from "kysely"
|
|
13
14
|
|
|
14
|
-
import { ADDRESS_CONVENTION_TABLE } from "#convention
|
|
15
|
+
import { ADDRESS_CONVENTION_TABLE } from "#convention"
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* One convention profile, keyed by the WOF admin polygon it attaches to.
|
|
@@ -19,7 +20,9 @@ import { ADDRESS_CONVENTION_TABLE } from "#convention/index"
|
|
|
19
20
|
export interface AddressConventionTable {
|
|
20
21
|
wof_id: number
|
|
21
22
|
/**
|
|
22
|
-
* The `Convention` record, JSON-encoded.
|
|
23
|
+
* The `Convention` record, JSON-encoded.
|
|
24
|
+
*
|
|
25
|
+
* Parsed at read time by `SqliteConventionSource`.
|
|
23
26
|
*/
|
|
24
27
|
convention: string
|
|
25
28
|
/**
|
|
@@ -28,28 +31,24 @@ export interface AddressConventionTable {
|
|
|
28
31
|
source: string
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
/**
|
|
32
|
-
* Key/value provenance for the sealed artifact.
|
|
33
|
-
*/
|
|
34
|
-
export interface ConventionMetaTable {
|
|
35
|
-
key: string
|
|
36
|
-
value: string | null
|
|
37
|
-
}
|
|
38
|
-
|
|
39
34
|
export interface ConventionDatabase {
|
|
40
35
|
address_convention: AddressConventionTable
|
|
41
|
-
meta:
|
|
36
|
+
meta: KeyValueMetaTable
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
/**
|
|
45
|
-
* The
|
|
46
|
-
*
|
|
40
|
+
* The subset of a Kysely handle the convention DDL touches.
|
|
41
|
+
*
|
|
42
|
+
* Kysely is invariant in its schema parameter, so naming only `schema` lets a
|
|
43
|
+
* builder holding a wider handle pass it without a cast.
|
|
47
44
|
*/
|
|
48
45
|
export type ConventionSchemaHandle = Pick<Kysely<ConventionDatabase>, "schema">
|
|
49
46
|
|
|
50
47
|
/**
|
|
51
|
-
* Create `address_convention`.
|
|
52
|
-
*
|
|
48
|
+
* Create `address_convention`.
|
|
49
|
+
*
|
|
50
|
+
* The table name comes from {@link ADDRESS_CONVENTION_TABLE} so the build script, the runtime source.
|
|
51
|
+
* The extract auto-detect stays aligned with it.
|
|
53
52
|
*/
|
|
54
53
|
export async function createAddressConventionTable(db: ConventionSchemaHandle): Promise<void> {
|
|
55
54
|
await db.schema
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
* @file Defines resolution conventions as per-polygon profiles merged along a place's ancestor chain.
|
|
6
|
+
*
|
|
7
|
+
* The strategy implementations live in `lookup.ts` and are registered by name.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { FindPlaceQuery, PlaceCandidate } from "#types"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Soft-scoring weights for the `postcode_area_resolution` strategy: `pc·S_pc + name·S_name + pop·S_pop`.
|
|
14
|
+
*/
|
|
15
|
+
export interface ScoringWeights {
|
|
16
|
+
pc: number
|
|
17
|
+
name: number
|
|
18
|
+
pop: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A resolution profile attached to a Who's On First admin polygon.
|
|
23
|
+
*/
|
|
24
|
+
export interface Convention {
|
|
25
|
+
/**
|
|
26
|
+
* The strategies that the dispatcher runs in order, by registered name.
|
|
27
|
+
* The first strategy that returns a non-null result wins.
|
|
28
|
+
*/
|
|
29
|
+
candidateStrategies?: string[]
|
|
30
|
+
/**
|
|
31
|
+
* Weights for the `postcode_area_resolution` soft score.
|
|
32
|
+
*
|
|
33
|
+
* A layer may set some weights and inherit the rest from the layers below it.
|
|
34
|
+
*/
|
|
35
|
+
scoringWeights?: Partial<ScoringWeights>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A convention with every field set, as `resolveConvention` returns it to strategies.
|
|
40
|
+
*/
|
|
41
|
+
export interface ResolvedConvention {
|
|
42
|
+
candidateStrategies: string[]
|
|
43
|
+
scoringWeights: ScoringWeights
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The base layer that every ancestor chain starts from.
|
|
48
|
+
*
|
|
49
|
+
* It tries `postcode_area_resolution` and then falls back to fuzzy name matching.
|
|
50
|
+
* Every place without a convention override uses these values, so a change here
|
|
51
|
+
* changes resolution in every such country.
|
|
52
|
+
*/
|
|
53
|
+
export const WORLD_DEFAULT: ResolvedConvention = {
|
|
54
|
+
candidateStrategies: ["postcode_area_resolution", "fallback_fuzzy_name_match"],
|
|
55
|
+
scoringWeights: { pc: 0.6, name: 0.3, pop: 0.1 },
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The names of the strategies that the backend registers.
|
|
60
|
+
*
|
|
61
|
+
* The dispatch registry and the build-time validator share this list, so the build
|
|
62
|
+
* rejects a convention that lists an unknown strategy.
|
|
63
|
+
*/
|
|
64
|
+
export const BUILTIN_STRATEGY_NAMES = ["postcode_area_resolution", "fallback_fuzzy_name_match"] as const
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The table name for the convention asset, shared by the build script,
|
|
68
|
+
* the runtime source and extract detection.
|
|
69
|
+
*/
|
|
70
|
+
export const ADDRESS_CONVENTION_TABLE = "address_convention"
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A resolution strategy selected by name.
|
|
74
|
+
*
|
|
75
|
+
* @returns `null` to abstain, so the dispatcher tries the next strategy,
|
|
76
|
+
* or an array, possibly empty, to claim the result.
|
|
77
|
+
*/
|
|
78
|
+
export type Strategy = (query: FindPlaceQuery, convention: ResolvedConvention) => Promise<PlaceCandidate[] | null>
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Looks up a convention by WOF polygon id.
|
|
82
|
+
* `get` returns `null` when the polygon has no override.
|
|
83
|
+
*/
|
|
84
|
+
export interface ConventionSource {
|
|
85
|
+
get(wofID: number): Convention | null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* An in-memory convention source built from a `{ wofID: Convention }` map.
|
|
90
|
+
*/
|
|
91
|
+
export class SeedConventionSource implements ConventionSource {
|
|
92
|
+
readonly #rows: Map<number, Convention>
|
|
93
|
+
|
|
94
|
+
constructor(rows: Record<number, Convention> = {}) {
|
|
95
|
+
this.#rows = new Map(Object.entries(rows).map(([k, v]) => [Number(k), v]))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get(wofID: number): Convention | null {
|
|
99
|
+
return this.#rows.get(wofID) ?? null
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Merges convention layers, with later, more specific layers winning per field.
|
|
105
|
+
*
|
|
106
|
+
* A layer's `candidateStrategies` replaces the whole list.
|
|
107
|
+
* `scoringWeights` merges key by key, so a layer can change one weight without restating the others.
|
|
108
|
+
*/
|
|
109
|
+
export function mergeConventions(base: Convention, ...overrides: Array<Convention | null | undefined>): Convention {
|
|
110
|
+
const out: Convention = {
|
|
111
|
+
candidateStrategies: base.candidateStrategies ? [...base.candidateStrategies] : undefined,
|
|
112
|
+
scoringWeights: base.scoringWeights ? { ...base.scoringWeights } : undefined,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for (const o of overrides) {
|
|
116
|
+
if (!o) continue
|
|
117
|
+
|
|
118
|
+
if (o.candidateStrategies) {
|
|
119
|
+
out.candidateStrategies = [...o.candidateStrategies]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (o.scoringWeights) {
|
|
123
|
+
out.scoringWeights = { ...(out.scoringWeights ?? WORLD_DEFAULT.scoringWeights), ...o.scoringWeights }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return out
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Resolves the effective convention for a place from its ancestor ids,
|
|
132
|
+
* ordered from most general to most specific.
|
|
133
|
+
*
|
|
134
|
+
* The merge starts from `WORLD_DEFAULT`, so every field is defined.
|
|
135
|
+
*/
|
|
136
|
+
export function resolveConvention(source: ConventionSource, ancestorIDs: readonly number[]): ResolvedConvention {
|
|
137
|
+
const layers = ancestorIDs.map((id) => source.get(id))
|
|
138
|
+
const merged = mergeConventions(WORLD_DEFAULT, ...layers)
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
candidateStrategies: merged.candidateStrategies ?? WORLD_DEFAULT.candidateStrategies,
|
|
142
|
+
scoringWeights: { ...WORLD_DEFAULT.scoringWeights, ...merged.scoringWeights },
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -2,25 +2,11 @@
|
|
|
2
2
|
* @copyright Sister Software
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
|
+
* @file Schema and read/write helpers for the candidate gazetteer's coverage manifest.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - `country_coverage`: the hard-country-filter coverage record (#743/#194) — per-country
|
|
11
|
-
* promotion-eval verdicts + the measured hard-resolve rates that used to live in a code comment on
|
|
12
|
-
* `HARD_PLACE_COUNTRY_SAFELIST`. Presence = measured; `hard_filter_safe = 0` = measured and
|
|
13
|
-
* FAILED the check (FI 69.5%, PL 77.8%) — distinguishable from a country never measured at all
|
|
14
|
-
* (the meaning-of-zero rule, `docs/engineering/reference/layer-contract.mdx`).
|
|
15
|
-
* - `country_bbox`: the coarse guard-B plausibility boxes that used to live in
|
|
16
|
-
* `resolver/plausibility.ts`'s `COUNTRY_BBOX`. An absent row fails open (never trips the guard),
|
|
17
|
-
* exactly like an absent key in the constant.
|
|
18
|
-
*
|
|
19
|
-
* Emission happens at build time (`mailwoman/gazetteer-pipeline/coverage-manifest.ts` owns the
|
|
20
|
-
* measured record and calls {@link writeGazetteerCoverageManifest} before the DB is sealed — never
|
|
21
|
-
* patch a shipped DB, rebuild). The read happens at open time ({@link WOFCandidateTableLookup}
|
|
22
|
-
* calls {@link readGazetteerCoverageManifest} in its constructor); an artifact that predates the
|
|
23
|
-
* manifest returns `undefined` and every consumer falls back to the code constants byte-identically.
|
|
7
|
+
* The manifest holds two country-keyed tables. `country_coverage` records each measured country's hard-filter
|
|
8
|
+
* verdict. A missing row means the country was never measured. `hard_filter_safe = 0` means measurement found the
|
|
9
|
+
* filter unsafe. `country_bbox` holds coarse plausibility boxes. A missing bbox row never trips the guard.
|
|
24
10
|
*/
|
|
25
11
|
|
|
26
12
|
import {
|
|
@@ -36,41 +22,45 @@ import { sql, type Kysely } from "kysely"
|
|
|
36
22
|
import { hasTable } from "#sqlite-utils"
|
|
37
23
|
|
|
38
24
|
/**
|
|
39
|
-
*
|
|
25
|
+
* The stored form of {@link CountryCoverageFact}, one row per country.
|
|
40
26
|
*/
|
|
41
27
|
export interface CountryCoverageTable {
|
|
42
28
|
/**
|
|
43
|
-
* ISO 3166-1 alpha-2
|
|
29
|
+
* The upper-case ISO 3166-1 alpha-2 code.
|
|
30
|
+
* This field is the primary key.
|
|
44
31
|
*/
|
|
45
32
|
country: string
|
|
46
33
|
/**
|
|
47
|
-
*
|
|
34
|
+
* The promotion-eval verdict as 0 or 1.
|
|
35
|
+
*
|
|
36
|
+
* It is stored directly because the rate by itself does not decide it.
|
|
48
37
|
*/
|
|
49
38
|
hard_filter_safe: number
|
|
50
39
|
/**
|
|
51
|
-
*
|
|
40
|
+
* The hard-resolve rate from 0 to 1 on the panel given in `source`, or NULL when none was recorded.
|
|
52
41
|
*/
|
|
53
42
|
hard_resolve_rate: number | null
|
|
54
43
|
/**
|
|
55
|
-
*
|
|
44
|
+
* The panel size behind `hard_resolve_rate`, or NULL when none was recorded.
|
|
56
45
|
*/
|
|
57
46
|
sample_size: number | null
|
|
58
47
|
/**
|
|
59
|
-
* ISO
|
|
48
|
+
* The ISO 8601 date of the measurement.
|
|
60
49
|
*/
|
|
61
50
|
measured_at: string
|
|
62
51
|
/**
|
|
63
|
-
* The
|
|
52
|
+
* The panel or check that produced this row.
|
|
64
53
|
*/
|
|
65
54
|
source: string
|
|
66
55
|
}
|
|
67
56
|
|
|
68
57
|
/**
|
|
69
|
-
*
|
|
58
|
+
* The stored form of {@link CountryBBoxFact}, one coarse bounding box per country.
|
|
70
59
|
*/
|
|
71
60
|
export interface CountryBBoxTable {
|
|
72
61
|
/**
|
|
73
|
-
* ISO 3166-1 alpha-2
|
|
62
|
+
* The upper-case ISO 3166-1 alpha-2 code.
|
|
63
|
+
* This field is the primary key.
|
|
74
64
|
*/
|
|
75
65
|
country: string
|
|
76
66
|
lat_min: number
|
|
@@ -78,7 +68,7 @@ export interface CountryBBoxTable {
|
|
|
78
68
|
lon_min: number
|
|
79
69
|
lon_max: number
|
|
80
70
|
/**
|
|
81
|
-
*
|
|
71
|
+
* The provenance of the box.
|
|
82
72
|
*/
|
|
83
73
|
source: string
|
|
84
74
|
}
|
|
@@ -92,16 +82,17 @@ export interface GazetteerCoverageDatabase {
|
|
|
92
82
|
}
|
|
93
83
|
|
|
94
84
|
/**
|
|
95
|
-
*
|
|
85
|
+
* The name of the per-country coverage table.
|
|
86
|
+
* Readers check that it exists before querying it.
|
|
96
87
|
*/
|
|
97
88
|
export const COUNTRY_COVERAGE_TABLE = "country_coverage"
|
|
98
89
|
/**
|
|
99
|
-
*
|
|
90
|
+
* The name of the per-country bounding-box table, used to reject a placement outside its own country.
|
|
100
91
|
*/
|
|
101
92
|
export const COUNTRY_BBOX_TABLE = "country_bbox"
|
|
102
93
|
|
|
103
94
|
/**
|
|
104
|
-
*
|
|
95
|
+
* Creates the `country_coverage` table as a `WITHOUT ROWID` table keyed by country.
|
|
105
96
|
*/
|
|
106
97
|
export async function createCountryCoverageTable(db: Kysely<GazetteerCoverageDatabase>): Promise<void> {
|
|
107
98
|
await db.schema
|
|
@@ -113,13 +104,13 @@ export async function createCountryCoverageTable(db: Kysely<GazetteerCoverageDat
|
|
|
113
104
|
.addColumn("sample_size", "integer")
|
|
114
105
|
.addColumn("measured_at", "text", (c) => c.notNull())
|
|
115
106
|
.addColumn("source", "text", (c) => c.notNull())
|
|
116
|
-
//
|
|
107
|
+
// Kysely has no builder method for `without rowid`.
|
|
117
108
|
.modifyEnd(sql`without rowid`)
|
|
118
109
|
.execute()
|
|
119
110
|
}
|
|
120
111
|
|
|
121
112
|
/**
|
|
122
|
-
*
|
|
113
|
+
* Creates the `country_bbox` table as a `WITHOUT ROWID` table keyed by country.
|
|
123
114
|
*/
|
|
124
115
|
export async function createCountryBBoxTable(db: Kysely<GazetteerCoverageDatabase>): Promise<void> {
|
|
125
116
|
await db.schema
|
|
@@ -136,8 +127,10 @@ export async function createCountryBBoxTable(db: Kysely<GazetteerCoverageDatabas
|
|
|
136
127
|
}
|
|
137
128
|
|
|
138
129
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
130
|
+
* Creates both manifest tables in a candidate database under construction and inserts the facts.
|
|
131
|
+
*
|
|
132
|
+
* The gazetteer build calls it once.
|
|
133
|
+
* A shipped database is rebuilt instead of patched.
|
|
141
134
|
*/
|
|
142
135
|
export async function writeGazetteerCoverageManifest(
|
|
143
136
|
db: Kysely<GazetteerCoverageDatabase>,
|
|
@@ -153,8 +146,8 @@ export async function writeGazetteerCoverageManifest(
|
|
|
153
146
|
facts.coverage.map((f) => ({
|
|
154
147
|
country: f.country.toUpperCase(),
|
|
155
148
|
hard_filter_safe: f.hardFilterSafe ? 1 : 0,
|
|
156
|
-
hard_resolve_rate: f.hardResolveRate
|
|
157
|
-
sample_size: f.sampleSize
|
|
149
|
+
hard_resolve_rate: f.hardResolveRate,
|
|
150
|
+
sample_size: f.sampleSize,
|
|
158
151
|
measured_at: f.measuredAt,
|
|
159
152
|
source: f.source,
|
|
160
153
|
}))
|
|
@@ -180,16 +173,18 @@ export async function writeGazetteerCoverageManifest(
|
|
|
180
173
|
}
|
|
181
174
|
|
|
182
175
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
176
|
+
* Reads the coverage manifest from an open candidate database.
|
|
177
|
+
*
|
|
178
|
+
* It returns `undefined` when neither table exists.
|
|
179
|
+
* Consumers then fall back to the code constants.
|
|
180
|
+
*
|
|
181
|
+
* The reads are synchronous because the candidate lookup calls this from its constructor.
|
|
187
182
|
*/
|
|
188
|
-
export function readGazetteerCoverageManifest<DB>(db: DatabaseClient<DB>): GazetteerArtifactCoverage |
|
|
183
|
+
export function readGazetteerCoverageManifest<DB>(db: DatabaseClient<DB>): GazetteerArtifactCoverage | null {
|
|
189
184
|
const hasCoverage = hasTable(db, COUNTRY_COVERAGE_TABLE)
|
|
190
185
|
const hasBBox = hasTable(db, COUNTRY_BBOX_TABLE)
|
|
191
186
|
|
|
192
|
-
if (!hasCoverage && !hasBBox) return
|
|
187
|
+
if (!hasCoverage && !hasBBox) return null
|
|
193
188
|
|
|
194
189
|
const countryCoverage = new Map<string, CountryCoverageFact>()
|
|
195
190
|
|
|
@@ -206,8 +201,8 @@ export function readGazetteerCoverageManifest<DB>(db: DatabaseClient<DB>): Gazet
|
|
|
206
201
|
countryCoverage.set(country, {
|
|
207
202
|
country,
|
|
208
203
|
hardFilterSafe: Number(row.hard_filter_safe) !== 0,
|
|
209
|
-
|
|
210
|
-
|
|
204
|
+
hardResolveRate: row.hard_resolve_rate === null ? null : Number(row.hard_resolve_rate),
|
|
205
|
+
sampleSize: row.sample_size === null ? null : Number(row.sample_size),
|
|
211
206
|
measuredAt: String(row.measured_at),
|
|
212
207
|
source: String(row.source),
|
|
213
208
|
})
|