@react-x11/components 0.4.0 → 0.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.
- package/README.md +143 -8
- package/dist/charts/components.d.ts +3 -1
- package/dist/charts/components.d.ts.map +1 -1
- package/dist/charts/components.js +8 -4
- package/dist/charts/components.js.map +1 -1
- package/dist/charts/node.d.ts +13 -6
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +75 -24
- package/dist/charts/node.js.map +1 -1
- package/dist/charts/render.d.ts +4 -0
- package/dist/charts/render.d.ts.map +1 -1
- package/dist/charts/render.js +11 -6
- package/dist/charts/render.js.map +1 -1
- package/dist/code-editor/node.d.ts +47 -16
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +121 -51
- package/dist/code-editor/node.js.map +1 -1
- package/dist/flow/draw.d.ts +19 -1
- package/dist/flow/draw.d.ts.map +1 -1
- package/dist/flow/draw.js +64 -35
- package/dist/flow/draw.js.map +1 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +24 -8
- package/dist/flow/index.js.map +1 -1
- package/dist/flow/model.d.ts +4 -2
- package/dist/flow/model.d.ts.map +1 -1
- package/dist/flow/model.js +4 -2
- package/dist/flow/model.js.map +1 -1
- package/dist/flow/node.d.ts +40 -9
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +142 -53
- package/dist/flow/node.js.map +1 -1
- package/dist/flow/types.d.ts +46 -21
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/formula/index.d.ts +4 -2
- package/dist/formula/index.d.ts.map +1 -1
- package/dist/formula/index.js.map +1 -1
- package/dist/formula/node.d.ts +5 -1
- package/dist/formula/node.d.ts.map +1 -1
- package/dist/formula/node.js +19 -3
- package/dist/formula/node.js.map +1 -1
- package/dist/html/controls.d.ts +4 -1
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +11 -5
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +18 -8
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/parse.d.ts +15 -10
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +41 -16
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +3 -0
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +3 -1
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/values.d.ts +14 -1
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +11 -8
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +1 -0
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +12 -5
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +7 -4
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +22 -1
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/node.d.ts +22 -8
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +97 -15
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +3 -0
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +2 -2
- package/dist/html/paint.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -1
- package/dist/maps/clip.d.ts +43 -0
- package/dist/maps/clip.d.ts.map +1 -0
- package/dist/maps/clip.js +171 -0
- package/dist/maps/clip.js.map +1 -0
- package/dist/maps/gzip.d.ts +15 -0
- package/dist/maps/gzip.d.ts.map +1 -0
- package/dist/maps/gzip.js +56 -0
- package/dist/maps/gzip.js.map +1 -0
- package/dist/maps/index.d.ts +55 -0
- package/dist/maps/index.d.ts.map +1 -0
- package/dist/maps/index.js +141 -0
- package/dist/maps/index.js.map +1 -0
- package/dist/maps/labels.d.ts +123 -0
- package/dist/maps/labels.d.ts.map +1 -0
- package/dist/maps/labels.js +323 -0
- package/dist/maps/labels.js.map +1 -0
- package/dist/maps/mvt.d.ts +244 -0
- package/dist/maps/mvt.d.ts.map +1 -0
- package/dist/maps/mvt.js +820 -0
- package/dist/maps/mvt.js.map +1 -0
- package/dist/maps/node.d.ts +278 -0
- package/dist/maps/node.d.ts.map +1 -0
- package/dist/maps/node.js +1459 -0
- package/dist/maps/node.js.map +1 -0
- package/dist/maps/overlay.d.ts +157 -0
- package/dist/maps/overlay.d.ts.map +1 -0
- package/dist/maps/overlay.js +472 -0
- package/dist/maps/overlay.js.map +1 -0
- package/dist/maps/paint.d.ts +226 -0
- package/dist/maps/paint.d.ts.map +1 -0
- package/dist/maps/paint.js +705 -0
- package/dist/maps/paint.js.map +1 -0
- package/dist/maps/proj.d.ts +281 -0
- package/dist/maps/proj.d.ts.map +1 -0
- package/dist/maps/proj.js +451 -0
- package/dist/maps/proj.js.map +1 -0
- package/dist/maps/sources.d.ts +256 -0
- package/dist/maps/sources.d.ts.map +1 -0
- package/dist/maps/sources.js +210 -0
- package/dist/maps/sources.js.map +1 -0
- package/dist/maps/style.d.ts +141 -0
- package/dist/maps/style.d.ts.map +1 -0
- package/dist/maps/style.js +147 -0
- package/dist/maps/style.js.map +1 -0
- package/dist/maps/styles.d.ts +82 -0
- package/dist/maps/styles.d.ts.map +1 -0
- package/dist/maps/styles.js +898 -0
- package/dist/maps/styles.js.map +1 -0
- package/dist/maps/tiles.d.ts +283 -0
- package/dist/maps/tiles.d.ts.map +1 -0
- package/dist/maps/tiles.js +469 -0
- package/dist/maps/tiles.js.map +1 -0
- package/dist/maps/types.d.ts +244 -0
- package/dist/maps/types.d.ts.map +1 -0
- package/dist/maps/types.js +2 -0
- package/dist/maps/types.js.map +1 -0
- package/dist/markdown/ast.d.ts +80 -6
- package/dist/markdown/ast.d.ts.map +1 -1
- package/dist/markdown/ast.js +13 -4
- package/dist/markdown/ast.js.map +1 -1
- package/dist/markdown/expressions.d.ts +27 -0
- package/dist/markdown/expressions.d.ts.map +1 -0
- package/dist/markdown/expressions.js +203 -0
- package/dist/markdown/expressions.js.map +1 -0
- package/dist/markdown/index.d.ts +61 -2
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +101 -2
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/parse.d.ts +1 -1
- package/dist/markdown/parse.d.ts.map +1 -1
- package/dist/markdown/parse.js +163 -15
- package/dist/markdown/parse.js.map +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/markdown/spans.js +4 -0
- package/dist/markdown/spans.js.map +1 -1
- package/dist/markdown/tags.d.ts +35 -0
- package/dist/markdown/tags.d.ts.map +1 -0
- package/dist/markdown/tags.js +213 -0
- package/dist/markdown/tags.js.map +1 -0
- package/dist/reorder/hx.d.ts +18 -0
- package/dist/reorder/hx.d.ts.map +1 -0
- package/dist/reorder/hx.js +10 -0
- package/dist/reorder/hx.js.map +1 -0
- package/dist/reorder/index.d.ts +314 -0
- package/dist/reorder/index.d.ts.map +1 -0
- package/dist/reorder/index.js +1539 -0
- package/dist/reorder/index.js.map +1 -0
- package/dist/reorder/model.d.ts +107 -0
- package/dist/reorder/model.d.ts.map +1 -0
- package/dist/reorder/model.js +162 -0
- package/dist/reorder/model.js.map +1 -0
- package/dist/richtext/node.d.ts +15 -5
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +40 -11
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +35 -13
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +83 -27
- package/dist/richtext/runs.js.map +1 -1
- package/dist/tabs/index.d.ts +17 -0
- package/dist/tabs/index.d.ts.map +1 -1
- package/dist/tabs/index.js +849 -186
- package/dist/tabs/index.js.map +1 -1
- package/dist/terminal/vt/fonts.d.ts +45 -15
- package/dist/terminal/vt/fonts.d.ts.map +1 -1
- package/dist/terminal/vt/fonts.js +89 -19
- package/dist/terminal/vt/fonts.js.map +1 -1
- package/dist/terminal/vt/node.d.ts +42 -0
- package/dist/terminal/vt/node.d.ts.map +1 -1
- package/dist/terminal/vt/node.js +95 -11
- package/dist/terminal/vt/node.js.map +1 -1
- package/dist/terminal/vt/renderer.d.ts.map +1 -1
- package/dist/terminal/vt/renderer.js +13 -3
- package/dist/terminal/vt/renderer.js.map +1 -1
- package/package.json +14 -3
- package/src/charts/components.ts +14 -7
- package/src/charts/node.ts +81 -26
- package/src/charts/render.ts +16 -6
- package/src/code-editor/node.ts +147 -53
- package/src/flow/draw.ts +104 -35
- package/src/flow/index.ts +33 -13
- package/src/flow/model.ts +4 -2
- package/src/flow/node.ts +158 -59
- package/src/flow/types.ts +46 -21
- package/src/formula/index.ts +4 -2
- package/src/formula/node.ts +24 -5
- package/src/html/controls.ts +4 -1
- package/src/html/css/cascade.ts +18 -7
- package/src/html/css/parse.ts +46 -21
- package/src/html/css/style.ts +6 -1
- package/src/html/css/values.ts +25 -9
- package/src/html/index.ts +1 -0
- package/src/html/layout/boxes.ts +19 -9
- package/src/html/layout/inline.ts +23 -1
- package/src/html/node.ts +110 -17
- package/src/html/paint.ts +17 -2
- package/src/index.ts +102 -0
- package/src/maps/clip.ts +184 -0
- package/src/maps/gzip.ts +66 -0
- package/src/maps/index.ts +316 -0
- package/src/maps/labels.ts +442 -0
- package/src/maps/mvt.ts +857 -0
- package/src/maps/node.ts +1819 -0
- package/src/maps/overlay.ts +639 -0
- package/src/maps/paint.ts +978 -0
- package/src/maps/proj.ts +641 -0
- package/src/maps/sources.ts +425 -0
- package/src/maps/style.ts +309 -0
- package/src/maps/styles.ts +1040 -0
- package/src/maps/tiles.ts +676 -0
- package/src/maps/types.ts +260 -0
- package/src/markdown/ast.ts +85 -9
- package/src/markdown/expressions.ts +223 -0
- package/src/markdown/index.ts +190 -5
- package/src/markdown/parse.ts +230 -15
- package/src/markdown/spans.ts +4 -0
- package/src/markdown/tags.ts +240 -0
- package/src/reorder/hx.ts +31 -0
- package/src/reorder/index.ts +2223 -0
- package/src/reorder/model.ts +232 -0
- package/src/richtext/node.ts +49 -14
- package/src/richtext/runs.ts +115 -30
- package/src/tabs/index.ts +1040 -204
- package/src/terminal/vt/fonts.ts +128 -36
- package/src/terminal/vt/node.ts +105 -12
- package/src/terminal/vt/renderer.ts +13 -3
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
// Where tiles come from — and, deliberately, not how they are fetched.
|
|
2
|
+
//
|
|
3
|
+
// **Nothing in this package makes a network request.** That is the same
|
|
4
|
+
// call `src/html/` makes about `onResource` and `src/desktop-calendar/`
|
|
5
|
+
// makes about credentials, and it is the rule rather than an omission: a
|
|
6
|
+
// component that quietly fetched would turn "render this map" into "make
|
|
7
|
+
// these requests to this company", with the map's own default deciding
|
|
8
|
+
// whose servers an application talks to, what its user agent says, and
|
|
9
|
+
// whose usage policy it is now bound by. A tile source is a function the
|
|
10
|
+
// application supplies, and the adapters below are the *arguments* to it —
|
|
11
|
+
// URL templates and a decoder — not a fetch.
|
|
12
|
+
//
|
|
13
|
+
// {@link osmVectorSource} and {@link osmRasterSource} exist because the two
|
|
14
|
+
// keyless, open, canonical OpenStreetMap endpoints are what make
|
|
15
|
+
// `<Map source={…} />` a line that works with no signup; both still take
|
|
16
|
+
// the application's own `load`, and both carry the attribution the licence
|
|
17
|
+
// requires.
|
|
18
|
+
import { gunzipIfNeeded } from './gzip.js';
|
|
19
|
+
import { parseTile } from './mvt.js';
|
|
20
|
+
import type { VectorTile } from './mvt.js';
|
|
21
|
+
import { DEFAULT_TILE_SIZE } from './proj.js';
|
|
22
|
+
import type { TileId } from './proj.js';
|
|
23
|
+
|
|
24
|
+
/** Which tile a source is being asked for. */
|
|
25
|
+
export interface TileRequest extends TileId {
|
|
26
|
+
/** The source's own id, for an application serving several from one
|
|
27
|
+
* loader. */
|
|
28
|
+
sourceId: string;
|
|
29
|
+
/**
|
|
30
|
+
* Aborted when the tile leaves the view before it arrives.
|
|
31
|
+
*
|
|
32
|
+
* **This is a real `AbortSignal`** on every runtime that has one, which
|
|
33
|
+
* is what makes `fetch(url, { signal })` work — `fetch` rejects anything
|
|
34
|
+
* that is not an instance of it (`TypeError: Expected signal … to be an
|
|
35
|
+
* instance of AbortSignal`), so a look-alike would fail every load.
|
|
36
|
+
* `undefined` on a runtime with no `AbortController`.
|
|
37
|
+
*
|
|
38
|
+
* It is *typed* structurally because `src/` compiles with no DOM lib and
|
|
39
|
+
* cannot name the class, which is why an application in TypeScript casts:
|
|
40
|
+
* `fetch(url, { signal: signal as AbortSignal })`.
|
|
41
|
+
*/
|
|
42
|
+
signal: { readonly aborted: boolean; addEventListener?: unknown } | undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Raw tile bytes, or pixels, or nothing. */
|
|
46
|
+
export type TileData =
|
|
47
|
+
/** Mapbox Vector Tile bytes. Gzip is unwrapped for you, so either the
|
|
48
|
+
* compressed or the decompressed body is fine. */
|
|
49
|
+
| { kind: 'vector'; data: Uint8Array }
|
|
50
|
+
/**
|
|
51
|
+
* Decoded pixels: straight (non-premultiplied) RGBA, rows top to bottom
|
|
52
|
+
* — the canvas contract, and what ntk's own `decodeImage` produces.
|
|
53
|
+
*
|
|
54
|
+
* Bytes rather than a PNG or a JPEG on purpose. Decoding an image format
|
|
55
|
+
* needs a codec, and this package will not grow one or assume which of
|
|
56
|
+
* them a provider serves; the application already has ntk's, through
|
|
57
|
+
* `react-x11/ntk`, and the docs page has the four-line adapter.
|
|
58
|
+
*/
|
|
59
|
+
| { kind: 'raster'; width: number; height: number; data: Uint8Array }
|
|
60
|
+
/** There is no tile here. An ordinary state — an ocean tile in a
|
|
61
|
+
* land-only pyramid, a 404 — and never an error. */
|
|
62
|
+
| null;
|
|
63
|
+
|
|
64
|
+
/** A tile source. */
|
|
65
|
+
export interface MapSource {
|
|
66
|
+
/** Distinguishes this source's cache entries from another's. Defaults to
|
|
67
|
+
* the source's position in the `sources` array. */
|
|
68
|
+
id?: string;
|
|
69
|
+
/** Fetch, or answer from a cache, or decline. May be async. */
|
|
70
|
+
load(request: TileRequest): TileData | Promise<TileData>;
|
|
71
|
+
/** The shallowest and deepest levels this source has data for. Outside
|
|
72
|
+
* the range the nearest level is used and scaled — "overzoom" above,
|
|
73
|
+
* which is how a z14 pyramid serves a z18 view. Defaults to 0 and 14. */
|
|
74
|
+
minZoom?: number;
|
|
75
|
+
maxZoom?: number;
|
|
76
|
+
/** Edge length of one tile in logical pixels: 512 for most vector
|
|
77
|
+
* pyramids, 256 for the older raster convention. */
|
|
78
|
+
tileSize?: number;
|
|
79
|
+
/**
|
|
80
|
+
* The attribution this source's licence requires, drawn in the corner of
|
|
81
|
+
* the map.
|
|
82
|
+
*
|
|
83
|
+
* Not optional in practice for open data, which is why it is a field of
|
|
84
|
+
* the source rather than a prop of the map: whoever chose the tiles is
|
|
85
|
+
* who knows what has to be said about them.
|
|
86
|
+
*/
|
|
87
|
+
attribution?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** A source's pyramid, with the defaults filled in. */
|
|
91
|
+
export function pyramidOf(source: MapSource): {
|
|
92
|
+
minZoom: number;
|
|
93
|
+
maxZoom: number;
|
|
94
|
+
tileSize: number;
|
|
95
|
+
} {
|
|
96
|
+
return {
|
|
97
|
+
minZoom: source.minZoom ?? 0,
|
|
98
|
+
maxZoom: source.maxZoom ?? 14,
|
|
99
|
+
tileSize: source.tileSize ?? DEFAULT_TILE_SIZE,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Substitute `{z}`, `{x}`, `{y}` — and `{s}` from `subdomains`, which the
|
|
104
|
+
* older raster services still shard on. */
|
|
105
|
+
export function tileUrl(
|
|
106
|
+
template: string,
|
|
107
|
+
tile: TileId,
|
|
108
|
+
subdomains?: readonly string[],
|
|
109
|
+
): string {
|
|
110
|
+
let url = template
|
|
111
|
+
.replace('{z}', String(tile.z))
|
|
112
|
+
.replace('{x}', String(tile.x))
|
|
113
|
+
.replace('{y}', String(tile.y));
|
|
114
|
+
if (subdomains && subdomains.length > 0) {
|
|
115
|
+
// Deterministic in the tile, so the same tile always goes to the same
|
|
116
|
+
// host and a browser-style connection cache is not defeated.
|
|
117
|
+
const index = (tile.x + tile.y) % subdomains.length;
|
|
118
|
+
url = url.replace('{s}', subdomains[index]);
|
|
119
|
+
}
|
|
120
|
+
return url;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** What every OpenStreetMap-derived map has to say. */
|
|
124
|
+
export const OSM_ATTRIBUTION = '© OpenStreetMap contributors';
|
|
125
|
+
|
|
126
|
+
/** Where the OSM Foundation serves Shortbread vector tiles. */
|
|
127
|
+
export const OSM_VECTOR_URL =
|
|
128
|
+
'https://vector.openstreetmap.org/shortbread_v1/{z}/{x}/{y}.mvt';
|
|
129
|
+
|
|
130
|
+
/** …and the classic raster layer. */
|
|
131
|
+
export const OSM_RASTER_URL = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
|
|
132
|
+
|
|
133
|
+
/** What a ready-made adapter still needs from the application. */
|
|
134
|
+
export interface OsmSourceOptions {
|
|
135
|
+
/**
|
|
136
|
+
* Fetch one URL. **Required**, and required on purpose — see the note at
|
|
137
|
+
* the top of this file. The whole of it is usually:
|
|
138
|
+
*
|
|
139
|
+
* ```ts
|
|
140
|
+
* fetch: async (url, signal) => {
|
|
141
|
+
* const response = await fetch(url, {
|
|
142
|
+
* signal,
|
|
143
|
+
* headers: { 'user-agent': 'my-app/1.0 (me@example.com)' },
|
|
144
|
+
* });
|
|
145
|
+
* if (response.status === 404) return null;
|
|
146
|
+
* if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
147
|
+
* return new Uint8Array(await response.arrayBuffer());
|
|
148
|
+
* }
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* `null` means "no tile here", which is an ordinary answer.
|
|
152
|
+
*/
|
|
153
|
+
fetch: (
|
|
154
|
+
url: string,
|
|
155
|
+
signal: TileRequest['signal'],
|
|
156
|
+
tile: TileId,
|
|
157
|
+
) => Promise<Uint8Array | null> | Uint8Array | null;
|
|
158
|
+
/** Serve from somewhere else — a mirror, a proxy, a local tile server. */
|
|
159
|
+
url?: string;
|
|
160
|
+
id?: string;
|
|
161
|
+
attribution?: string;
|
|
162
|
+
maxZoom?: number;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* OpenStreetMap's own vector tiles, in the Shortbread schema.
|
|
167
|
+
*
|
|
168
|
+
* Pairs with {@link shortbreadStyle}, which is written against the same
|
|
169
|
+
* schema. z14 is the deepest level cut; a view past it overzooms, which is
|
|
170
|
+
* what every client does with this pyramid.
|
|
171
|
+
*
|
|
172
|
+
* **Read the OSM tile usage policy before shipping.** It asks for a
|
|
173
|
+
* user-agent identifying the application, and it is not a CDN for an
|
|
174
|
+
* application with real traffic — which is exactly why `fetch` is the
|
|
175
|
+
* caller's.
|
|
176
|
+
*/
|
|
177
|
+
export function osmVectorSource(options: OsmSourceOptions): MapSource {
|
|
178
|
+
const url = options.url ?? OSM_VECTOR_URL;
|
|
179
|
+
return {
|
|
180
|
+
id: options.id ?? 'osm-vector',
|
|
181
|
+
minZoom: 0,
|
|
182
|
+
maxZoom: options.maxZoom ?? 14,
|
|
183
|
+
tileSize: 512,
|
|
184
|
+
attribution: options.attribution ?? OSM_ATTRIBUTION,
|
|
185
|
+
async load(request) {
|
|
186
|
+
const bytes = await options.fetch(
|
|
187
|
+
tileUrl(url, request),
|
|
188
|
+
request.signal,
|
|
189
|
+
request,
|
|
190
|
+
);
|
|
191
|
+
if (!bytes || bytes.length === 0) return null;
|
|
192
|
+
return { kind: 'vector', data: gunzipIfNeeded(bytes) };
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* OpenStreetMap's raster tiles — the standard style, as `openstreetmap.org`
|
|
199
|
+
* draws it.
|
|
200
|
+
*
|
|
201
|
+
* `decode` is the second thing the application supplies, because a PNG
|
|
202
|
+
* needs a codec and this package will not grow one. ntk already has one,
|
|
203
|
+
* and it is already installed:
|
|
204
|
+
*
|
|
205
|
+
* ```ts
|
|
206
|
+
* import ntk from 'react-x11/ntk';
|
|
207
|
+
* const decode = (bytes) => {
|
|
208
|
+
* const image = (ntk as { decodeImage(b: Uint8Array): {
|
|
209
|
+
* width: number; height: number; data: Uint8Array } }).decodeImage(bytes);
|
|
210
|
+
* return { width: image.width, height: image.height, data: image.data };
|
|
211
|
+
* };
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export function osmRasterSource(
|
|
215
|
+
options: OsmSourceOptions & {
|
|
216
|
+
decode: (bytes: Uint8Array) => {
|
|
217
|
+
width: number;
|
|
218
|
+
height: number;
|
|
219
|
+
data: Uint8Array;
|
|
220
|
+
};
|
|
221
|
+
subdomains?: readonly string[];
|
|
222
|
+
},
|
|
223
|
+
): MapSource {
|
|
224
|
+
const url = options.url ?? OSM_RASTER_URL;
|
|
225
|
+
return {
|
|
226
|
+
id: options.id ?? 'osm-raster',
|
|
227
|
+
minZoom: 0,
|
|
228
|
+
maxZoom: options.maxZoom ?? 19,
|
|
229
|
+
tileSize: 256,
|
|
230
|
+
attribution: options.attribution ?? OSM_ATTRIBUTION,
|
|
231
|
+
async load(request) {
|
|
232
|
+
const bytes = await options.fetch(
|
|
233
|
+
tileUrl(url, request, options.subdomains),
|
|
234
|
+
request.signal,
|
|
235
|
+
request,
|
|
236
|
+
);
|
|
237
|
+
if (!bytes || bytes.length === 0) return null;
|
|
238
|
+
const image = options.decode(bytes);
|
|
239
|
+
return {
|
|
240
|
+
kind: 'raster',
|
|
241
|
+
width: image.width,
|
|
242
|
+
height: image.height,
|
|
243
|
+
data: image.data,
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Decode vector tile bytes. Exported so an application holding a `.mvt`
|
|
250
|
+
* of its own can hand the map a parsed tile through a source. */
|
|
251
|
+
export function parseVectorTile(bytes: Uint8Array): VectorTile {
|
|
252
|
+
return parseTile(gunzipIfNeeded(bytes));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// --- Google Maps ------------------------------------------------------------
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* What Google's Map Tiles API hands back for a session request.
|
|
259
|
+
*
|
|
260
|
+
* Written out structurally rather than imported: this package has no Google
|
|
261
|
+
* dependency and is not about to grow one.
|
|
262
|
+
*/
|
|
263
|
+
export interface GoogleSession {
|
|
264
|
+
/** The token every tile request carries. */
|
|
265
|
+
session: string;
|
|
266
|
+
/** Usually 256. */
|
|
267
|
+
tileWidth?: number;
|
|
268
|
+
tileHeight?: number;
|
|
269
|
+
/** `'png'` or `'jpeg'`. */
|
|
270
|
+
imageFormat?: string;
|
|
271
|
+
/** Seconds since the epoch, as a string, per the API. */
|
|
272
|
+
expiry?: string;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export interface GoogleTileSourceOptions {
|
|
276
|
+
/**
|
|
277
|
+
* Fetch one tile URL. The application's, exactly as every other source
|
|
278
|
+
* here — see the note at the top of this file.
|
|
279
|
+
*/
|
|
280
|
+
fetch: (
|
|
281
|
+
url: string,
|
|
282
|
+
signal: TileRequest['signal'],
|
|
283
|
+
tile: TileId,
|
|
284
|
+
) => Promise<Uint8Array | null> | Uint8Array | null;
|
|
285
|
+
/**
|
|
286
|
+
* Create a session, by POSTing `body` as JSON to
|
|
287
|
+
* `https://tile.googleapis.com/v1/createSession?key=…`, and hand back
|
|
288
|
+
* what it answered.
|
|
289
|
+
*
|
|
290
|
+
* A separate callback from `fetch` because it is a POST with a JSON body
|
|
291
|
+
* and a key in the query, which the tile-fetching shape cannot express —
|
|
292
|
+
* and because it is where the API key lives, which is the application's
|
|
293
|
+
* secret and not something to hand a component.
|
|
294
|
+
*
|
|
295
|
+
* Called lazily on the first tile and again when the session expires;
|
|
296
|
+
* Google's are good for two weeks.
|
|
297
|
+
*/
|
|
298
|
+
createSession: (body: Record<string, unknown>) => Promise<GoogleSession>;
|
|
299
|
+
/**
|
|
300
|
+
* Decode the PNG or JPEG. Google's 2D tiles are **raster** — there is no
|
|
301
|
+
* public vector endpoint and no published schema — so a codec is needed
|
|
302
|
+
* exactly as it is for `osmRasterSource`.
|
|
303
|
+
*/
|
|
304
|
+
decode: (bytes: Uint8Array) => {
|
|
305
|
+
width: number;
|
|
306
|
+
height: number;
|
|
307
|
+
data: Uint8Array;
|
|
308
|
+
};
|
|
309
|
+
/** `'roadmap'` (the default), `'satellite'` or `'terrain'`. Satellite is
|
|
310
|
+
* the one thing OpenStreetMap has no answer for at all. */
|
|
311
|
+
mapType?: 'roadmap' | 'satellite' | 'terrain';
|
|
312
|
+
/** `'layerRoadmap'` over satellite or terrain, `'layerStreetview'`. */
|
|
313
|
+
layerTypes?: readonly string[];
|
|
314
|
+
/** IETF language tag and CLDR region — Google's cartography is localized
|
|
315
|
+
* server-side, which is the other thing you cannot do with a style. */
|
|
316
|
+
language?: string;
|
|
317
|
+
region?: string;
|
|
318
|
+
/** `'scaleFactor1x'`, `'scaleFactor2x'`, `'scaleFactor4x'`. */
|
|
319
|
+
scale?: string;
|
|
320
|
+
highDpi?: boolean;
|
|
321
|
+
id?: string;
|
|
322
|
+
maxZoom?: number;
|
|
323
|
+
/**
|
|
324
|
+
* What is drawn in the corner. **Do not remove it.** Google's Map Tiles
|
|
325
|
+
* API policies require the Google Maps logo, or the text "Google Maps"
|
|
326
|
+
* where space is limited, together with the data providers — and require
|
|
327
|
+
* that it is not obscured by anyone else's attribution. This component
|
|
328
|
+
* draws attribution as text, so the text form is what it can satisfy;
|
|
329
|
+
* a logo is an overlay of your own (`<Map>` takes `children`).
|
|
330
|
+
*/
|
|
331
|
+
attribution?: string;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Google's Map Tiles API, as a `MapSource`.
|
|
336
|
+
*
|
|
337
|
+
* **Raster, and that is not a limitation of this adapter.** Google publishes
|
|
338
|
+
* no vector tile endpoint and no schema, so there is nothing for a
|
|
339
|
+
* `mapStyle` to name: what arrives is Google's own cartography, already
|
|
340
|
+
* drawn. `mapType` and `language` are the styling controls, and they are
|
|
341
|
+
* applied server-side when the session is created.
|
|
342
|
+
*
|
|
343
|
+
* Three things about it that differ from every other source here, and each
|
|
344
|
+
* is the API's rather than this package's:
|
|
345
|
+
*
|
|
346
|
+
* - **A session token comes first.** One POST, reused for every tile,
|
|
347
|
+
* good for two weeks, and it is what carries the display options — so
|
|
348
|
+
* changing `mapType` means a new session, which this handles by making a
|
|
349
|
+
* new source.
|
|
350
|
+
* - **A key is required**, and it is yours: it goes in `createSession` and
|
|
351
|
+
* in the tile URL, both of which are the application's callbacks.
|
|
352
|
+
* - **The terms restrict caching.** `<Map>` keeps parsed tiles and rendered
|
|
353
|
+
* surfaces in memory for the session, which is what any renderer does;
|
|
354
|
+
* writing tiles to disk is a different thing and the policies forbid it.
|
|
355
|
+
* Read them before shipping — they also govern showing Google's tiles
|
|
356
|
+
* beside another map's.
|
|
357
|
+
*/
|
|
358
|
+
export function googleTileSource(options: GoogleTileSourceOptions): MapSource {
|
|
359
|
+
let session: GoogleSession | null = null;
|
|
360
|
+
let pending: Promise<GoogleSession> | null = null;
|
|
361
|
+
let tileSize = 256;
|
|
362
|
+
|
|
363
|
+
const ensure = async (): Promise<GoogleSession> => {
|
|
364
|
+
const expiry = Number(session?.expiry ?? 0);
|
|
365
|
+
// A minute of slack, so a token does not expire between being chosen
|
|
366
|
+
// and being used.
|
|
367
|
+
if (session && (!expiry || expiry * 1000 > Date.now() + 60_000)) {
|
|
368
|
+
return session;
|
|
369
|
+
}
|
|
370
|
+
if (!pending) {
|
|
371
|
+
pending = Promise.resolve(
|
|
372
|
+
options.createSession({
|
|
373
|
+
mapType: options.mapType ?? 'roadmap',
|
|
374
|
+
language: options.language ?? 'en-US',
|
|
375
|
+
region: options.region ?? 'US',
|
|
376
|
+
...(options.layerTypes
|
|
377
|
+
? { layerTypes: [...options.layerTypes] }
|
|
378
|
+
: {}),
|
|
379
|
+
...(options.scale ? { scale: options.scale } : {}),
|
|
380
|
+
...(options.highDpi !== undefined
|
|
381
|
+
? { highDpi: options.highDpi }
|
|
382
|
+
: {}),
|
|
383
|
+
}),
|
|
384
|
+
)
|
|
385
|
+
.then((answer) => {
|
|
386
|
+
session = answer;
|
|
387
|
+
if (answer.tileWidth && answer.tileWidth > 0) {
|
|
388
|
+
tileSize = answer.tileWidth;
|
|
389
|
+
}
|
|
390
|
+
return answer;
|
|
391
|
+
})
|
|
392
|
+
.finally(() => {
|
|
393
|
+
pending = null;
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
return pending;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
id: options.id ?? `google-${options.mapType ?? 'roadmap'}`,
|
|
401
|
+
minZoom: 0,
|
|
402
|
+
// Google serves to 22, deeper than any vector pyramid here — and since
|
|
403
|
+
// these are finished images there is nothing to sub-tile past it.
|
|
404
|
+
maxZoom: options.maxZoom ?? 22,
|
|
405
|
+
get tileSize(): number {
|
|
406
|
+
return tileSize;
|
|
407
|
+
},
|
|
408
|
+
attribution: options.attribution ?? 'Google Maps',
|
|
409
|
+
async load(request) {
|
|
410
|
+
const token = await ensure();
|
|
411
|
+
const url =
|
|
412
|
+
`https://tile.googleapis.com/v1/2dtiles/${request.z}/${request.x}/${request.y}` +
|
|
413
|
+
`?session=${encodeURIComponent(token.session)}`;
|
|
414
|
+
const bytes = await options.fetch(url, request.signal, request);
|
|
415
|
+
if (!bytes || bytes.length === 0) return null;
|
|
416
|
+
const image = options.decode(bytes);
|
|
417
|
+
return {
|
|
418
|
+
kind: 'raster',
|
|
419
|
+
width: image.width,
|
|
420
|
+
height: image.height,
|
|
421
|
+
data: image.data,
|
|
422
|
+
};
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
}
|