@mailwoman/corpus 10.4.0 → 10.6.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.
Files changed (96) hide show
  1. package/data/PROVENANCE.md +14 -0
  2. package/data/address-source-register.json +32 -4
  3. package/data/source-resolutions.json +35 -0
  4. package/data/training-manifests/v0.7.2-address-systems.effective.json +1169 -0
  5. package/data/training-manifests/v0.7.2-address-systems.json +550 -0
  6. package/lib/adapters/cordis/adapter.ts +1 -1
  7. package/lib/adapters.ts +2 -0
  8. package/lib/be/adapters/brussels/adapter.ts +1 -1
  9. package/lib/be/adapters/wallonie/adapter.ts +1 -1
  10. package/lib/build/align.ts +1 -1
  11. package/lib/cz/adapters/cuzk/adapter.ts +1 -1
  12. package/lib/cz/tools/fetch/cuzk.ts +2 -2
  13. package/lib/es/tools/fetch/bizkaia.ts +1 -1
  14. package/lib/gb/adapters/os-open/adapter.ts +453 -0
  15. package/lib/gb/os-open/nearest-link.ts +169 -0
  16. package/lib/gb/os-open/open/names.ts +180 -0
  17. package/lib/gb/os-open/open/roads.ts +145 -0
  18. package/lib/gb/tools/fetch/os-open.ts +251 -0
  19. package/lib/nl/tools/fetch/kadaster.ts +1 -1
  20. package/lib/registers.ts +7 -0
  21. package/lib/source-register/effective-manifest.ts +12 -6
  22. package/lib/source-register/training-manifest.ts +30 -2
  23. package/lib/source-register.ts +1 -0
  24. package/lib/subvenue/surfaces.ts +1 -1
  25. package/lib/tools/fetch/gleif.ts +1 -1
  26. package/lib/tools/fetch.ts +2 -0
  27. package/lib/tools/lint/recipe-output.ts +1 -1
  28. package/lib/tools/migrate/source-names.ts +1 -1
  29. package/lib/us/adapters/usgov/nad/adapter.ts +1 -1
  30. package/lib/us/adapters/usgov/nppes/adapter.ts +1 -1
  31. package/out/adapters/cordis/adapter.js +1 -1
  32. package/out/adapters.d.ts.map +1 -1
  33. package/out/adapters.js +2 -0
  34. package/out/adapters.js.map +1 -1
  35. package/out/be/adapters/brussels/adapter.d.ts +1 -1
  36. package/out/be/adapters/brussels/adapter.js +1 -1
  37. package/out/be/adapters/wallonie/adapter.d.ts +1 -1
  38. package/out/be/adapters/wallonie/adapter.js +1 -1
  39. package/out/build/align.d.ts +1 -1
  40. package/out/cz/adapters/cuzk/adapter.d.ts +1 -1
  41. package/out/cz/adapters/cuzk/adapter.js +1 -1
  42. package/out/cz/tools/fetch/cuzk.js +2 -2
  43. package/out/cz/tools/fetch/cuzk.js.map +1 -1
  44. package/out/es/tools/fetch/bizkaia.js +1 -1
  45. package/out/es/tools/fetch/bizkaia.js.map +1 -1
  46. package/out/gb/adapters/os-open/adapter.d.ts +165 -0
  47. package/out/gb/adapters/os-open/adapter.d.ts.map +1 -0
  48. package/out/gb/adapters/os-open/adapter.js +298 -0
  49. package/out/gb/adapters/os-open/adapter.js.map +1 -0
  50. package/out/gb/os-open/nearest-link.d.ts +73 -0
  51. package/out/gb/os-open/nearest-link.d.ts.map +1 -0
  52. package/out/gb/os-open/nearest-link.js +133 -0
  53. package/out/gb/os-open/nearest-link.js.map +1 -0
  54. package/out/gb/os-open/open/names.d.ts +92 -0
  55. package/out/gb/os-open/open/names.d.ts.map +1 -0
  56. package/out/gb/os-open/open/names.js +107 -0
  57. package/out/gb/os-open/open/names.js.map +1 -0
  58. package/out/gb/os-open/open/roads.d.ts +80 -0
  59. package/out/gb/os-open/open/roads.d.ts.map +1 -0
  60. package/out/gb/os-open/open/roads.js +79 -0
  61. package/out/gb/os-open/open/roads.js.map +1 -0
  62. package/out/gb/tools/fetch/os-open.d.ts +91 -0
  63. package/out/gb/tools/fetch/os-open.d.ts.map +1 -0
  64. package/out/gb/tools/fetch/os-open.js +166 -0
  65. package/out/gb/tools/fetch/os-open.js.map +1 -0
  66. package/out/nl/tools/fetch/kadaster.d.ts +1 -1
  67. package/out/nl/tools/fetch/kadaster.js +1 -1
  68. package/out/registers.d.ts +7 -0
  69. package/out/registers.d.ts.map +1 -1
  70. package/out/registers.js +7 -0
  71. package/out/registers.js.map +1 -1
  72. package/out/source-register/effective-manifest.d.ts +9 -3
  73. package/out/source-register/effective-manifest.d.ts.map +1 -1
  74. package/out/source-register/effective-manifest.js +9 -4
  75. package/out/source-register/effective-manifest.js.map +1 -1
  76. package/out/source-register/training-manifest.d.ts +27 -0
  77. package/out/source-register/training-manifest.d.ts.map +1 -1
  78. package/out/source-register/training-manifest.js +13 -1
  79. package/out/source-register/training-manifest.js.map +1 -1
  80. package/out/source-register.d.ts +1 -1
  81. package/out/source-register.d.ts.map +1 -1
  82. package/out/source-register.js +1 -1
  83. package/out/source-register.js.map +1 -1
  84. package/out/subvenue/surfaces.d.ts +1 -1
  85. package/out/tools/fetch/gleif.d.ts +1 -1
  86. package/out/tools/fetch.d.ts +2 -0
  87. package/out/tools/fetch.d.ts.map +1 -1
  88. package/out/tools/fetch.js +2 -0
  89. package/out/tools/fetch.js.map +1 -1
  90. package/out/tools/lint/recipe-output.d.ts +1 -1
  91. package/out/tools/migrate/source-names.d.ts +1 -1
  92. package/out/us/adapters/usgov/nad/adapter.d.ts +1 -1
  93. package/out/us/adapters/usgov/nad/adapter.js +1 -1
  94. package/out/us/adapters/usgov/nppes/adapter.d.ts +1 -1
  95. package/out/us/adapters/usgov/nppes/adapter.js +1 -1
  96. package/package.json +14 -13
@@ -0,0 +1,298 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * `gb-os-open`: Great Britain street addresses joined from three OS OpenData products, each under
7
+ * the Open Government Licence v3.0.
8
+ *
9
+ * - **Code-Point Open** gives every unit postcode in England, Scotland and Wales a grid point.
10
+ * - **OS Open Roads** gives each named road link a centreline and the roadNameTOID of its name.
11
+ * - **OS Open Names** gives each roadNameTOID its `NAME1`, `POSTCODE_DISTRICT` and `POPULATED_PLACE`.
12
+ *
13
+ * Each postcode is joined to the nearest named road link within 100 m, measured point-to-segment in
14
+ * EPSG:27700 meters. The street is the Open Names `NAME1` of that link's TOID, the locality is its
15
+ * `POPULATED_PLACE`. A row carries no region, because the GB layout prints none. Every
16
+ * component is OS data except `house_number`, which no open GB product publishes and which this
17
+ * adapter **draws** from local bands seeded by the postcode and TOID, so each row carries a number the
18
+ * source never stated.
19
+ *
20
+ * A postcode is dropped when:
21
+ *
22
+ * - its Code-Point coordinate has no position, `Positional_quality_indicator` 90;
23
+ * - its coordinate is shared with another postcode, so the point does not locate this postcode's street;
24
+ * - no named link lies within 100 m;
25
+ * - the joined road's `POSTCODE_DISTRICT` differs from the postcode's own outward code.
26
+ *
27
+ * `inputPath` is a directory holding the three products unzipped, each in its own subdirectory:
28
+ * `code-point-open/` (with `Data/CSV/*.csv`), `open-names/` (with `Data/*.csv`) and `open-roads/`
29
+ * (with the `.gml` tiles anywhere beneath it). The Code-Point rows are held in memory, the road
30
+ * tiles stream past them once, and Open Names is read once for the roadNameTOID values the join chose.
31
+ */
32
+ import { formatAddressRow } from "@mailwoman/codex/address/format";
33
+ import { titleCaseGB } from "@mailwoman/codex/gb/place-name";
34
+ import { outwardCode } from "@mailwoman/codex/gb/postcode";
35
+ import { pathExists } from "@mailwoman/core/fs/readers/stat";
36
+ import { stringifyJSON } from "@mailwoman/core/json";
37
+ import { mulberry32 } from "@mailwoman/core/random";
38
+ import { createCodePointParseStats, PQI_NO_COORDINATE, readCodePointCSV } from "@mailwoman/spatial/gb/codepoint";
39
+ import { PathBuilder } from "path-ts";
40
+ import { Globerator } from "spliterator/node/fs";
41
+ import { UnsupportedCountryError } from "#adapters/errors";
42
+ import { stableSourceIDFromParts } from "#adapters/source-id";
43
+ import { NearestLinkJoin } from "#gb/os-open/nearest-link";
44
+ import { createOpenNamesReadStats, populatedPlaceName1, readOpenNamesRoads, } from "#gb/os-open/open/names";
45
+ import { createOpenRoadsReadStats, readOpenRoadsLinks } from "#gb/os-open/open/roads";
46
+ import { SourceRegister } from "#registers";
47
+ import { tieredNumber } from "#synthesizers/utils";
48
+ import { AddressRole, SurfaceOrigin } from "#types";
49
+ /**
50
+ * Registry id for this adapter.
51
+ *
52
+ * Stamped into every row it emits, so a corpus record can be traced back to the dataset it came from.
53
+ */
54
+ export const GB_OS_OPEN_ADAPTER_ID = "gb-os-open";
55
+ /**
56
+ * The license on all three products, and on every row.
57
+ */
58
+ export const GB_OS_OPEN_LICENSE = "OGL-UK-3.0";
59
+ /**
60
+ * The jurisdictions the adapter emits.
61
+ *
62
+ * Code-Point Open covers Great Britain and carries no `BT` postcode.
63
+ */
64
+ export const GB_OS_OPEN_COUNTRIES = ["GB"];
65
+ /**
66
+ * The farthest a road link may lie from a postcode's point and still name its street, in EPSG:27700 meters.
67
+ */
68
+ export const GB_OS_OPEN_JOIN_RADIUS_M = 100;
69
+ /**
70
+ * The subdirectory of `inputPath` that holds each product.
71
+ */
72
+ export const GB_OS_OPEN_PRODUCT_DIRECTORIES = {
73
+ codePointOpen: "code-point-open",
74
+ openNames: "open-names",
75
+ openRoads: "open-roads",
76
+ };
77
+ /**
78
+ * The bands the drawn house number comes from.
79
+ *
80
+ * Seven in ten draws fall in 1–60, a quarter in 61–200, and the rest in 201–400.
81
+ */
82
+ export const GB_OS_OPEN_HOUSE_NUMBER_BANDS = [
83
+ { cutoff: 0.7, base: 1, span: 60 },
84
+ { cutoff: 0.95, base: 61, span: 140 },
85
+ { base: 201, span: 200 },
86
+ ];
87
+ /**
88
+ * The three product directories under `inputPath`.
89
+ *
90
+ * @throws Naming the missing path, when a product's subdirectory is absent.
91
+ */
92
+ export async function gbOSOpenProductPaths(inputPath) {
93
+ const root = PathBuilder.from(inputPath);
94
+ const paths = {
95
+ codePointOpen: root(GB_OS_OPEN_PRODUCT_DIRECTORIES.codePointOpen),
96
+ openNames: root(GB_OS_OPEN_PRODUCT_DIRECTORIES.openNames),
97
+ openRoads: root(GB_OS_OPEN_PRODUCT_DIRECTORIES.openRoads),
98
+ };
99
+ for (const path of Object.values(paths)) {
100
+ if (!(await pathExists(path))) {
101
+ throw new Error(`${GB_OS_OPEN_ADAPTER_ID} adapter: ${path.toString()} is absent; inputPath must hold ${Object.values(GB_OS_OPEN_PRODUCT_DIRECTORIES).join("/, ")}/`);
102
+ }
103
+ }
104
+ return paths;
105
+ }
106
+ /**
107
+ * The files matching `pattern` under `directory`, sorted so a run reads them in one order.
108
+ *
109
+ * @throws When none match, so a wrong layout reports itself rather than reading as an empty product.
110
+ */
111
+ async function productFiles(directory, pattern) {
112
+ const names = await Globerator.from(pattern, { cwd: directory.toString(), absolute: false }).toSorted();
113
+ if (!names.length) {
114
+ throw new Error(`${GB_OS_OPEN_ADAPTER_ID} adapter: ${directory.toString()} holds no file matching ${pattern}`);
115
+ }
116
+ return names.map((name) => directory(name).toString());
117
+ }
118
+ /**
119
+ * The house number drawn for one postcode and road, from {@linkcode GB_OS_OPEN_HOUSE_NUMBER_BANDS}.
120
+ *
121
+ * The seed is the row's source id, so a rerun draws the same number.
122
+ */
123
+ export function drawGBHouseNumber(sourceID) {
124
+ const seed = Number.parseInt(sourceID.slice(-8), 16);
125
+ return tieredNumber(mulberry32(seed), GB_OS_OPEN_HOUSE_NUMBER_BANDS);
126
+ }
127
+ /**
128
+ * The street name as the row prints it.
129
+ *
130
+ * OS writes nearly every road name in mixed case.
131
+ * A name with no lowercase letter is title-cased.
132
+ */
133
+ function streetName(name1) {
134
+ if (/\p{Ll}/u.test(name1))
135
+ return { street: name1, titleCased: false };
136
+ return { street: titleCaseGB(name1), titleCased: true };
137
+ }
138
+ /**
139
+ * Creates the OS OpenData adapter.
140
+ */
141
+ export function createGBOSOpenAdapter(options = {}) {
142
+ return {
143
+ id: GB_OS_OPEN_ADAPTER_ID,
144
+ defaultLicense: GB_OS_OPEN_LICENSE,
145
+ addressRole: AddressRole.Premise,
146
+ register: SourceRegister.OrdnanceSurveyOpenData,
147
+ surface: SurfaceOrigin.Rendered,
148
+ description: "OS OpenData (Code-Point Open, OS Open Roads, OS Open Names): GB postcodes joined to their nearest named road, with a drawn house number.",
149
+ async *rows(opts) {
150
+ if (opts.country && !GB_OS_OPEN_COUNTRIES.includes(opts.country)) {
151
+ throw new UnsupportedCountryError(GB_OS_OPEN_ADAPTER_ID, GB_OS_OPEN_COUNTRIES, opts.country);
152
+ }
153
+ const paths = await gbOSOpenProductPaths(opts.inputPath);
154
+ const codePointFiles = await productFiles(paths.codePointOpen, "Data/CSV/*.csv");
155
+ const roadFiles = await productFiles(paths.openRoads, "**/*.gml");
156
+ const nameFiles = await productFiles(paths.openNames, "Data/*.csv");
157
+ const summary = {
158
+ codePointRows: 0,
159
+ codePointMalformed: 0,
160
+ pqi: {},
161
+ postcodesWithCoordinate: 0,
162
+ joined: 0,
163
+ dropped: {
164
+ noCoordinate: 0,
165
+ sharedCoordinate: 0,
166
+ noLinkWithinRadius: 0,
167
+ noOpenNamesRow: 0,
168
+ districtMismatch: 0,
169
+ },
170
+ emitted: 0,
171
+ titleCasedStreets: 0,
172
+ noLocality: 0,
173
+ bilingualLocality: 0,
174
+ roads: createOpenRoadsReadStats(),
175
+ names: createOpenNamesReadStats(),
176
+ };
177
+ // Code-Point: every postcode with a coordinate, and how many postcodes share each coordinate.
178
+ const codePointStats = createCodePointParseStats();
179
+ const postcodes = [];
180
+ const xs = [];
181
+ const ys = [];
182
+ const pointCounts = new Map();
183
+ for (const file of codePointFiles) {
184
+ for await (const record of readCodePointCSV(file, codePointStats)) {
185
+ postcodes.push(record.postcode);
186
+ xs.push(record.easting);
187
+ ys.push(record.northing);
188
+ const key = `${record.easting},${record.northing}`;
189
+ pointCounts.set(key, (pointCounts.get(key) ?? 0) + 1);
190
+ summary.pqi[record.quality] = (summary.pqi[record.quality] ?? 0) + 1;
191
+ }
192
+ }
193
+ summary.codePointRows = codePointStats.read;
194
+ summary.codePointMalformed = codePointStats.skippedMalformed;
195
+ summary.dropped.noCoordinate = codePointStats.skippedNoCoordinate;
196
+ summary.postcodesWithCoordinate = postcodes.length;
197
+ if (codePointStats.skippedNoCoordinate > 0) {
198
+ summary.pqi[PQI_NO_COORDINATE] = codePointStats.skippedNoCoordinate;
199
+ }
200
+ // Open Roads: stream every named link past the postcodes once.
201
+ const join = new NearestLinkJoin({ xs: Float64Array.from(xs), ys: Float64Array.from(ys) }, GB_OS_OPEN_JOIN_RADIUS_M);
202
+ for (const file of roadFiles) {
203
+ for await (const link of readOpenRoadsLinks(file, summary.roads, { signal: opts.signal })) {
204
+ join.offer({ roadNameTOID: link.roadNameTOID }, link.vertices);
205
+ }
206
+ }
207
+ const wanted = new Set();
208
+ for (let i = 0; i < postcodes.length; i++) {
209
+ const match = join.match(i);
210
+ if (!match)
211
+ continue;
212
+ summary.joined++;
213
+ wanted.add(match.link.roadNameTOID);
214
+ }
215
+ // Open Names: only the roads the join chose.
216
+ const roads = new Map();
217
+ for (const file of nameFiles) {
218
+ for await (const road of readOpenNamesRoads(file, summary.names, wanted)) {
219
+ roads.set(road.id, road);
220
+ }
221
+ }
222
+ try {
223
+ for (let i = 0; i < postcodes.length; i++) {
224
+ if (opts.signal?.aborted)
225
+ break;
226
+ if (opts.limit !== undefined && summary.emitted >= opts.limit)
227
+ break;
228
+ const postcode = postcodes[i];
229
+ if ((pointCounts.get(`${xs[i]},${ys[i]}`) ?? 0) > 1) {
230
+ summary.dropped.sharedCoordinate++;
231
+ continue;
232
+ }
233
+ const match = join.match(i);
234
+ if (!match) {
235
+ summary.dropped.noLinkWithinRadius++;
236
+ continue;
237
+ }
238
+ const road = roads.get(match.link.roadNameTOID);
239
+ if (!road) {
240
+ summary.dropped.noOpenNamesRow++;
241
+ continue;
242
+ }
243
+ if (road.postcodeDistrict !== outwardCode(postcode)) {
244
+ summary.dropped.districtMismatch++;
245
+ continue;
246
+ }
247
+ const { street, titleCased } = streetName(road.name1);
248
+ const locality = populatedPlaceName1(road.populatedPlace);
249
+ const sourceID = stableSourceIDFromParts(GB_OS_OPEN_ADAPTER_ID, {
250
+ postcode,
251
+ roadNameTOID: road.id,
252
+ });
253
+ const components = {
254
+ house_number: drawGBHouseNumber(sourceID),
255
+ street,
256
+ };
257
+ if (locality) {
258
+ components.locality = locality;
259
+ }
260
+ components.postcode = postcode;
261
+ const rendered = formatAddressRow(components, "GB", { singleLine: true });
262
+ if (!rendered)
263
+ continue;
264
+ if (titleCased) {
265
+ summary.titleCasedStreets++;
266
+ }
267
+ if (!locality) {
268
+ summary.noLocality++;
269
+ }
270
+ if (road.populatedPlace.includes(" / ")) {
271
+ summary.bilingualLocality++;
272
+ }
273
+ yield {
274
+ raw: rendered.raw,
275
+ components: rendered.components,
276
+ country: "GB",
277
+ locale: "en-GB",
278
+ source: GB_OS_OPEN_ADAPTER_ID,
279
+ source_id: sourceID,
280
+ corpus_version: "",
281
+ license: GB_OS_OPEN_LICENSE,
282
+ };
283
+ summary.emitted++;
284
+ }
285
+ }
286
+ finally {
287
+ options.onSummary?.(summary);
288
+ process.stderr.write(` ${GB_OS_OPEN_ADAPTER_ID}: ${summary.emitted} emitted of ${summary.codePointRows} Code-Point rows; ` +
289
+ `dropped ${stringifyJSON(summary.dropped)}\n`);
290
+ }
291
+ },
292
+ };
293
+ }
294
+ /**
295
+ * The configured adapter instance registered with the corpus builder.
296
+ */
297
+ export const gbOSOpenAdapter = createGBOSOpenAdapter();
298
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../../lib/gb/adapters/os-open/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * The nearest line feature to each of a set of points, measured point-to-segment in a planar
7
+ * projection.
8
+ *
9
+ * The points sit in a bucket grid and the lines stream past it. Each segment of each line asks the
10
+ * grid for the points inside its bounding box widened by the search radius, and each of those
11
+ * points keeps the nearest line it has been offered. Memory is bounded by the point count rather
12
+ * than by the line count, so a whole country's road network can be read once from disk.
13
+ *
14
+ * Distances are Euclidean in the projection's own units. For EPSG:27700, the British National
15
+ * Grid, those are meters, and the grid's scale error stays under 0.04% across Great Britain.
16
+ */
17
+ /**
18
+ * A uniform grid of point indices, keyed by cell.
19
+ */
20
+ export declare class PointBucketIndex {
21
+ #private;
22
+ readonly cellSize: number;
23
+ constructor(cellSize: number);
24
+ /**
25
+ * Files the point `index` under the cell holding `(x, y)`.
26
+ */
27
+ add(index: number, x: number, y: number): void;
28
+ /**
29
+ * Every point index filed in a cell that the box touches.
30
+ *
31
+ * The answer holds every point of each touched cell, including points up to one cell outside the box.
32
+ * A caller measures each one.
33
+ */
34
+ within(minX: number, minY: number, maxX: number, maxY: number): Generator<number>;
35
+ }
36
+ /**
37
+ * The line a point was joined to, and how far away it lies.
38
+ */
39
+ export interface NearestLinkMatch<T> {
40
+ link: T;
41
+ /**
42
+ * The point-to-segment distance in projection units.
43
+ */
44
+ distance: number;
45
+ }
46
+ /**
47
+ * Joins each point to the nearest line offered to it within `radius`.
48
+ *
49
+ * A line offered at exactly the same distance as the current nearest does not replace it,
50
+ * so the first-offered line wins a tie and the answer follows the order the lines are read in.
51
+ */
52
+ export declare class NearestLinkJoin<T> {
53
+ #private;
54
+ readonly radius: number;
55
+ /**
56
+ * @param points The points to join, as parallel coordinate arrays.
57
+ * A point's position in the arrays is its index in every answer.
58
+ * @param radius The largest distance at which a line joins, in projection units.
59
+ */
60
+ constructor(points: {
61
+ xs: Float64Array;
62
+ ys: Float64Array;
63
+ }, radius: number);
64
+ /**
65
+ * Offers one line, as a flat `[x0, y0, x1, y1, …]` vertex array, to every point within reach of it.
66
+ */
67
+ offer(link: T, vertices: ArrayLike<number>): void;
68
+ /**
69
+ * The nearest line offered to point `index`, or null when none came within the radius.
70
+ */
71
+ match(index: number): NearestLinkMatch<T> | null;
72
+ }
73
+ //# sourceMappingURL=nearest-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nearest-link.d.ts","sourceRoot":"","sources":["../../../lib/gb/os-open/nearest-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAYH;;GAEG;AACH,qBAAa,gBAAgB;;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IAIzB,YAAY,QAAQ,EAAE,MAAM,EAI3B;IAMD;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAS7C;IAED;;;;;OAKG;IACF,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAejF;CACD;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAClC,IAAI,EAAE,CAAC,CAAA;IACP;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;CAChB;AAED;;;;;GAKG;AACH,qBAAa,eAAe,CAAC,CAAC;;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IAQvB;;;;OAIG;IACH,YAAY,MAAM,EAAE;QAAE,EAAE,EAAE,YAAY,CAAC;QAAC,EAAE,EAAE,YAAY,CAAA;KAAE,EAAE,MAAM,EAAE,MAAM,EAezE;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,CAwBhD;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI,CAI/C;CACD"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * The nearest line feature to each of a set of points, measured point-to-segment in a planar
7
+ * projection.
8
+ *
9
+ * The points sit in a bucket grid and the lines stream past it. Each segment of each line asks the
10
+ * grid for the points inside its bounding box widened by the search radius, and each of those
11
+ * points keeps the nearest line it has been offered. Memory is bounded by the point count rather
12
+ * than by the line count, so a whole country's road network can be read once from disk.
13
+ *
14
+ * Distances are Euclidean in the projection's own units. For EPSG:27700, the British National
15
+ * Grid, those are meters, and the grid's scale error stays under 0.04% across Great Britain.
16
+ */
17
+ import { planarSegmentDistanceSquared } from "@mailwoman/spatial/distance";
18
+ /**
19
+ * The multiplier that packs a cell's column and row into one numeric key.
20
+ *
21
+ * A row index must stay below it.
22
+ * With 100 m cells, 2^21 rows span 209,715 km, so a projected coordinate in meters cannot reach it.
23
+ */
24
+ const CELL_ROW_SPAN = 2 ** 21;
25
+ /**
26
+ * A uniform grid of point indices, keyed by cell.
27
+ */
28
+ export class PointBucketIndex {
29
+ cellSize;
30
+ #cells = new Map();
31
+ constructor(cellSize) {
32
+ if (!(cellSize > 0))
33
+ throw new RangeError(`PointBucketIndex: cell size must be positive, got ${cellSize}`);
34
+ this.cellSize = cellSize;
35
+ }
36
+ #key(column, row) {
37
+ return column * CELL_ROW_SPAN + row;
38
+ }
39
+ /**
40
+ * Files the point `index` under the cell holding `(x, y)`.
41
+ */
42
+ add(index, x, y) {
43
+ const key = this.#key(Math.floor(x / this.cellSize), Math.floor(y / this.cellSize));
44
+ const bucket = this.#cells.get(key);
45
+ if (bucket) {
46
+ bucket.push(index);
47
+ }
48
+ else {
49
+ this.#cells.set(key, [index]);
50
+ }
51
+ }
52
+ /**
53
+ * Every point index filed in a cell that the box touches.
54
+ *
55
+ * The answer holds every point of each touched cell, including points up to one cell outside the box.
56
+ * A caller measures each one.
57
+ */
58
+ *within(minX, minY, maxX, maxY) {
59
+ const firstColumn = Math.floor(minX / this.cellSize);
60
+ const lastColumn = Math.floor(maxX / this.cellSize);
61
+ const firstRow = Math.floor(minY / this.cellSize);
62
+ const lastRow = Math.floor(maxY / this.cellSize);
63
+ for (let column = firstColumn; column <= lastColumn; column++) {
64
+ for (let row = firstRow; row <= lastRow; row++) {
65
+ const bucket = this.#cells.get(this.#key(column, row));
66
+ if (bucket) {
67
+ yield* bucket;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ /**
74
+ * Joins each point to the nearest line offered to it within `radius`.
75
+ *
76
+ * A line offered at exactly the same distance as the current nearest does not replace it,
77
+ * so the first-offered line wins a tie and the answer follows the order the lines are read in.
78
+ */
79
+ export class NearestLinkJoin {
80
+ radius;
81
+ #xs;
82
+ #ys;
83
+ #bestSquared;
84
+ #best;
85
+ #index;
86
+ /**
87
+ * @param points The points to join, as parallel coordinate arrays.
88
+ * A point's position in the arrays is its index in every answer.
89
+ * @param radius The largest distance at which a line joins, in projection units.
90
+ */
91
+ constructor(points, radius) {
92
+ if (points.xs.length !== points.ys.length) {
93
+ throw new RangeError(`NearestLinkJoin: ${points.xs.length} x values against ${points.ys.length} y values`);
94
+ }
95
+ this.radius = radius;
96
+ this.#xs = points.xs;
97
+ this.#ys = points.ys;
98
+ this.#bestSquared = new Float64Array(points.xs.length).fill(Number.POSITIVE_INFINITY);
99
+ this.#best = Array.from({ length: points.xs.length });
100
+ this.#index = new PointBucketIndex(radius);
101
+ for (let i = 0; i < points.xs.length; i++) {
102
+ this.#index.add(i, points.xs[i], points.ys[i]);
103
+ }
104
+ }
105
+ /**
106
+ * Offers one line, as a flat `[x0, y0, x1, y1, …]` vertex array, to every point within reach of it.
107
+ */
108
+ offer(link, vertices) {
109
+ const radius = this.radius;
110
+ const radiusSquared = radius * radius;
111
+ for (let v = 0; v + 3 < vertices.length; v += 2) {
112
+ const ax = vertices[v];
113
+ const ay = vertices[v + 1];
114
+ const bx = vertices[v + 2];
115
+ const by = vertices[v + 3];
116
+ for (const i of this.#index.within(Math.min(ax, bx) - radius, Math.min(ay, by) - radius, Math.max(ax, bx) + radius, Math.max(ay, by) + radius)) {
117
+ const squared = planarSegmentDistanceSquared(this.#xs[i], this.#ys[i], ax, ay, bx, by);
118
+ if (squared <= radiusSquared && squared < this.#bestSquared[i]) {
119
+ this.#bestSquared[i] = squared;
120
+ this.#best[i] = link;
121
+ }
122
+ }
123
+ }
124
+ }
125
+ /**
126
+ * The nearest line offered to point `index`, or null when none came within the radius.
127
+ */
128
+ match(index) {
129
+ const link = this.#best[index];
130
+ return link === undefined ? null : { link, distance: Math.sqrt(this.#bestSquared[index]) };
131
+ }
132
+ }
133
+ //# sourceMappingURL=nearest-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nearest-link.js","sourceRoot":"","sources":["../../../lib/gb/os-open/nearest-link.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Reads the road rows of OS Open Names.
7
+ *
8
+ * The CSV product is 819 files, one per 10 km National Grid square, with no header row. OS ships
9
+ * the 34 column names in `Doc/OS_Open_Names_Header.csv`, and the first data row of each file opens
10
+ * with a byte-order mark, which this reader strips from the `ID` value.
11
+ *
12
+ * A road row's `ID` is the roadNameTOID that OS Open Roads links reference, such as
13
+ * `osgb4000000010927776`. Two `LOCAL_TYPE` values are roads: `Named Road`, and
14
+ * `Section Of Named Road` for a road that OS splits where it crosses a postcode district or a
15
+ * settlement.
16
+ */
17
+ /**
18
+ * The 34 columns of an OS Open Names CSV, in file order, as `Doc/OS_Open_Names_Header.csv` names them.
19
+ */
20
+ export declare const OPEN_NAMES_COLUMNS: readonly ["ID", "NAMES_URI", "NAME1", "NAME1_LANG", "NAME2", "NAME2_LANG", "TYPE", "LOCAL_TYPE", "GEOMETRY_X", "GEOMETRY_Y", "MOST_DETAIL_VIEW_RES", "LEAST_DETAIL_VIEW_RES", "MBR_XMIN", "MBR_YMIN", "MBR_XMAX", "MBR_YMAX", "POSTCODE_DISTRICT", "POSTCODE_DISTRICT_URI", "POPULATED_PLACE", "POPULATED_PLACE_URI", "POPULATED_PLACE_TYPE", "DISTRICT_BOROUGH", "DISTRICT_BOROUGH_URI", "DISTRICT_BOROUGH_TYPE", "COUNTY_UNITARY", "COUNTY_UNITARY_URI", "COUNTY_UNITARY_TYPE", "REGION", "REGION_URI", "COUNTRY", "COUNTRY_URI", "RELATED_SPATIAL_OBJECT", "SAME_AS_DBPEDIA", "SAME_AS_GEONAMES"];
21
+ /**
22
+ * The `LOCAL_TYPE` values that mark a road row.
23
+ */
24
+ export declare const OPEN_NAMES_ROAD_TYPES: ReadonlySet<string>;
25
+ /**
26
+ * One road row of OS Open Names, with the columns the corpus adapter reads.
27
+ */
28
+ export interface OpenNamesRoad {
29
+ /**
30
+ * The roadNameTOID, such as `osgb4000000010927776`.
31
+ */
32
+ id: string;
33
+ /**
34
+ * The road's name, in the language OS lists first.
35
+ */
36
+ name1: string;
37
+ /**
38
+ * The road's other-language name, Welsh or Gaelic, or an empty string.
39
+ */
40
+ name2: string;
41
+ /**
42
+ * The outward code of the postcode district holding the road, such as `LS6`.
43
+ */
44
+ postcodeDistrict: string;
45
+ /**
46
+ * The settlement holding the road, or an empty string.
47
+ *
48
+ * A bilingual settlement is written `<NAME1> / <NAME2>` of that settlement,
49
+ * such as `Stornoway / Steòrnabhagh`.
50
+ */
51
+ populatedPlace: string;
52
+ districtBorough: string;
53
+ countyUnitary: string;
54
+ }
55
+ /**
56
+ * Counts from a read, with a reason for each row that did not yield.
57
+ */
58
+ export interface OpenNamesReadStats {
59
+ /**
60
+ * Rows read across every file.
61
+ */
62
+ read: number;
63
+ /**
64
+ * Road rows yielded.
65
+ */
66
+ roads: number;
67
+ /**
68
+ * Road rows skipped because their `ID` was not among the requested ids.
69
+ */
70
+ unrequested: number;
71
+ /**
72
+ * Rows with a column count other than 34.
73
+ */
74
+ malformed: number;
75
+ }
76
+ /**
77
+ * A stats object with every count at zero.
78
+ */
79
+ export declare function createOpenNamesReadStats(): OpenNamesReadStats;
80
+ /**
81
+ * Streams the road rows of one Open Names CSV.
82
+ *
83
+ * @param ids When given, only roads whose `ID` is in the set are yielded.
84
+ */
85
+ export declare function readOpenNamesRoads(csvPath: string, stats: OpenNamesReadStats, ids?: ReadonlySet<string>): AsyncGenerator<OpenNamesRoad>;
86
+ /**
87
+ * The settlement's first-listed name from a `POPULATED_PLACE` value.
88
+ *
89
+ * OS writes a bilingual settlement as `<NAME1> / <NAME2>`, and NAME1 is the form the adapter emits.
90
+ */
91
+ export declare function populatedPlaceName1(populatedPlace: string): string;
92
+ //# sourceMappingURL=names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../../../lib/gb/os-open/open/names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH;;GAEG;AACH,eAAO,MAAM,kBAAkB,YAC9B,IAAI,EACJ,WAAW,EACX,OAAO,EACP,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,aAAa,EACb,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,CACT,CAAA;AAOV;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,MAAM,CAAoD,CAAA;AAE1G;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAA;IACV;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;CACjB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,kBAAkB,CAE7D;AAED;;;;GAIG;AACH,wBAAuB,kBAAkB,CACxC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,kBAAkB,EACzB,GAAG,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GACvB,cAAc,CAAC,aAAa,CAAC,CAgC/B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAElE"}