@mailwoman/map-tui 9.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/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # @mailwoman/map-tui
2
+
3
+ A frame-first terminal map renderer. It decodes vector tiles from a
4
+ [PMTiles](https://protomaps.com/) archive, rasterizes the visible layers, and
5
+ resolves the result to a grid of terminal cells — braille-glyph map frames
6
+ suitable for TUIs and other character-grid displays.
7
+
8
+ The unit of output is a `MapFrame`: frames are values, not side effects.
9
+ Rendering never writes to stdout, an Ink component, or any other presentation
10
+ layer directly — a caller reads the frame's cells and decides how (and
11
+ whether) to draw them.
12
+
13
+ Tiles are never bundled with this package. Every render takes a caller-supplied
14
+ PMTiles path (local file or remote archive), so callers own their own tile
15
+ sourcing and coverage.
package/frame.ts ADDED
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+
7
+ /**
8
+ * Braille frame value + conversion for map-tui's debug view.
9
+ *
10
+ * `rasterizeToFrame` turns an `RGBAGrid` (drawn by ./raster.ts) into a `MapFrame`: one codepoint and one packed color
11
+ * per cell. The braille dither/luminance work is asciify's — `FrameRasterizer` subclasses `AsciifyTerminal` with a
12
+ * no-op sink purely to reach its protected `_computeBrailleCells`, `_cellChars`, `_cellColors`, so this module never
13
+ * re-implements the dot math. `frameToANSILines` and `overlayText` then work on the plain `MapFrame` value, with no
14
+ * further asciify dependency.
15
+ */
16
+
17
+ import { AsciifyTerminal } from "@sister.software/asciify/tui"
18
+
19
+ import type { RGBAGrid } from "./raster.ts"
20
+ import type { RGB } from "./style.ts"
21
+
22
+ const SGR_RESET = "\u001B[0m"
23
+
24
+ /**
25
+ * A rendered braille frame: one codepoint and one packed color per cell, row-major.
26
+ */
27
+ export interface MapFrame {
28
+ columns: number
29
+ rows: number
30
+ /**
31
+ * Codepoint per cell, row-major (braille U+2800.. or overlay text).
32
+ */
33
+ chars: Uint32Array
34
+ /**
35
+ * 0xRRGGBB per cell; 0 = inkless.
36
+ */
37
+ colors: Uint32Array
38
+ attribution: string
39
+ }
40
+
41
+ /**
42
+ * Reaches asciify's protected braille conversion from the outside. Constructed fresh per {@link rasterizeToFrame} call
43
+ * — cheap, since the state is just two typed arrays sized to the cell grid. The `write` sink is never invoked: this
44
+ * class only ever calls `_computeBrailleCells` directly, never `rasterize`/`flush`.
45
+ */
46
+ class FrameRasterizer extends AsciifyTerminal {
47
+ constructor(columns: number, rows: number) {
48
+ super({ write: () => true, columns, rows }, { mode: "braille", colorDepth: "truecolor", synchronizedOutput: false })
49
+ this.setSize(columns, rows)
50
+ }
51
+
52
+ toCells(buffer: Uint8ClampedArray): { chars: Uint32Array; colors: Uint32Array } {
53
+ this._computeBrailleCells(buffer, false)
54
+
55
+ return { chars: this._cellChars.slice(), colors: this._cellColors.slice() }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Converts a 2×4-subpixel RGBA grid into braille cells. Grid must be `columns * 2` x `rows * 4`.
61
+ *
62
+ * @throws If the grid's dimensions don't match `columns * 2` x `rows * 4` — a caller sizing bug, not something to
63
+ * silently clip.
64
+ */
65
+ export function rasterizeToFrame(grid: RGBAGrid, columns: number, rows: number, attribution: string): MapFrame {
66
+ if (grid.width !== columns * 2 || grid.height !== rows * 4) {
67
+ throw new Error(
68
+ `rasterizeToFrame: grid is ${grid.width}x${grid.height}, expected ${columns * 2}x${rows * 4} for ${columns}x${rows} cells`
69
+ )
70
+ }
71
+
72
+ const rasterizer = new FrameRasterizer(columns, rows)
73
+ const { chars, colors } = rasterizer.toCells(grid.data)
74
+
75
+ return { columns, rows, chars, colors, attribution }
76
+ }
77
+
78
+ /**
79
+ * One SGR-styled string per row. `color: false` strips styling (NO_COLOR consumers).
80
+ *
81
+ * Inkless cells (packed color 0) never emit a color escape, matching asciify's own damage emitter — a cell going from
82
+ * inked to inkless shouldn't touch color state. Each styled line ends with the SGR reset so a truncated terminal write
83
+ * never bleeds color into whatever follows.
84
+ */
85
+ export function frameToANSILines(frame: MapFrame, options?: { color?: boolean }): string[] {
86
+ const colorEnabled = options?.color ?? true
87
+ const lines: string[] = []
88
+
89
+ for (let row = 0; row < frame.rows; row++) {
90
+ let line = ""
91
+ let activeColor = -1
92
+
93
+ for (let column = 0; column < frame.columns; column++) {
94
+ const cellIndex = row * frame.columns + column
95
+ const char = frame.chars[cellIndex]!
96
+ const color = frame.colors[cellIndex]!
97
+ const inked = color !== 0
98
+
99
+ if (colorEnabled && inked && color !== activeColor) {
100
+ line += `\u001B[38;2;${(color >> 16) & 0xff};${(color >> 8) & 0xff};${color & 0xff}m`
101
+ activeColor = color
102
+ }
103
+
104
+ line += char === 0 ? " " : String.fromCodePoint(char)
105
+ }
106
+
107
+ lines.push(colorEnabled ? line + SGR_RESET : line)
108
+ }
109
+
110
+ return lines
111
+ }
112
+
113
+ /**
114
+ * Writes text into cells (clipped); occupied is the label-collision bitmap, updated in place.
115
+ *
116
+ * When `occupied` is given, every target cell plus one cell of padding on each side is checked before anything is
117
+ * written — a single colliding cell rejects the whole label rather than partially overlaying it. Without `occupied`,
118
+ * writes proceed unconditionally (still clipped to the frame's bounds) and always report success.
119
+ */
120
+ export function overlayText(
121
+ frame: MapFrame,
122
+ column: number,
123
+ row: number,
124
+ text: string,
125
+ color: number,
126
+ occupied?: Uint8Array
127
+ ): boolean {
128
+ if (row < 0 || row >= frame.rows) return false
129
+
130
+ const codePoints = Array.from(text)
131
+
132
+ if (occupied) {
133
+ for (let i = 0; i < codePoints.length; i++) {
134
+ const cellColumn = column + i
135
+
136
+ for (let padding = -1; padding <= 1; padding++) {
137
+ const checkColumn = cellColumn + padding
138
+
139
+ if (checkColumn < 0 || checkColumn >= frame.columns) continue
140
+
141
+ if (occupied[row * frame.columns + checkColumn]) return false
142
+ }
143
+ }
144
+ }
145
+
146
+ for (let i = 0; i < codePoints.length; i++) {
147
+ const cellColumn = column + i
148
+
149
+ if (cellColumn < 0 || cellColumn >= frame.columns) continue
150
+
151
+ const cellIndex = row * frame.columns + cellColumn
152
+
153
+ frame.chars[cellIndex] = codePoints[i]!.codePointAt(0)!
154
+ frame.colors[cellIndex] = color
155
+
156
+ if (occupied) {
157
+ occupied[cellIndex] = 1
158
+ }
159
+ }
160
+
161
+ return true
162
+ }
163
+
164
+ /**
165
+ * Packs RGB channels into the frame's 0xRRGGBB color representation. Shared with Task 8's marker colors so both sides
166
+ * of the frame boundary agree on the packing.
167
+ */
168
+ export function rgbToPacked(color: RGB): number {
169
+ return (color[0] << 16) | (color[1] << 8) | color[2]
170
+ }
package/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+
7
+ export * from "./frame.ts"
8
+ export * from "./mercator.ts"
9
+ export * from "./mvt.ts"
10
+ export * from "./raster.ts"
11
+ export * from "./renderer.ts"
12
+ export * from "./style.ts"
13
+ export * from "./tile-source.ts"
package/mercator.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+
7
+ /**
8
+ * Web-Mercator projection math for map-tui.
9
+ *
10
+ * This module reimplements the standard Web-Mercator projection (EPSG:3857) math locally rather than importing from
11
+ * `@mailwoman/cartographer` or `@mailwoman/spatial`. The cartographer dependency drags maplibre-gl +
12
+ * `@mailwoman/tiger`; spatial drags `@mailwoman/core`'s shipped data. map-tui maintains a dependency-lean surface for
13
+ * the standalone `npx` story (the nuts-lookup precedent).
14
+ */
15
+
16
+ /**
17
+ * Number of pixels per tile in the Web-Mercator projection (standard: 256).
18
+ */
19
+ export const TILE_SIZE = 256
20
+
21
+ export interface LonLat {
22
+ lon: number
23
+ lat: number
24
+ }
25
+
26
+ export interface WorldPx {
27
+ x: number
28
+ y: number
29
+ }
30
+
31
+ export function lonLatToWorldPx(lon: number, lat: number, zoom: number): WorldPx {
32
+ const scale = TILE_SIZE * 2 ** zoom
33
+ const sin = Math.sin((lat * Math.PI) / 180)
34
+
35
+ return {
36
+ x: scale * (lon / 360 + 0.5),
37
+ y: scale * (0.5 - (0.25 * Math.log((1 + sin) / (1 - sin))) / Math.PI),
38
+ }
39
+ }
40
+
41
+ export function worldPxToLonLat(x: number, y: number, zoom: number): LonLat {
42
+ const scale = TILE_SIZE * 2 ** zoom
43
+ const n = Math.PI * (1 - (2 * y) / scale)
44
+
45
+ return {
46
+ lon: (x / scale - 0.5) * 360,
47
+ lat: (Math.atan(Math.sinh(n)) * 180) / Math.PI,
48
+ }
49
+ }
50
+
51
+ const EARTH_CIRCUMFERENCE_M = 40_075_016.686
52
+
53
+ export function metersPerPixel(lat: number, zoom: number): number {
54
+ return (EARTH_CIRCUMFERENCE_M * Math.cos((lat * Math.PI) / 180)) / (TILE_SIZE * 2 ** zoom)
55
+ }
package/mvt.ts ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+
7
+ /**
8
+ * Mapbox Vector Tile (MVT) decoding for map-tui.
9
+ *
10
+ * Wraps @mapbox/vector-tile + pbf behind a plain-data shape (DecodedLayer / DecodedFeature) so the rest of map-tui
11
+ * never touches the upstream library's lazy-geometry classes.
12
+ */
13
+
14
+ import { VectorTile } from "@mapbox/vector-tile"
15
+ import Pbf from "pbf"
16
+
17
+ export interface DecodedFeature {
18
+ /**
19
+ * 1 = point, 2 = line, 3 = polygon (MVT geometry types).
20
+ */
21
+ type: 1 | 2 | 3
22
+
23
+ /**
24
+ * Rings/lines/points in tile-local integer coords (0..extent).
25
+ */
26
+ geometry: Array<Array<{ x: number; y: number }>>
27
+ properties: Record<string, unknown>
28
+ }
29
+
30
+ export interface DecodedLayer {
31
+ name: string
32
+ extent: number
33
+ features: DecodedFeature[]
34
+ }
35
+
36
+ export function decodeMVT(data: Uint8Array): DecodedLayer[] {
37
+ const tile = new VectorTile(new Pbf(data))
38
+
39
+ return Object.entries(tile.layers).map(([name, layer]) => {
40
+ const features: DecodedFeature[] = []
41
+
42
+ for (let index = 0; index < layer.length; index++) {
43
+ const feature = layer.feature(index)
44
+
45
+ features.push({
46
+ type: feature.type as 1 | 2 | 3,
47
+ geometry: feature.loadGeometry(),
48
+ properties: feature.properties as Record<string, unknown>,
49
+ })
50
+ }
51
+
52
+ return { name, extent: layer.extent, features }
53
+ })
54
+ }
package/out/frame.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ import type { RGBAGrid } from "./raster.ts";
7
+ import type { RGB } from "./style.ts";
8
+ /**
9
+ * A rendered braille frame: one codepoint and one packed color per cell, row-major.
10
+ */
11
+ export interface MapFrame {
12
+ columns: number;
13
+ rows: number;
14
+ /**
15
+ * Codepoint per cell, row-major (braille U+2800.. or overlay text).
16
+ */
17
+ chars: Uint32Array;
18
+ /**
19
+ * 0xRRGGBB per cell; 0 = inkless.
20
+ */
21
+ colors: Uint32Array;
22
+ attribution: string;
23
+ }
24
+ /**
25
+ * Converts a 2×4-subpixel RGBA grid into braille cells. Grid must be `columns * 2` x `rows * 4`.
26
+ *
27
+ * @throws If the grid's dimensions don't match `columns * 2` x `rows * 4` — a caller sizing bug, not something to
28
+ * silently clip.
29
+ */
30
+ export declare function rasterizeToFrame(grid: RGBAGrid, columns: number, rows: number, attribution: string): MapFrame;
31
+ /**
32
+ * One SGR-styled string per row. `color: false` strips styling (NO_COLOR consumers).
33
+ *
34
+ * Inkless cells (packed color 0) never emit a color escape, matching asciify's own damage emitter — a cell going from
35
+ * inked to inkless shouldn't touch color state. Each styled line ends with the SGR reset so a truncated terminal write
36
+ * never bleeds color into whatever follows.
37
+ */
38
+ export declare function frameToANSILines(frame: MapFrame, options?: {
39
+ color?: boolean;
40
+ }): string[];
41
+ /**
42
+ * Writes text into cells (clipped); occupied is the label-collision bitmap, updated in place.
43
+ *
44
+ * When `occupied` is given, every target cell plus one cell of padding on each side is checked before anything is
45
+ * written — a single colliding cell rejects the whole label rather than partially overlaying it. Without `occupied`,
46
+ * writes proceed unconditionally (still clipped to the frame's bounds) and always report success.
47
+ */
48
+ export declare function overlayText(frame: MapFrame, column: number, row: number, text: string, color: number, occupied?: Uint8Array): boolean;
49
+ /**
50
+ * Packs RGB channels into the frame's 0xRRGGBB color representation. Shared with Task 8's marker colors so both sides
51
+ * of the frame boundary agree on the packing.
52
+ */
53
+ export declare function rgbToPacked(color: RGB): number;
54
+ //# sourceMappingURL=frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../frame.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAA;AAIrC;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,KAAK,EAAE,WAAW,CAAA;IAClB;;OAEG;IACH,MAAM,EAAE,WAAW,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACnB;AAoBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,QAAQ,CAW7G;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,EAAE,CA0BzF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAC1B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,UAAU,GACnB,OAAO,CAmCT;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAE9C"}
package/out/frame.js ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ /**
7
+ * Braille frame value + conversion for map-tui's debug view.
8
+ *
9
+ * `rasterizeToFrame` turns an `RGBAGrid` (drawn by ./raster.ts) into a `MapFrame`: one codepoint and one packed color
10
+ * per cell. The braille dither/luminance work is asciify's — `FrameRasterizer` subclasses `AsciifyTerminal` with a
11
+ * no-op sink purely to reach its protected `_computeBrailleCells`, `_cellChars`, `_cellColors`, so this module never
12
+ * re-implements the dot math. `frameToANSILines` and `overlayText` then work on the plain `MapFrame` value, with no
13
+ * further asciify dependency.
14
+ */
15
+ import { AsciifyTerminal } from "@sister.software/asciify/tui";
16
+ const SGR_RESET = "\u001B[0m";
17
+ /**
18
+ * Reaches asciify's protected braille conversion from the outside. Constructed fresh per {@link rasterizeToFrame} call
19
+ * — cheap, since the state is just two typed arrays sized to the cell grid. The `write` sink is never invoked: this
20
+ * class only ever calls `_computeBrailleCells` directly, never `rasterize`/`flush`.
21
+ */
22
+ class FrameRasterizer extends AsciifyTerminal {
23
+ constructor(columns, rows) {
24
+ super({ write: () => true, columns, rows }, { mode: "braille", colorDepth: "truecolor", synchronizedOutput: false });
25
+ this.setSize(columns, rows);
26
+ }
27
+ toCells(buffer) {
28
+ this._computeBrailleCells(buffer, false);
29
+ return { chars: this._cellChars.slice(), colors: this._cellColors.slice() };
30
+ }
31
+ }
32
+ /**
33
+ * Converts a 2×4-subpixel RGBA grid into braille cells. Grid must be `columns * 2` x `rows * 4`.
34
+ *
35
+ * @throws If the grid's dimensions don't match `columns * 2` x `rows * 4` — a caller sizing bug, not something to
36
+ * silently clip.
37
+ */
38
+ export function rasterizeToFrame(grid, columns, rows, attribution) {
39
+ if (grid.width !== columns * 2 || grid.height !== rows * 4) {
40
+ throw new Error(`rasterizeToFrame: grid is ${grid.width}x${grid.height}, expected ${columns * 2}x${rows * 4} for ${columns}x${rows} cells`);
41
+ }
42
+ const rasterizer = new FrameRasterizer(columns, rows);
43
+ const { chars, colors } = rasterizer.toCells(grid.data);
44
+ return { columns, rows, chars, colors, attribution };
45
+ }
46
+ /**
47
+ * One SGR-styled string per row. `color: false` strips styling (NO_COLOR consumers).
48
+ *
49
+ * Inkless cells (packed color 0) never emit a color escape, matching asciify's own damage emitter — a cell going from
50
+ * inked to inkless shouldn't touch color state. Each styled line ends with the SGR reset so a truncated terminal write
51
+ * never bleeds color into whatever follows.
52
+ */
53
+ export function frameToANSILines(frame, options) {
54
+ const colorEnabled = options?.color ?? true;
55
+ const lines = [];
56
+ for (let row = 0; row < frame.rows; row++) {
57
+ let line = "";
58
+ let activeColor = -1;
59
+ for (let column = 0; column < frame.columns; column++) {
60
+ const cellIndex = row * frame.columns + column;
61
+ const char = frame.chars[cellIndex];
62
+ const color = frame.colors[cellIndex];
63
+ const inked = color !== 0;
64
+ if (colorEnabled && inked && color !== activeColor) {
65
+ line += `\u001B[38;2;${(color >> 16) & 0xff};${(color >> 8) & 0xff};${color & 0xff}m`;
66
+ activeColor = color;
67
+ }
68
+ line += char === 0 ? " " : String.fromCodePoint(char);
69
+ }
70
+ lines.push(colorEnabled ? line + SGR_RESET : line);
71
+ }
72
+ return lines;
73
+ }
74
+ /**
75
+ * Writes text into cells (clipped); occupied is the label-collision bitmap, updated in place.
76
+ *
77
+ * When `occupied` is given, every target cell plus one cell of padding on each side is checked before anything is
78
+ * written — a single colliding cell rejects the whole label rather than partially overlaying it. Without `occupied`,
79
+ * writes proceed unconditionally (still clipped to the frame's bounds) and always report success.
80
+ */
81
+ export function overlayText(frame, column, row, text, color, occupied) {
82
+ if (row < 0 || row >= frame.rows)
83
+ return false;
84
+ const codePoints = Array.from(text);
85
+ if (occupied) {
86
+ for (let i = 0; i < codePoints.length; i++) {
87
+ const cellColumn = column + i;
88
+ for (let padding = -1; padding <= 1; padding++) {
89
+ const checkColumn = cellColumn + padding;
90
+ if (checkColumn < 0 || checkColumn >= frame.columns)
91
+ continue;
92
+ if (occupied[row * frame.columns + checkColumn])
93
+ return false;
94
+ }
95
+ }
96
+ }
97
+ for (let i = 0; i < codePoints.length; i++) {
98
+ const cellColumn = column + i;
99
+ if (cellColumn < 0 || cellColumn >= frame.columns)
100
+ continue;
101
+ const cellIndex = row * frame.columns + cellColumn;
102
+ frame.chars[cellIndex] = codePoints[i].codePointAt(0);
103
+ frame.colors[cellIndex] = color;
104
+ if (occupied) {
105
+ occupied[cellIndex] = 1;
106
+ }
107
+ }
108
+ return true;
109
+ }
110
+ /**
111
+ * Packs RGB channels into the frame's 0xRRGGBB color representation. Shared with Task 8's marker colors so both sides
112
+ * of the frame boundary agree on the packing.
113
+ */
114
+ export function rgbToPacked(color) {
115
+ return (color[0] << 16) | (color[1] << 8) | color[2];
116
+ }
117
+ //# sourceMappingURL=frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.js","sourceRoot":"","sources":["../frame.ts"],"names":[],"mappings":"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"}
package/out/index.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ export * from "./frame.ts";
7
+ export * from "./mercator.ts";
8
+ export * from "./mvt.ts";
9
+ export * from "./raster.ts";
10
+ export * from "./renderer.ts";
11
+ export * from "./style.ts";
12
+ export * from "./tile-source.ts";
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA"}
package/out/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ export * from "./frame.js";
7
+ export * from "./mercator.js";
8
+ export * from "./mvt.js";
9
+ export * from "./raster.js";
10
+ export * from "./renderer.js";
11
+ export * from "./style.js";
12
+ export * from "./tile-source.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ /**
7
+ * Web-Mercator projection math for map-tui.
8
+ *
9
+ * This module reimplements the standard Web-Mercator projection (EPSG:3857) math locally rather than importing from
10
+ * `@mailwoman/cartographer` or `@mailwoman/spatial`. The cartographer dependency drags maplibre-gl +
11
+ * `@mailwoman/tiger`; spatial drags `@mailwoman/core`'s shipped data. map-tui maintains a dependency-lean surface for
12
+ * the standalone `npx` story (the nuts-lookup precedent).
13
+ */
14
+ /**
15
+ * Number of pixels per tile in the Web-Mercator projection (standard: 256).
16
+ */
17
+ export declare const TILE_SIZE = 256;
18
+ export interface LonLat {
19
+ lon: number;
20
+ lat: number;
21
+ }
22
+ export interface WorldPx {
23
+ x: number;
24
+ y: number;
25
+ }
26
+ export declare function lonLatToWorldPx(lon: number, lat: number, zoom: number): WorldPx;
27
+ export declare function worldPxToLonLat(x: number, y: number, zoom: number): LonLat;
28
+ export declare function metersPerPixel(lat: number, zoom: number): number;
29
+ //# sourceMappingURL=mercator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mercator.d.ts","sourceRoot":"","sources":["../mercator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,MAAM,CAAA;AAE5B,MAAM,WAAW,MAAM;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,OAAO;IACvB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACT;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAQ/E;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ1E;AAID,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhE"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ /**
7
+ * Web-Mercator projection math for map-tui.
8
+ *
9
+ * This module reimplements the standard Web-Mercator projection (EPSG:3857) math locally rather than importing from
10
+ * `@mailwoman/cartographer` or `@mailwoman/spatial`. The cartographer dependency drags maplibre-gl +
11
+ * `@mailwoman/tiger`; spatial drags `@mailwoman/core`'s shipped data. map-tui maintains a dependency-lean surface for
12
+ * the standalone `npx` story (the nuts-lookup precedent).
13
+ */
14
+ /**
15
+ * Number of pixels per tile in the Web-Mercator projection (standard: 256).
16
+ */
17
+ export const TILE_SIZE = 256;
18
+ export function lonLatToWorldPx(lon, lat, zoom) {
19
+ const scale = TILE_SIZE * 2 ** zoom;
20
+ const sin = Math.sin((lat * Math.PI) / 180);
21
+ return {
22
+ x: scale * (lon / 360 + 0.5),
23
+ y: scale * (0.5 - (0.25 * Math.log((1 + sin) / (1 - sin))) / Math.PI),
24
+ };
25
+ }
26
+ export function worldPxToLonLat(x, y, zoom) {
27
+ const scale = TILE_SIZE * 2 ** zoom;
28
+ const n = Math.PI * (1 - (2 * y) / scale);
29
+ return {
30
+ lon: (x / scale - 0.5) * 360,
31
+ lat: (Math.atan(Math.sinh(n)) * 180) / Math.PI,
32
+ };
33
+ }
34
+ const EARTH_CIRCUMFERENCE_M = 40_075_016.686;
35
+ export function metersPerPixel(lat, zoom) {
36
+ return (EARTH_CIRCUMFERENCE_M * Math.cos((lat * Math.PI) / 180)) / (TILE_SIZE * 2 ** zoom);
37
+ }
38
+ //# sourceMappingURL=mercator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mercator.js","sourceRoot":"","sources":["../mercator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAA;AAY5B,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,GAAW,EAAE,IAAY;IACrE,MAAM,KAAK,GAAG,SAAS,GAAG,CAAC,IAAI,IAAI,CAAA;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAA;IAE3C,OAAO;QACN,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;QAC5B,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;KACrE,CAAA;AACF,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS,EAAE,IAAY;IACjE,MAAM,KAAK,GAAG,SAAS,GAAG,CAAC,IAAI,IAAI,CAAA;IACnC,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAA;IAEzC,OAAO;QACN,GAAG,EAAE,CAAC,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;QAC5B,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE;KAC9C,CAAA;AACF,CAAC;AAED,MAAM,qBAAqB,GAAG,cAAc,CAAA;AAE5C,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,IAAY;IACvD,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,IAAI,IAAI,CAAC,CAAA;AAC3F,CAAC"}
package/out/mvt.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ export interface DecodedFeature {
7
+ /**
8
+ * 1 = point, 2 = line, 3 = polygon (MVT geometry types).
9
+ */
10
+ type: 1 | 2 | 3;
11
+ /**
12
+ * Rings/lines/points in tile-local integer coords (0..extent).
13
+ */
14
+ geometry: Array<Array<{
15
+ x: number;
16
+ y: number;
17
+ }>>;
18
+ properties: Record<string, unknown>;
19
+ }
20
+ export interface DecodedLayer {
21
+ name: string;
22
+ extent: number;
23
+ features: DecodedFeature[];
24
+ }
25
+ export declare function decodeMVT(data: Uint8Array): DecodedLayer[];
26
+ //# sourceMappingURL=mvt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mvt.d.ts","sourceRoot":"","sources":["../mvt.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAEf;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAA;IAChD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,cAAc,EAAE,CAAA;CAC1B;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY,EAAE,CAkB1D"}
package/out/mvt.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @copyright Sister Software.
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+ /**
7
+ * Mapbox Vector Tile (MVT) decoding for map-tui.
8
+ *
9
+ * Wraps @mapbox/vector-tile + pbf behind a plain-data shape (DecodedLayer / DecodedFeature) so the rest of map-tui
10
+ * never touches the upstream library's lazy-geometry classes.
11
+ */
12
+ import { VectorTile } from "@mapbox/vector-tile";
13
+ import Pbf from "pbf";
14
+ export function decodeMVT(data) {
15
+ const tile = new VectorTile(new Pbf(data));
16
+ return Object.entries(tile.layers).map(([name, layer]) => {
17
+ const features = [];
18
+ for (let index = 0; index < layer.length; index++) {
19
+ const feature = layer.feature(index);
20
+ features.push({
21
+ type: feature.type,
22
+ geometry: feature.loadGeometry(),
23
+ properties: feature.properties,
24
+ });
25
+ }
26
+ return { name, extent: layer.extent, features };
27
+ });
28
+ }
29
+ //# sourceMappingURL=mvt.js.map