@nspot/geo-engine 0.3.0 → 0.5.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 +74 -1
- package/dist/cli.js +2 -2
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/localities.d.ts +71 -0
- package/dist/localities.js +87 -0
- package/dist/packs.d.ts +14 -2
- package/dist/packs.js +10 -3
- package/dist/query.d.ts +13 -1
- package/dist/query.js +15 -0
- package/dist/types.d.ts +506 -26
- package/dist/types.js +52 -1
- package/package.json +1 -1
- package/sql/008_regions_bbox.sql +24 -0
- package/sql/009_localities.sql +665 -0
package/dist/types.js
CHANGED
|
@@ -26,6 +26,48 @@ export const packVersionSchema = z
|
|
|
26
26
|
.min(1)
|
|
27
27
|
.max(50)
|
|
28
28
|
.regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "letters, digits, dots, dashes and underscores");
|
|
29
|
+
/**
|
|
30
|
+
* One locality of a pack (schema 9): a named point, keyed by `(country, code)`. Its geometry is
|
|
31
|
+
* a GeoJSON Point (the label point) or null, and exactly one of a point and a non-empty
|
|
32
|
+
* `point_gap` (the pack's reason for having none) is given.
|
|
33
|
+
*/
|
|
34
|
+
export const localityFeatureSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
type: z.literal("Feature"),
|
|
37
|
+
geometry: z
|
|
38
|
+
.object({ type: z.literal("Point"), coordinates: z.array(z.number()).min(2).max(3) })
|
|
39
|
+
.passthrough()
|
|
40
|
+
.nullable(),
|
|
41
|
+
properties: z.object({
|
|
42
|
+
country: countryCodeSchema,
|
|
43
|
+
code: z.string().min(1).max(200),
|
|
44
|
+
name: z.string().min(1).max(200),
|
|
45
|
+
parent: z.string().nullable().optional(),
|
|
46
|
+
parent_country: countryCodeSchema.nullable().optional(),
|
|
47
|
+
point_gap: z.string().nullable().optional(),
|
|
48
|
+
tags: z.array(z.string()).default([]),
|
|
49
|
+
properties: z.record(z.unknown()).default({}),
|
|
50
|
+
}),
|
|
51
|
+
})
|
|
52
|
+
.refine((f) => (f.geometry !== null) !== Boolean(f.properties.point_gap), {
|
|
53
|
+
message: "give a point or a point_gap, not both and not neither",
|
|
54
|
+
});
|
|
55
|
+
const localitiesSectionSchema = z
|
|
56
|
+
.object({ type: z.literal("FeatureCollection"), features: z.array(localityFeatureSchema) })
|
|
57
|
+
.superRefine((fc, ctx) => {
|
|
58
|
+
const seen = new Set();
|
|
59
|
+
fc.features.forEach((f, i) => {
|
|
60
|
+
const key = `${f.properties.country} ${f.properties.code}`;
|
|
61
|
+
if (seen.has(key))
|
|
62
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["features", i], message: "code given twice" });
|
|
63
|
+
seen.add(key);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* A pack document. `regions` may be left out (a pack holding only localities) and reads as an
|
|
68
|
+
* empty collection. `localities` stays absent when absent: a pack without the key leaves the
|
|
69
|
+
* localities it installed alone, while an empty section removes them, so no default is given.
|
|
70
|
+
*/
|
|
29
71
|
export const packSchema = z.object({
|
|
30
72
|
pack: z.object({
|
|
31
73
|
slug: packSlugSchema,
|
|
@@ -54,5 +96,14 @@ export const packSchema = z.object({
|
|
|
54
96
|
properties: z.record(z.unknown()).default({}),
|
|
55
97
|
}),
|
|
56
98
|
})),
|
|
57
|
-
}),
|
|
99
|
+
}).default({ type: "FeatureCollection", features: [] }),
|
|
100
|
+
localities: localitiesSectionSchema.optional(),
|
|
101
|
+
}).superRefine((doc, ctx) => {
|
|
102
|
+
if (doc.localities !== undefined && doc.pack.schema_version < 9) {
|
|
103
|
+
ctx.addIssue({
|
|
104
|
+
code: z.ZodIssueCode.custom,
|
|
105
|
+
path: ["pack", "schema_version"],
|
|
106
|
+
message: "a pack with localities needs schema_version 9 or more",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
58
109
|
});
|
package/package.json
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- 008 — region bounding boxes
|
|
3
|
+
--
|
|
4
|
+
-- geo.regions_bbox(slugs, country) answers "what box holds these regions?" for a
|
|
5
|
+
-- map that fits its camera to them: [west, south, east, north] of the union of
|
|
6
|
+
-- the named regions' envelopes, as jsonb, exact to the coordinate, or NULL when
|
|
7
|
+
-- no slug names a region. Unknown slugs among known ones are ignored. Slugs are
|
|
8
|
+
-- unique per country, not globally: without p_country a slug two countries share
|
|
9
|
+
-- gives the box of both; with it (an ISO code, either case) that country's only.
|
|
10
|
+
-- ============================================================================
|
|
11
|
+
|
|
12
|
+
CREATE OR REPLACE FUNCTION geo.regions_bbox(p_slugs text[], p_country text DEFAULT NULL)
|
|
13
|
+
RETURNS jsonb
|
|
14
|
+
LANGUAGE sql STABLE PARALLEL SAFE AS $$
|
|
15
|
+
SELECT CASE WHEN count(*) = 0 THEN NULL
|
|
16
|
+
ELSE jsonb_build_array(min(ST_XMin(r.geom)), min(ST_YMin(r.geom)), max(ST_XMax(r.geom)), max(ST_YMax(r.geom)))
|
|
17
|
+
END
|
|
18
|
+
FROM geo.regions r
|
|
19
|
+
JOIN geo.countries c ON c.id = r.country_id
|
|
20
|
+
WHERE r.slug = ANY (p_slugs)
|
|
21
|
+
AND (p_country IS NULL OR c.code = upper(p_country));
|
|
22
|
+
$$;
|
|
23
|
+
|
|
24
|
+
SELECT geo.pin_search_path();
|