@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/unified-schema.ts
CHANGED
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* table (which lookup.ts's parent-constraint subquery needs) — see `populateAncestors`. The
|
|
12
|
-
* `place_search` FTS5 + `place_bbox` R*Tree are built separately by `build-fts` (fts.ts).
|
|
6
|
+
* Schema for the unified WOF SQLite database built from cloned WOF GeoJSON repos
|
|
7
|
+
* (`scripts/build-unified-wof.ts`). The table and column names match the resolver's
|
|
8
|
+
* expectations (`lookup.ts`) so `WOFSQLitePlaceLookup` works unchanged. The `ancestors` table
|
|
9
|
+
* table is what lookup.ts's parent-constraint subquery reads (see `populateAncestors`).
|
|
10
|
+
* The `place_search` FTS5 and `place_bbox` R*Tree tables are built separately by `build-fts` (fts.ts).
|
|
13
11
|
*/
|
|
14
12
|
|
|
15
13
|
import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
@@ -17,13 +15,14 @@ import type { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
|
17
15
|
import type { WOFDatabase } from "#schema"
|
|
18
16
|
|
|
19
17
|
export async function createUnifiedSchema(db: DatabaseClient<WOFDatabase>): Promise<void> {
|
|
20
|
-
// PRAGMAs
|
|
18
|
+
// PRAGMAs run raw because Kysely does not model them.
|
|
19
|
+
// They tune the bulk build.
|
|
21
20
|
db.exec("PRAGMA journal_mode = WAL")
|
|
22
21
|
db.exec("PRAGMA busy_timeout = 10000")
|
|
23
22
|
db.exec("PRAGMA synchronous = OFF")
|
|
24
23
|
|
|
25
|
-
//
|
|
26
|
-
//
|
|
24
|
+
// The caller owns `db`'s lifecycle, so this function does not destroy it.
|
|
25
|
+
// The bulk INSERTs (populateAncestors + build-unified-wof) run on the raw handle.
|
|
27
26
|
|
|
28
27
|
await db.schema
|
|
29
28
|
.createTable("spr")
|
|
@@ -47,12 +46,16 @@ export async function createUnifiedSchema(db: DatabaseClient<WOFDatabase>): Prom
|
|
|
47
46
|
.addColumn("lastmodified", "integer", (c) => c.notNull().defaultTo(0))
|
|
48
47
|
.execute()
|
|
49
48
|
|
|
50
|
-
// `privateuse`
|
|
51
|
-
// ("preferred" | "").
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
49
|
+
// `privateuse` represents WOF's x_<variant> kind (preferred | variant) or GeoNames'
|
|
50
|
+
// isPreferredName ("preferred" | "").
|
|
51
|
+
// `official` is the ingest bit.
|
|
52
|
+
// It is 1 when the row's language is an official language of the place's country
|
|
53
|
+
// (codex OFFICIAL_LANGUAGES) and the row is a preferred form. x_variant rows
|
|
54
|
+
// tagged with an official language ("MSP", "Frisco") stay 0.
|
|
55
|
+
// Primary-name mirror rows stay 0 too.
|
|
56
|
+
// The name-exact tier already consults spr.name.
|
|
57
|
+
// `official` only marks the aliases eligible to join it.
|
|
58
|
+
// Both are ingest-time facts, never computed at query time.
|
|
56
59
|
await db.schema
|
|
57
60
|
.createTable("names")
|
|
58
61
|
.ifNotExists()
|
|
@@ -82,11 +85,12 @@ export async function createUnifiedSchema(db: DatabaseClient<WOFDatabase>): Prom
|
|
|
82
85
|
.addColumn("population", "integer", (c) => c.notNull().defaultTo(0))
|
|
83
86
|
.execute()
|
|
84
87
|
|
|
85
|
-
// `ancestors` maps each place to every place above it in the hierarchy (and itself).
|
|
86
|
-
// resolver's parent-constraint scopes a child lookup to a parent's descendants
|
|
87
|
-
// `spr.id IN (
|
|
88
|
-
//
|
|
89
|
-
//
|
|
88
|
+
// `ancestors` maps each place to every place above it in the hierarchy (and itself).
|
|
89
|
+
// The resolver's parent-constraint scopes a child lookup to a parent's descendants
|
|
90
|
+
// via `spr.id IN (select id from ancestors where ancestor_id = ?)`.
|
|
91
|
+
// The off-the-shelf WOF dumps ship this table.
|
|
92
|
+
// Our build derives it from the parent_id chain (see populateAncestors)
|
|
93
|
+
// since we don't capture `wof:hierarchy`.
|
|
90
94
|
await db.schema
|
|
91
95
|
.createTable("ancestors")
|
|
92
96
|
.ifNotExists()
|
|
@@ -98,11 +102,9 @@ export async function createUnifiedSchema(db: DatabaseClient<WOFDatabase>): Prom
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
/**
|
|
101
|
-
* Populate the `ancestors` table by walking each place's `parent_id` chain in `spr`
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* Sentinel/negative parent_ids and cycles terminate the walk. ~4 rows/place average; a transaction keeps the ~5M
|
|
105
|
-
* inserts fast.
|
|
105
|
+
* Populate the `ancestors` table by walking each place's `parent_id` chain in `spr`
|
|
106
|
+
* to a transitive closure that includes the place itself.
|
|
107
|
+
* Run this after `spr` is fully ingested.
|
|
106
108
|
*/
|
|
107
109
|
export function populateAncestors<DB>(db: DatabaseClient<DB>): number {
|
|
108
110
|
db.exec("DELETE FROM ancestors")
|
|
@@ -126,7 +128,6 @@ export function populateAncestors<DB>(db: DatabaseClient<DB>): number {
|
|
|
126
128
|
for (const r of rows) {
|
|
127
129
|
insert.run(r.id, r.id, r.placetype)
|
|
128
130
|
|
|
129
|
-
// self
|
|
130
131
|
count++
|
|
131
132
|
const seen = new Set<number>([r.id])
|
|
132
133
|
let cur = r.parent_id
|
|
@@ -169,8 +170,7 @@ export async function createUnifiedIndexes(db: DatabaseClient<WOFDatabase>): Pro
|
|
|
169
170
|
.columns(["other_source", "other_id"])
|
|
170
171
|
.execute()
|
|
171
172
|
|
|
172
|
-
// ancestor_id is the hot column
|
|
173
|
-
// the reverse lookup.
|
|
173
|
+
// ancestor_id is the hot column for parent-constraint queries. id supports the reverse lookup.
|
|
174
174
|
await db.schema.createIndex("ancestors_by_ancestor").ifNotExists().on("ancestors").column("ancestor_id").execute()
|
|
175
175
|
await db.schema.createIndex("ancestors_by_id").ifNotExists().on("ancestors").column("id").execute()
|
|
176
176
|
}
|
package/lib/uprn/existence.ts
CHANGED
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
* @file The consult `./lookup.ts`'s docstring instructs: "callers building negative evidence must consult
|
|
6
|
-
* `readLayerCoverage`, not this reader
|
|
7
|
-
* or
|
|
6
|
+
* `readLayerCoverage`, not this reader by itself." A bare `null` from `nearestUPRN` is two different facts — no uprn here,
|
|
7
|
+
* or an unsurveyed area — and this is the only place that separates them. It answers an {@link Exclusion} or `null`,
|
|
8
8
|
* so a caller cannot read an unsurveyed cell as an empty one by accident.
|
|
9
9
|
*
|
|
10
|
-
* `radiusM` is a
|
|
11
|
-
* this coordinate" and "is this street built at all", and picking one here would bury that choice where
|
|
10
|
+
* `radiusM` is a caller'S parameter with no default. There is no radius that is correct for both "which property is
|
|
11
|
+
* this coordinate" and "is this street built at all", and picking one here would bury that choice where reviewers
|
|
12
12
|
* reviewing an exclusion can see it.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { readLayerCoverage, readLayerManifest, type
|
|
15
|
+
import { readLayerCoverage, readLayerManifest, type layerschemahandle } from "@mailwoman/core/layers"
|
|
16
16
|
import { foldIdentity, requireExclusionBasis, type Exclusion } from "@mailwoman/evidence"
|
|
17
17
|
import { shortCellToParentInt } from "@mailwoman/spatial"
|
|
18
18
|
|
|
@@ -20,24 +20,31 @@ import type { UPRNLookup } from "#uprn/lookup"
|
|
|
20
20
|
import { UPRN_COVERAGE_H3_RESOLUTION, UPRN_H3_RESOLUTION, uprnH3Cell } from "#uprn/schema"
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* The identity fold, on purpose: this probe keys on a
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* The identity fold, on purpose: this probe keys on a coordinate rather than a name,
|
|
24
|
+
* so there is no string folding for the builder and the probe to disagree about.
|
|
25
|
+
*
|
|
26
|
+
* The same identity in both `probeFold` and `layerFold` records that the fold axis
|
|
27
|
+
* is not in play here, rather than silently omitting the check.
|
|
28
|
+
* It is not a stub.
|
|
26
29
|
*/
|
|
27
30
|
export const UPRN_EXISTENCE_FOLD = foldIdentity((s) => s)
|
|
28
31
|
|
|
29
32
|
/**
|
|
30
|
-
* The countries OS Open
|
|
31
|
-
*
|
|
33
|
+
* The countries OS Open uprn covers.
|
|
34
|
+
*
|
|
35
|
+
* Northern Ireland is outside the product, so a `null` there is unknown
|
|
36
|
+
* and must never become evidence of absence.
|
|
37
|
+
* The country check is what keeps it out.
|
|
32
38
|
*/
|
|
33
39
|
export const UPRN_COVERED_COUNTRIES: ReadonlySet<string> = new Set(["GB"])
|
|
34
40
|
|
|
35
41
|
export interface UPRNAbsenceInput {
|
|
36
42
|
lookup: UPRNLookup
|
|
37
43
|
/**
|
|
38
|
-
* The same file the lookup reads
|
|
44
|
+
* The same file the lookup reads.
|
|
45
|
+
* The layer interface tables live beside the points.
|
|
39
46
|
*/
|
|
40
|
-
|
|
47
|
+
schemadb: layerschemahandle
|
|
41
48
|
latitude: number
|
|
42
49
|
longitude: number
|
|
43
50
|
radiusM: number
|
|
@@ -48,27 +55,31 @@ export interface UPRNAbsenceInput {
|
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
/**
|
|
51
|
-
* The coverage cell a coordinate falls in, derived from its stored res-9 cell exactly
|
|
52
|
-
*
|
|
58
|
+
* The coverage cell a coordinate falls in, derived from its stored res-9 cell exactly
|
|
59
|
+
* as the builder derives it rather than from the centroid.
|
|
60
|
+
*
|
|
61
|
+
* The centroid lands in a different parent for a fraction of cells.
|
|
53
62
|
*/
|
|
54
63
|
export function uprnCoverageCell(latitude: number, longitude: number): number {
|
|
55
64
|
return shortCellToParentInt(uprnH3Cell(latitude, longitude), UPRN_H3_RESOLUTION, UPRN_COVERAGE_H3_RESOLUTION)
|
|
56
65
|
}
|
|
57
66
|
|
|
58
67
|
/**
|
|
59
|
-
* An {@link Exclusion} when no
|
|
60
|
-
* (presence is not this probe's business) and on every refusal
|
|
61
|
-
*
|
|
68
|
+
* An {@link Exclusion} when no uprn lies within `radiusM` and the layer's coverage licenses
|
|
69
|
+
* saying so; `null` on a hit (presence is not this probe's business) and on every refusal
|
|
70
|
+
* (unsurveyed cell, `source_present` basis, a country the product does not cover).
|
|
71
|
+
*
|
|
72
|
+
* Never throws on a refusal: the caller falls open to the ranking it already had.
|
|
62
73
|
*/
|
|
63
74
|
export async function uprnAbsenceAt(input: UPRNAbsenceInput): Promise<Exclusion | null> {
|
|
64
75
|
const coverageCell = uprnCoverageCell(input.latitude, input.longitude)
|
|
65
|
-
const coverage = await readLayerCoverage(input.
|
|
76
|
+
const coverage = await readLayerCoverage(input.schemadb, coverageCell)
|
|
66
77
|
|
|
67
78
|
if (!coverage) return null
|
|
68
79
|
|
|
69
80
|
if (input.lookup.nearestUPRN(input.latitude, input.longitude, input.radiusM)) return null
|
|
70
81
|
|
|
71
|
-
const manifest = await readLayerManifest(input.
|
|
82
|
+
const manifest = await readLayerManifest(input.schemadb)
|
|
72
83
|
|
|
73
84
|
return requireExclusionBasis({
|
|
74
85
|
layer: manifest.name,
|
package/lib/uprn/lookup.ts
CHANGED
|
@@ -3,53 +3,32 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - **`coordinateOf(uprn)`**: rowid B-tree hit on the `uprn` integer PK.
|
|
9
|
-
* - **`nearestUPRN(lat, lon, radiusM)`**: bounded nearest-point search over the res-9 `h3_cell`
|
|
10
|
-
* index — ring-by-ring `gridDisk` expansion with chunked `IN` probes and haversine ranking.
|
|
11
|
-
* Rings stop as soon as geometry proves no unprobed cell could beat the best hit — a distance
|
|
12
|
-
* bound, not POILookup's row-count accumulation, so the early exit can never strand a nearer
|
|
13
|
-
* point in an unprobed ring.
|
|
14
|
-
*
|
|
15
|
-
* ## `null` is a claim, scoped by coverage
|
|
16
|
-
*
|
|
17
|
-
* OS designates Open UPRN complete for GB (every UPRN in AddressBase Premium with geometry), and
|
|
18
|
-
* the builder writes `layer_coverage` with basis `designated` for every cell the product touches.
|
|
19
|
-
* So a `null` from either probe inside a covered cell is evidence of absence — "no such published
|
|
20
|
-
* GB UPRN" / "no UPRN within the radius". Outside coverage (Northern Ireland, the Isle of Man, the
|
|
21
|
-
* Channel Islands, open water) it is UNKNOWN, per the meaning-of-zero rule — callers building
|
|
22
|
-
* negative evidence must consult `readLayerCoverage`, not this reader alone.
|
|
23
|
-
*
|
|
24
|
-
* `latLngToCell`/`gridDisk` come from `h3-js`; the 48-bit short-cell packing is
|
|
25
|
-
* `@mailwoman/spatial`'s `shortCellToInt` via `uprnFullCell` — never reimplemented here.
|
|
6
|
+
* Node reader for `uprn.db`, where a `null` is evidence of absence only inside published coverage, so callers building negative evidence must consult `readLayerCoverage`.
|
|
26
7
|
*/
|
|
27
8
|
|
|
28
9
|
import { allRows } from "@mailwoman/core/utils"
|
|
29
|
-
import { haversineKm, shortCellToInt, type H3Cell } from "@mailwoman/spatial"
|
|
10
|
+
import { haversineKm, shortCellToInt, type GeoCoordinate, type H3Cell } from "@mailwoman/spatial"
|
|
30
11
|
import { DatabaseClient } from "@mailwoman/sqlite/client"
|
|
31
12
|
import { gridDisk } from "h3-js"
|
|
13
|
+
import type { PathBuilderLike } from "path-ts"
|
|
32
14
|
|
|
33
15
|
import type { UPRNDatabase } from "#uprn/schema"
|
|
34
16
|
import { uprnFullCell } from "#uprn/schema"
|
|
35
17
|
/**
|
|
36
|
-
* Conservative
|
|
37
|
-
*
|
|
38
|
-
* distortion shrinks edges by well under the slack this leaves (the true worst is ≈217 m per grid step). Dividing a
|
|
39
|
-
* radius by this over-counts rings and can never miss a cell; multiplying a grid distance by it under-states reach and
|
|
40
|
-
* can never end the ring walk early.
|
|
18
|
+
* Conservative floor, in meters, on the center distance one unit of res-9 grid distance buys,
|
|
19
|
+
* so multiplying a grid distance under-states reach and can never end the ring walk early.
|
|
41
20
|
*/
|
|
42
21
|
const RES9_CENTER_SPACING_FLOOR_M = 150
|
|
43
22
|
|
|
44
23
|
/**
|
|
45
|
-
* Conservative
|
|
46
|
-
*
|
|
24
|
+
* Conservative ceiling, in meters, on a res-9 cell's center-to-vertex distance, so a point
|
|
25
|
+
* within `radiusM` of the query sits in a cell whose center is within `radiusM` plus this.
|
|
47
26
|
*/
|
|
48
27
|
const RES9_CELL_RADIUS_CEILING_M = 300
|
|
49
28
|
|
|
50
29
|
/**
|
|
51
|
-
* Hard ceiling on `radiusM
|
|
52
|
-
*
|
|
30
|
+
* Hard ceiling on `radiusM` that keeps the probe bounded, since a caller wanting a wider
|
|
31
|
+
* search than "which property is this coordinate" has outgrown this reader.
|
|
53
32
|
*/
|
|
54
33
|
export const UPRN_MAX_NEAREST_RADIUS_M = 10_000
|
|
55
34
|
|
|
@@ -58,28 +37,23 @@ export const UPRN_MAX_NEAREST_RADIUS_M = 10_000
|
|
|
58
37
|
*/
|
|
59
38
|
const CELL_PROBE_CHUNK = 900
|
|
60
39
|
|
|
61
|
-
export interface UPRNCoordinate {
|
|
62
|
-
latitude: number
|
|
63
|
-
longitude: number
|
|
64
|
-
}
|
|
65
|
-
|
|
66
40
|
export interface UPRNNearestHit {
|
|
67
41
|
uprn: number
|
|
68
42
|
latitude: number
|
|
69
43
|
longitude: number
|
|
70
44
|
/**
|
|
71
|
-
* Haversine distance from the query point,
|
|
45
|
+
* Haversine distance from the query point, meters.
|
|
72
46
|
*/
|
|
73
47
|
distanceM: number
|
|
74
48
|
}
|
|
75
49
|
|
|
76
50
|
export interface UPRNLookupOpts {
|
|
77
51
|
/**
|
|
78
|
-
* Path to a `uprn.db` built by `mailwoman`'s gazetteer pipeline
|
|
52
|
+
* Path to a `uprn.db` built by `mailwoman`'s gazetteer pipeline, opened read-only.
|
|
79
53
|
*/
|
|
80
|
-
databasePath?:
|
|
54
|
+
databasePath?: PathBuilderLike
|
|
81
55
|
/**
|
|
82
|
-
* Pre-opened handle (tests / shared connections)
|
|
56
|
+
* Pre-opened handle (tests / shared connections), mutually exclusive with `databasePath`.
|
|
83
57
|
*/
|
|
84
58
|
database?: DatabaseClient<UPRNDatabase>
|
|
85
59
|
}
|
|
@@ -91,54 +65,45 @@ interface UPRNRow {
|
|
|
91
65
|
}
|
|
92
66
|
|
|
93
67
|
/**
|
|
94
|
-
* Node reader over `uprn.db
|
|
95
|
-
* precedent as {@link POILookup}.
|
|
68
|
+
* Node reader over `uprn.db`, disposable so callers can take it with `using`.
|
|
96
69
|
*/
|
|
97
70
|
export class UPRNLookup implements Disposable {
|
|
98
71
|
#db: DatabaseClient<UPRNDatabase>
|
|
99
72
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
73
|
+
* Ownership is membership: a connection handed in by a caller is not in here,
|
|
74
|
+
* so disposal cannot reach it.
|
|
102
75
|
*/
|
|
103
|
-
readonly #resources
|
|
76
|
+
readonly #resources: DisposableStack
|
|
104
77
|
|
|
105
|
-
/**
|
|
106
|
-
* `uprn` → its point (rowid-alias PK hit).
|
|
107
|
-
*/
|
|
108
78
|
readonly #coordinateProbe: ReturnType<DatabaseClient["prepare"]>
|
|
109
79
|
|
|
110
80
|
constructor(opts: UPRNLookupOpts) {
|
|
81
|
+
using resources = new DisposableStack()
|
|
82
|
+
|
|
111
83
|
if (opts.database) {
|
|
112
84
|
this.#db = opts.database
|
|
113
85
|
} else if (opts.databasePath) {
|
|
114
|
-
this.#db =
|
|
86
|
+
this.#db = resources.use(new DatabaseClient<UPRNDatabase>(opts.databasePath, { readOnly: true }))
|
|
115
87
|
} else {
|
|
116
88
|
throw new Error("UPRNLookup needs `databasePath` or `database`")
|
|
117
89
|
}
|
|
118
90
|
|
|
119
91
|
this.#coordinateProbe = this.#db.prepare("SELECT lat, lon FROM uprn WHERE uprn = ?")
|
|
92
|
+
this.#resources = resources.move()
|
|
120
93
|
}
|
|
121
94
|
|
|
122
95
|
/**
|
|
123
|
-
* The WGS84 point OS publishes for `uprn`, or `null` when the layer holds no such
|
|
124
|
-
* what that `null` claims).
|
|
96
|
+
* The WGS84 point OS publishes for `uprn`, or `null` when the layer holds no such uprn.
|
|
125
97
|
*/
|
|
126
|
-
coordinateOf(uprn: number):
|
|
98
|
+
coordinateOf(uprn: number): GeoCoordinate | null {
|
|
127
99
|
const row = this.#coordinateProbe.get(uprn) as { lat: number; lon: number } | undefined
|
|
128
100
|
|
|
129
101
|
return row ? { latitude: row.lat, longitude: row.lon } : null
|
|
130
102
|
}
|
|
131
103
|
|
|
132
104
|
/**
|
|
133
|
-
* The single nearest
|
|
134
|
-
*
|
|
135
|
-
* Bounded two ways: `radiusM` is capped at {@link UPRN_MAX_NEAREST_RADIUS_M}, and rings expand outward only until no
|
|
136
|
-
* unprobed cell could beat the best hit found so far (or the radius, when nothing has been found). The stop rule is
|
|
137
|
-
* geometric — a cell at grid distance `g` holds no point nearer than `g` × spacing floor − cell radius, using the
|
|
138
|
-
* same conservative constants the reach math uses — so unlike POILookup's row-count accumulation there is no
|
|
139
|
-
* early-exit ambiguity: a break can never strand a nearer point in an unprobed ring. This is what keeps a
|
|
140
|
-
* capped-radius call over dense ground at milliseconds instead of a full-disk fetch (measured 6.4 s → 13 ms for a 10
|
|
141
|
-
* km radius over central London, 41.6M-row layer; an empty-sea miss at the cap runs the full expansion, 74 ms).
|
|
105
|
+
* The single nearest uprn within `radiusM` meters of the query point, or `null`
|
|
106
|
+
* when no uprn lies inside the radius.
|
|
142
107
|
*
|
|
143
108
|
* @throws {RangeError} When `radiusM` is not a positive finite number, or exceeds the cap.
|
|
144
109
|
*/
|
|
@@ -155,17 +120,14 @@ export class UPRNLookup implements Disposable {
|
|
|
155
120
|
const seenCells = new Set<string>()
|
|
156
121
|
let best: UPRNNearestHit | null = null
|
|
157
122
|
|
|
158
|
-
//
|
|
159
|
-
// RangeError above caps.
|
|
123
|
+
// The loop terminates because the break bound is at most `radiusM`.
|
|
124
|
+
// The RangeError above caps that value.
|
|
160
125
|
for (let ring = 0; ; ring++) {
|
|
161
|
-
//
|
|
162
|
-
// win — the best hit so far, or the radius itself — further rings cannot improve the answer.
|
|
126
|
+
// Once this bound exceeds the best hit so far, or the radius, further rings cannot improve the answer.
|
|
163
127
|
const closestPossibleM = ring * RES9_CENTER_SPACING_FLOOR_M - RES9_CELL_RADIUS_CEILING_M
|
|
164
128
|
|
|
165
129
|
if (closestPossibleM > Math.min(radiusM, best?.distanceM ?? radiusM)) break
|
|
166
130
|
|
|
167
|
-
// gridDisk(origin, ring) returns the WHOLE disk out to `ring`; diffing against what's already been
|
|
168
|
-
// probed derives just this ring's new cells (the POILookup pattern).
|
|
169
131
|
const diskCells = gridDisk(origin, ring) as string[]
|
|
170
132
|
const newCells: number[] = []
|
|
171
133
|
|
|
@@ -180,7 +142,6 @@ export class UPRNLookup implements Disposable {
|
|
|
180
142
|
const chunk = newCells.slice(i, i + CELL_PROBE_CHUNK)
|
|
181
143
|
const placeholders = chunk.map(() => "?").join(", ")
|
|
182
144
|
|
|
183
|
-
// Prepared fresh per chunk arity — a cold, per-call path, same posture as POILookup's batched hydration.
|
|
184
145
|
const rows = allRows<UPRNRow>(
|
|
185
146
|
this.#db.prepare(`SELECT uprn, lat, lon FROM uprn WHERE h3_cell IN (${placeholders})`),
|
|
186
147
|
...chunk
|
|
@@ -189,7 +150,7 @@ export class UPRNLookup implements Disposable {
|
|
|
189
150
|
for (const row of rows) {
|
|
190
151
|
const distanceM = haversineKm(latitude, longitude, row.lat, row.lon) * 1000
|
|
191
152
|
|
|
192
|
-
if (distanceM <= radiusM && (best
|
|
153
|
+
if (distanceM <= radiusM && (!best || distanceM < best.distanceM)) {
|
|
193
154
|
best = { uprn: row.uprn, latitude: row.lat, longitude: row.lon, distanceM }
|
|
194
155
|
}
|
|
195
156
|
}
|
package/lib/uprn/schema.ts
CHANGED
|
@@ -3,51 +3,27 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* Number with its WGS84 point, so mailwoman results can carry UPRN as an interoperability key
|
|
8
|
-
* beside our own `@mailwoman/address-id`. One rowid table keyed `uprn INTEGER PRIMARY KEY` (the
|
|
9
|
-
* rowid alias — the optimal shape for an integer-PK point table; `WITHOUT ROWID` buys nothing
|
|
10
|
-
* here), plus a secondary res-9 `h3_cell` index for the bounded nearest-point probe.
|
|
11
|
-
*
|
|
12
|
-
* ## Coordinates are OS's own WGS84 columns
|
|
13
|
-
*
|
|
14
|
-
* The source CSV publishes BOTH coordinate systems per row — OSGB36 eastings/northings AND WGS84
|
|
15
|
-
* `LATITUDE`/`LONGITUDE`. This layer stores OS's own lat/lon verbatim and never reconverts from
|
|
16
|
-
* eastings: `@mailwoman/spatial`'s `osgb36ToWGS84` is a 7-parameter Helmert with a measured p95 of
|
|
17
|
-
* 4.18 m, and re-deriving what the publisher already computed (with OSTN15, exactly) would replace
|
|
18
|
-
* their answer with a strictly worse one.
|
|
19
|
-
*
|
|
20
|
-
* ## Why `h3_cell` exists at all
|
|
21
|
-
*
|
|
22
|
-
* The layer contract requires every domain row to be addressable by at least one spine key —
|
|
23
|
-
* `writeLayerManifest` throws on a manifest that declares none — and UPRN is its own id space, not
|
|
24
|
-
* H3/WOF/address-id/street. The res-9 short cell (`shortCellToInt`, the same packing as poi.db and
|
|
25
|
-
* the OSM situs extracts) is the spine that fits a point table, and its index doubles as the
|
|
26
|
-
* `nearestUPRN` ring probe.
|
|
27
|
-
*
|
|
28
|
-
* The DB also embeds the layer-contract tables from `@mailwoman/core/layers`; the builder
|
|
29
|
-
* (`packages/mailwoman/lib/gazetteer-pipeline/uprn-layer.ts`) writes the manifest and per-res-6-cell
|
|
30
|
-
* coverage.
|
|
6
|
+
* Typed schema for `uprn.db`, storing OS's own WGS84 latitude/longitude verbatim rather than reconverting from the source's eastings.
|
|
31
7
|
*/
|
|
32
8
|
|
|
33
|
-
import type {
|
|
9
|
+
import type { layerschemadatabase } from "@mailwoman/core/layers"
|
|
34
10
|
import { shortCellToInt, type H3Cell } from "@mailwoman/spatial"
|
|
35
11
|
import { latLngToCell } from "h3-js"
|
|
36
12
|
import type { Kysely } from "kysely"
|
|
37
13
|
|
|
38
14
|
/**
|
|
39
|
-
* Resolution the `uprn` table's `h3_cell` column is keyed at
|
|
40
|
-
* extracts).
|
|
15
|
+
* Resolution the `uprn` table's `h3_cell` column is keyed at, shared with the other layer spines.
|
|
41
16
|
*/
|
|
42
17
|
export const UPRN_H3_RESOLUTION = 9
|
|
43
18
|
|
|
44
19
|
/**
|
|
45
|
-
* Resolution of the layer's `layer_coverage` cells
|
|
20
|
+
* Resolution of the layer's `layer_coverage` cells, coarse per the layer interface
|
|
21
|
+
* and shared with the other spines.
|
|
46
22
|
*/
|
|
47
23
|
export const UPRN_COVERAGE_H3_RESOLUTION = 6
|
|
48
24
|
|
|
49
25
|
/**
|
|
50
|
-
* One
|
|
26
|
+
* One uprn point whose `uprn` is a rowid alias, making the primary probe a rowid B-tree hit.
|
|
51
27
|
*/
|
|
52
28
|
export interface UPRNTable {
|
|
53
29
|
/**
|
|
@@ -55,7 +31,7 @@ export interface UPRNTable {
|
|
|
55
31
|
*/
|
|
56
32
|
uprn: number
|
|
57
33
|
/**
|
|
58
|
-
* WGS84 latitude, as OS published it
|
|
34
|
+
* WGS84 latitude, as OS published it, never reconverted from eastings.
|
|
59
35
|
*/
|
|
60
36
|
lat: number
|
|
61
37
|
/**
|
|
@@ -63,36 +39,38 @@ export interface UPRNTable {
|
|
|
63
39
|
*/
|
|
64
40
|
lon: number
|
|
65
41
|
/**
|
|
66
|
-
* 48-bit short H3 cell at {@link UPRN_H3_RESOLUTION}
|
|
67
|
-
* `nearestUPRN` probe index.
|
|
42
|
+
* 48-bit short H3 cell at {@link UPRN_H3_RESOLUTION} — the layer-interface spine key
|
|
43
|
+
* and the `nearestUPRN` probe index.
|
|
68
44
|
*/
|
|
69
45
|
h3_cell: number
|
|
70
46
|
}
|
|
71
47
|
|
|
72
48
|
/**
|
|
73
|
-
* Build-provenance key/value pairs the fixed `layer_manifest` columns have no room for
|
|
74
|
-
*
|
|
49
|
+
* Build-provenance key/value pairs the fixed `layer_manifest` columns have no room for,
|
|
50
|
+
* including the upstream license text verbatim.
|
|
75
51
|
*/
|
|
76
52
|
export interface UPRNMetaTable {
|
|
77
53
|
key: string
|
|
78
54
|
value: string
|
|
79
55
|
}
|
|
80
56
|
|
|
81
|
-
export interface UPRNDatabase extends
|
|
57
|
+
export interface UPRNDatabase extends layerschemadatabase {
|
|
82
58
|
uprn: UPRNTable
|
|
83
59
|
uprn_meta: UPRNMetaTable
|
|
84
60
|
}
|
|
85
61
|
|
|
86
62
|
/**
|
|
87
|
-
* The full res-9 cell for a UPRN point
|
|
88
|
-
*
|
|
63
|
+
* The full res-9 cell for a UPRN point.
|
|
64
|
+
*
|
|
65
|
+
* This is the one derivation the builder and its consumers share so their cells cannot disagree.
|
|
89
66
|
*/
|
|
90
67
|
export function uprnFullCell(latitude: number, longitude: number): H3Cell {
|
|
91
68
|
return latLngToCell(latitude, longitude, UPRN_H3_RESOLUTION) as H3Cell
|
|
92
69
|
}
|
|
93
70
|
|
|
94
71
|
/**
|
|
95
|
-
* The `h3_cell` column value for a
|
|
72
|
+
* The `h3_cell` column value for a uprn point, packing {@link uprnFullCell} to
|
|
73
|
+
* the shared 48-bit short-cell integer.
|
|
96
74
|
*/
|
|
97
75
|
export function uprnH3Cell(latitude: number, longitude: number): number {
|
|
98
76
|
return shortCellToInt(uprnFullCell(latitude, longitude))
|
|
@@ -117,7 +95,7 @@ export async function createUPRNMetaTable(db: Kysely<UPRNDatabase>): Promise<voi
|
|
|
117
95
|
}
|
|
118
96
|
|
|
119
97
|
/**
|
|
120
|
-
* Secondary index for the `nearestUPRN` ring probe
|
|
98
|
+
* Secondary index for the `nearestUPRN` ring probe, created after the bulk load.
|
|
121
99
|
*/
|
|
122
100
|
export async function createUPRNIndexes(db: Kysely<UPRNDatabase>): Promise<void> {
|
|
123
101
|
await db.schema.createIndex("uprn_h3_cell").on("uprn").column("h3_cell").execute()
|