@motionscript/geography 0.1.0-alpha.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/CREDITS.md +8 -8
- package/LICENSE +201 -201
- package/NOTICE +49 -49
- package/README.md +149 -149
- package/dist/border/format.js.map +1 -1
- package/dist/border/geo-border.js.map +1 -1
- package/dist/border/geometry.js.map +1 -1
- package/dist/border/index.d.ts +1 -1
- package/dist/border/index.js +1 -1
- package/dist/border/index.js.map +1 -1
- package/dist/border/loader.js.map +1 -1
- package/dist/border/registry.js.map +1 -1
- package/dist/border/selection.js.map +1 -1
- package/dist/border/simplify.js.map +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/manifest.json +1 -1
- package/dist/index.js.map +1 -1
- package/dist/nodes.js.map +1 -1
- package/package.json +4 -4
- package/registry.json +16 -16
- package/src/border/format.ts +147 -147
- package/src/border/geo-border.ts +268 -268
- package/src/border/geometry.ts +463 -463
- package/src/border/index.ts +14 -14
- package/src/border/loader.ts +97 -97
- package/src/border/registry.ts +201 -201
- package/src/border/selection.ts +95 -95
- package/src/border/simplify.ts +110 -110
- package/src/index.ts +2 -2
- package/src/nodes.ts +16 -16
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC","sourcesContent":["export * from \"./border\";\
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC","sourcesContent":["export * from \"./border\";\nexport { NODES } from \"./nodes\";\n"]}
|
package/dist/nodes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodes.js","sourceRoot":"","sources":["../src/nodes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,yGAAyG;AACzG,MAAM,CAAC,MAAM,KAAK,GAAyB,CAAC,SAAS,CAAC,CAAC","sourcesContent":["import { GeoBorder } from \"./border\";\
|
|
1
|
+
{"version":3,"file":"nodes.js","sourceRoot":"","sources":["../src/nodes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,yGAAyG;AACzG,MAAM,CAAC,MAAM,KAAK,GAAyB,CAAC,SAAS,CAAC,CAAC","sourcesContent":["import { GeoBorder } from \"./border\";\n\n/**\n * Every node type this package publishes.\n *\n * A host registers these by handing the array to an engine —\n * `new Engine(platform, { nodes: NODES })` — which reads each component's key.\n * Defining a component only *declares* that key: nothing is registered by\n * importing a module, so a registry holds exactly what its owner asked for.\n *\n * Listing the component **values** is also what survives bundling: this package\n * declares `sideEffects: false`, and a document names a node type by string, so\n * an import kept only for its side effect is exactly what gets shaken out.\n */\n// Typed by name, so the declaration names each component rather than spelling out its type through core.\nexport const NODES: (typeof GeoBorder)[] = [GeoBorder];\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@motionscript/geography",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Geography for Motion Script: GeoBorder, and the country and state outlines it draws by ISO 3166 code, derived from Natural Earth (public domain).",
|
|
6
6
|
"repository": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"license": "Apache-2.0",
|
|
52
52
|
"author": "Motionscript",
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@motionscript/sdk": "^0.1.0
|
|
54
|
+
"@motionscript/sdk": "^0.1.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/d3-geo": "^3.1.0",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"typescript": "^6.0.3",
|
|
68
68
|
"typescript-eslint": "^8.48.0",
|
|
69
69
|
"vitest": "^4.1.5",
|
|
70
|
-
"@motionscript/
|
|
71
|
-
"@motionscript/
|
|
70
|
+
"@motionscript/core": "0.1.0",
|
|
71
|
+
"@motionscript/sdk": "0.1.0"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
74
|
"d3-geo": "^3.1.1"
|
package/registry.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
{
|
|
2
|
-
"kit": {
|
|
3
|
-
"barrel": "src/index.ts",
|
|
4
|
-
"specifier": "@motionscript/geography"
|
|
5
|
-
},
|
|
6
|
-
"items": [
|
|
7
|
-
{
|
|
8
|
-
"name": "geo-border",
|
|
9
|
-
"description": "Country and state outlines by ISO 3166 code, with adjustable detail.",
|
|
10
|
-
"root": "src/border",
|
|
11
|
-
"files": [
|
|
12
|
-
"geo-border.ts"
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
]
|
|
16
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"kit": {
|
|
3
|
+
"barrel": "src/index.ts",
|
|
4
|
+
"specifier": "@motionscript/geography"
|
|
5
|
+
},
|
|
6
|
+
"items": [
|
|
7
|
+
{
|
|
8
|
+
"name": "geo-border",
|
|
9
|
+
"description": "Country and state outlines by ISO 3166 code, with adjustable detail.",
|
|
10
|
+
"root": "src/border",
|
|
11
|
+
"files": [
|
|
12
|
+
"geo-border.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
package/src/border/format.ts
CHANGED
|
@@ -1,147 +1,147 @@
|
|
|
1
|
-
import { geoArea } from "d3-geo"
|
|
2
|
-
|
|
3
|
-
/** The on-disk shape `@motionscript/geography` writes; see its `generate.mjs`. */
|
|
4
|
-
export const GEO_DATA_FORMAT = 1
|
|
5
|
-
|
|
6
|
-
export interface GeoFileData {
|
|
7
|
-
format: number
|
|
8
|
-
source?: string
|
|
9
|
-
transform: { scale: [number, number]; translate: [number, number] }
|
|
10
|
-
/** Global arc id per local arc — identical across files for a shared border. */
|
|
11
|
-
arcIds: number[]
|
|
12
|
-
/** Per arc, flat `[x0, y0, w0, dx, dy, w, …]`: delta-encoded quantized lon/lat and a weight bucket. */
|
|
13
|
-
arcs: number[][]
|
|
14
|
-
features: GeoFeatureData[]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface GeoFeatureData {
|
|
18
|
-
code: string
|
|
19
|
-
name: string
|
|
20
|
-
nameNl?: string
|
|
21
|
-
parent: string | null
|
|
22
|
-
first: boolean
|
|
23
|
-
listed: boolean
|
|
24
|
-
remote?: boolean
|
|
25
|
-
area: number
|
|
26
|
-
mainland: number[] | null
|
|
27
|
-
areas: number[]
|
|
28
|
-
/** Polygons → rings → arc refs (`~i` is arc `i` reversed). */
|
|
29
|
-
polygons: number[][][]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** `[code, name, nameNl, parent, first]` per listed region. */
|
|
33
|
-
export type GeoIndexEntry = [string, string, string, string | null, 0 | 1]
|
|
34
|
-
|
|
35
|
-
export interface GeoIndex {
|
|
36
|
-
format: number
|
|
37
|
-
source?: string
|
|
38
|
-
regions: GeoIndexEntry[]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface GeoArc {
|
|
42
|
-
/** Identity for de-duplicating a border drawn by two regions, across files. */
|
|
43
|
-
key: number | string
|
|
44
|
-
/** Interleaved lon/lat, in degrees. */
|
|
45
|
-
points: Float64Array
|
|
46
|
-
/** Per point: 0 always survives, higher buckets are less significant; see `thresholdBucket`. */
|
|
47
|
-
buckets: Uint8Array
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface GeoRingRef {
|
|
51
|
-
arc: GeoArc
|
|
52
|
-
reversed: boolean
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface GeoPolygon {
|
|
56
|
-
rings: GeoRingRef[][]
|
|
57
|
-
/** Per ring, in steradians — exterior first, then holes. */
|
|
58
|
-
ringAreas: number[]
|
|
59
|
-
area: number
|
|
60
|
-
mainland: boolean
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export interface GeoFeature {
|
|
64
|
-
code: string
|
|
65
|
-
name: string
|
|
66
|
-
nameNl: string
|
|
67
|
-
parent: string | null
|
|
68
|
-
first: boolean
|
|
69
|
-
listed: boolean
|
|
70
|
-
remote: boolean
|
|
71
|
-
polygons: GeoPolygon[]
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface DecodedGeoFile {
|
|
75
|
-
features: Map<string, GeoFeature>
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function decodeArc(encoded: number[], key: number, [kx, ky]: [number, number], [tx, ty]: [number, number]): GeoArc {
|
|
79
|
-
const count = encoded.length / 3
|
|
80
|
-
const points = new Float64Array(count * 2)
|
|
81
|
-
const buckets = new Uint8Array(count)
|
|
82
|
-
let x = 0
|
|
83
|
-
let y = 0
|
|
84
|
-
for (let i = 0; i < count; i++) {
|
|
85
|
-
x += encoded[i * 3]
|
|
86
|
-
y += encoded[i * 3 + 1]
|
|
87
|
-
points[i * 2] = x * kx + tx
|
|
88
|
-
points[i * 2 + 1] = y * ky + ty
|
|
89
|
-
buckets[i] = encoded[i * 3 + 2]
|
|
90
|
-
}
|
|
91
|
-
return { key, points, buckets }
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function ringCoordinates(ring: readonly GeoRingRef[]): [number, number][] {
|
|
95
|
-
const out: [number, number][] = []
|
|
96
|
-
for (const { arc, reversed } of ring) {
|
|
97
|
-
const n = arc.buckets.length
|
|
98
|
-
for (let j = out.length === 0 ? 0 : 1; j < n; j++) {
|
|
99
|
-
const i = reversed ? n - 1 - j : j
|
|
100
|
-
out.push([arc.points[i * 2], arc.points[i * 2 + 1]])
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return out
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** A ring's area in steradians, whichever way it winds. */
|
|
107
|
-
export function ringArea(ring: readonly GeoRingRef[]): number {
|
|
108
|
-
const coordinates = ringCoordinates(ring)
|
|
109
|
-
if (coordinates.length < 4) return 0
|
|
110
|
-
const area = geoArea({ type: "Polygon", coordinates: [coordinates] })
|
|
111
|
-
return area > 2 * Math.PI ? 4 * Math.PI - area : area
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function decodeGeoFile(data: GeoFileData): DecodedGeoFile {
|
|
115
|
-
if (data.format !== GEO_DATA_FORMAT) {
|
|
116
|
-
throw new Error(`geo data format ${data.format} is not supported (expected ${GEO_DATA_FORMAT})`)
|
|
117
|
-
}
|
|
118
|
-
const arcs = data.arcs.map((encoded, i) =>
|
|
119
|
-
decodeArc(encoded, data.arcIds[i], data.transform.scale, data.transform.translate)
|
|
120
|
-
)
|
|
121
|
-
const ref = (index: number): GeoRingRef =>
|
|
122
|
-
index < 0 ? { arc: arcs[~index], reversed: true } : { arc: arcs[index], reversed: false }
|
|
123
|
-
|
|
124
|
-
const features = new Map<string, GeoFeature>()
|
|
125
|
-
for (const f of data.features) {
|
|
126
|
-
const mainland = f.mainland ? new Set(f.mainland) : null
|
|
127
|
-
features.set(f.code, {
|
|
128
|
-
code: f.code,
|
|
129
|
-
name: f.name,
|
|
130
|
-
nameNl: f.nameNl ?? f.name,
|
|
131
|
-
parent: f.parent,
|
|
132
|
-
first: f.first,
|
|
133
|
-
listed: f.listed,
|
|
134
|
-
remote: f.remote === true,
|
|
135
|
-
polygons: f.polygons.map((rings, i) => {
|
|
136
|
-
const resolved = rings.map((ring) => ring.map(ref))
|
|
137
|
-
return {
|
|
138
|
-
rings: resolved,
|
|
139
|
-
ringAreas: resolved.map((ring, r) => (r === 0 ? f.areas[i] : ringArea(ring))),
|
|
140
|
-
area: f.areas[i],
|
|
141
|
-
mainland: mainland === null || mainland.has(i),
|
|
142
|
-
}
|
|
143
|
-
}),
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
return { features }
|
|
147
|
-
}
|
|
1
|
+
import { geoArea } from "d3-geo"
|
|
2
|
+
|
|
3
|
+
/** The on-disk shape `@motionscript/geography` writes; see its `generate.mjs`. */
|
|
4
|
+
export const GEO_DATA_FORMAT = 1
|
|
5
|
+
|
|
6
|
+
export interface GeoFileData {
|
|
7
|
+
format: number
|
|
8
|
+
source?: string
|
|
9
|
+
transform: { scale: [number, number]; translate: [number, number] }
|
|
10
|
+
/** Global arc id per local arc — identical across files for a shared border. */
|
|
11
|
+
arcIds: number[]
|
|
12
|
+
/** Per arc, flat `[x0, y0, w0, dx, dy, w, …]`: delta-encoded quantized lon/lat and a weight bucket. */
|
|
13
|
+
arcs: number[][]
|
|
14
|
+
features: GeoFeatureData[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface GeoFeatureData {
|
|
18
|
+
code: string
|
|
19
|
+
name: string
|
|
20
|
+
nameNl?: string
|
|
21
|
+
parent: string | null
|
|
22
|
+
first: boolean
|
|
23
|
+
listed: boolean
|
|
24
|
+
remote?: boolean
|
|
25
|
+
area: number
|
|
26
|
+
mainland: number[] | null
|
|
27
|
+
areas: number[]
|
|
28
|
+
/** Polygons → rings → arc refs (`~i` is arc `i` reversed). */
|
|
29
|
+
polygons: number[][][]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `[code, name, nameNl, parent, first]` per listed region. */
|
|
33
|
+
export type GeoIndexEntry = [string, string, string, string | null, 0 | 1]
|
|
34
|
+
|
|
35
|
+
export interface GeoIndex {
|
|
36
|
+
format: number
|
|
37
|
+
source?: string
|
|
38
|
+
regions: GeoIndexEntry[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface GeoArc {
|
|
42
|
+
/** Identity for de-duplicating a border drawn by two regions, across files. */
|
|
43
|
+
key: number | string
|
|
44
|
+
/** Interleaved lon/lat, in degrees. */
|
|
45
|
+
points: Float64Array
|
|
46
|
+
/** Per point: 0 always survives, higher buckets are less significant; see `thresholdBucket`. */
|
|
47
|
+
buckets: Uint8Array
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface GeoRingRef {
|
|
51
|
+
arc: GeoArc
|
|
52
|
+
reversed: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface GeoPolygon {
|
|
56
|
+
rings: GeoRingRef[][]
|
|
57
|
+
/** Per ring, in steradians — exterior first, then holes. */
|
|
58
|
+
ringAreas: number[]
|
|
59
|
+
area: number
|
|
60
|
+
mainland: boolean
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface GeoFeature {
|
|
64
|
+
code: string
|
|
65
|
+
name: string
|
|
66
|
+
nameNl: string
|
|
67
|
+
parent: string | null
|
|
68
|
+
first: boolean
|
|
69
|
+
listed: boolean
|
|
70
|
+
remote: boolean
|
|
71
|
+
polygons: GeoPolygon[]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface DecodedGeoFile {
|
|
75
|
+
features: Map<string, GeoFeature>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function decodeArc(encoded: number[], key: number, [kx, ky]: [number, number], [tx, ty]: [number, number]): GeoArc {
|
|
79
|
+
const count = encoded.length / 3
|
|
80
|
+
const points = new Float64Array(count * 2)
|
|
81
|
+
const buckets = new Uint8Array(count)
|
|
82
|
+
let x = 0
|
|
83
|
+
let y = 0
|
|
84
|
+
for (let i = 0; i < count; i++) {
|
|
85
|
+
x += encoded[i * 3]
|
|
86
|
+
y += encoded[i * 3 + 1]
|
|
87
|
+
points[i * 2] = x * kx + tx
|
|
88
|
+
points[i * 2 + 1] = y * ky + ty
|
|
89
|
+
buckets[i] = encoded[i * 3 + 2]
|
|
90
|
+
}
|
|
91
|
+
return { key, points, buckets }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function ringCoordinates(ring: readonly GeoRingRef[]): [number, number][] {
|
|
95
|
+
const out: [number, number][] = []
|
|
96
|
+
for (const { arc, reversed } of ring) {
|
|
97
|
+
const n = arc.buckets.length
|
|
98
|
+
for (let j = out.length === 0 ? 0 : 1; j < n; j++) {
|
|
99
|
+
const i = reversed ? n - 1 - j : j
|
|
100
|
+
out.push([arc.points[i * 2], arc.points[i * 2 + 1]])
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** A ring's area in steradians, whichever way it winds. */
|
|
107
|
+
export function ringArea(ring: readonly GeoRingRef[]): number {
|
|
108
|
+
const coordinates = ringCoordinates(ring)
|
|
109
|
+
if (coordinates.length < 4) return 0
|
|
110
|
+
const area = geoArea({ type: "Polygon", coordinates: [coordinates] })
|
|
111
|
+
return area > 2 * Math.PI ? 4 * Math.PI - area : area
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function decodeGeoFile(data: GeoFileData): DecodedGeoFile {
|
|
115
|
+
if (data.format !== GEO_DATA_FORMAT) {
|
|
116
|
+
throw new Error(`geo data format ${data.format} is not supported (expected ${GEO_DATA_FORMAT})`)
|
|
117
|
+
}
|
|
118
|
+
const arcs = data.arcs.map((encoded, i) =>
|
|
119
|
+
decodeArc(encoded, data.arcIds[i], data.transform.scale, data.transform.translate)
|
|
120
|
+
)
|
|
121
|
+
const ref = (index: number): GeoRingRef =>
|
|
122
|
+
index < 0 ? { arc: arcs[~index], reversed: true } : { arc: arcs[index], reversed: false }
|
|
123
|
+
|
|
124
|
+
const features = new Map<string, GeoFeature>()
|
|
125
|
+
for (const f of data.features) {
|
|
126
|
+
const mainland = f.mainland ? new Set(f.mainland) : null
|
|
127
|
+
features.set(f.code, {
|
|
128
|
+
code: f.code,
|
|
129
|
+
name: f.name,
|
|
130
|
+
nameNl: f.nameNl ?? f.name,
|
|
131
|
+
parent: f.parent,
|
|
132
|
+
first: f.first,
|
|
133
|
+
listed: f.listed,
|
|
134
|
+
remote: f.remote === true,
|
|
135
|
+
polygons: f.polygons.map((rings, i) => {
|
|
136
|
+
const resolved = rings.map((ring) => ring.map(ref))
|
|
137
|
+
return {
|
|
138
|
+
rings: resolved,
|
|
139
|
+
ringAreas: resolved.map((ring, r) => (r === 0 ? f.areas[i] : ringArea(ring))),
|
|
140
|
+
area: f.areas[i],
|
|
141
|
+
mainland: mainland === null || mainland.has(i),
|
|
142
|
+
}
|
|
143
|
+
}),
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
return { features }
|
|
147
|
+
}
|