@versatiles/svg-renderer 2.1.0 → 2.2.1

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 (4) hide show
  1. package/README.md +54 -26
  2. package/dist/index.d.ts +258 -67
  3. package/dist/index.js +6662 -3119
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -40,8 +40,7 @@ const svg = await renderToSVG({ style, lon: 13.4, lat: 52.5, zoom: 10 });
40
40
 
41
41
  The result is an SVG document as a string: write it to a file, or put it into a web page. This works the same in Node.js and in the browser. Without `width` and `height`, the image is 1024 × 1024 pixels.
42
42
 
43
- > [!IMPORTANT]
44
- > The style's sources must list their tile URLs (`"tiles": [...]`). A source that only points at a TileJSON document (`"url": "…/tiles.json"`) renders as an **empty map, without an error**. Hosted styles like the one above list their tiles; a style built with `@versatiles/style` needs its `inlineSources()` first.
43
+ A source may list its tile URLs (`"tiles": [...]`) or point at a TileJSON document (`"url": "…/tiles.json"`). The renderer fetches a TileJSON document once per `SVGMapRenderer` and takes `tiles`, `minzoom`, `maxzoom` and the like from it, unless the style sets them, as MapLibre GL JS does.
45
44
 
46
45
  ### Many views of one style
47
46
 
@@ -60,29 +59,35 @@ const potsdam = await map.renderSVG({ lon: 13.06, lat: 52.4, zoom: 12 });
60
59
 
61
60
  ### `renderToSVG(options): Promise<string>`
62
61
 
63
- | Option | Type | Default | Description |
64
- | -------------- | -------------------- | -------------- | -------------------------------------------------------------------------------------- |
65
- | `style` | `StyleSpecification` | _(required)_ | MapLibre style specification |
66
- | `width` | `number` | `1024` | Output width in pixels |
67
- | `height` | `number` | `1024` | Output height in pixels |
68
- | `lon` | `number` | `0` | Center longitude |
69
- | `lat` | `number` | `0` | Center latitude |
70
- | `zoom` | `number` | `2` | Zoom level |
71
- | `renderLabels` | `boolean` | `false` | Enable rendering of text labels and icons |
72
- | `fetch` | `FetchFunction` | global `fetch` | Loads tiles and sprites; see [Loading tiles your own way](#loading-tiles-your-own-way) |
62
+ | Option | Type | Default | Description |
63
+ | ------------- | ----------------------------------------------- | --------------------------- | -------------------------------------------------------------------------------------- |
64
+ | `style` | `StyleSpecification` | _(required)_ | MapLibre style specification |
65
+ | `width` | `number` | `1024` | Output width in pixels |
66
+ | `height` | `number` | `1024` | Output height in pixels |
67
+ | `lon` | `number` | style's `center`, else `0` | Center longitude |
68
+ | `lat` | `number` | style's `center`, else `0` | Center latitude |
69
+ | `zoom` | `number` | style's `zoom`, else `2` | Zoom level |
70
+ | `bearing` | `number` | style's `bearing`, else `0` | Compass direction that is up, in degrees (`90`: east is up) |
71
+ | `padding` | `number \| Padding` | `0` | Space around the center, in pixels: it sits in the middle of the area inside |
72
+ | `labels` | `'none' \| 'text' \| 'glyphs' \| 'glyphs-text'` | `'none'` | Whether and how to draw labels and icons; see [About labels](#about-labels) |
73
+ | `fetch` | `FetchFunction` | global `fetch` | Loads tiles and sprites; see [Loading tiles your own way](#loading-tiles-your-own-way) |
74
+ | `onWarning` | `(message) => void` | `console.warn` | Reports parts of the style that are not drawn; see [Warnings](#warnings) |
75
+ | `globalState` | `GlobalState` | style's `state` | Values for `global-state` expressions, over the style's defaults |
73
76
 
74
77
  ### `new SVGMapRenderer(options)`
75
78
 
76
79
  Renders many views of one style. The options stay the same for every view:
77
80
 
78
- | Option | Type | Default | Description |
79
- | --------------- | -------------------- | -------------------- | ----------------------------------------------------------------------------------------- |
80
- | `style` | `StyleSpecification` | _(required)_ | MapLibre style specification. Read once: to render a changed style, create a new instance |
81
- | `renderLabels` | `boolean` | `false` | Enable rendering of text labels and icons |
82
- | `tileCacheSize` | `number` | `134217728` (128 MB) | How much memory fetched tiles may take, in bytes. `0` keeps none |
83
- | `fetch` | `FetchFunction` | global `fetch` | Loads tiles and sprites; see [Loading tiles your own way](#loading-tiles-your-own-way) |
81
+ | Option | Type | Default | Description |
82
+ | --------------- | ----------------------------------------------- | -------------------- | ----------------------------------------------------------------------------------------- |
83
+ | `style` | `StyleSpecification` | _(required)_ | MapLibre style specification. Read once: to render a changed style, create a new instance |
84
+ | `labels` | `'none' \| 'text' \| 'glyphs' \| 'glyphs-text'` | `'none'` | Whether and how to draw labels and icons; see [About labels](#about-labels) |
85
+ | `tileCacheSize` | `number` | `134217728` (128 MB) | How much memory fetched tiles may take, in bytes. `0` keeps none |
86
+ | `fetch` | `FetchFunction` | global `fetch` | Loads tiles and sprites; see [Loading tiles your own way](#loading-tiles-your-own-way) |
87
+ | `onWarning` | `(message) => void` | `console.warn` | Reports parts of the style that are not drawn; see [Warnings](#warnings) |
88
+ | `globalState` | `GlobalState` | style's `state` | Values for `global-state` expressions, over the style's defaults |
84
89
 
85
- - **`renderSVG(view?): Promise<string>`** renders one view. `view` takes `width`, `height`, `lon`, `lat` and `zoom`, with the same defaults as `renderToSVG`. Renders may run concurrently.
90
+ - **`renderSVG(view?): Promise<string>`** renders one view. `view` takes `width`, `height`, `lon`, `lat`, `zoom`, `bearing` and `padding`, with the same defaults as `renderToSVG`. Renders may run concurrently.
86
91
  - **`project(view, [lon, lat]): [x, y] | undefined`** tells where a coordinate lands in the image of `view`, in the units of `width` and `height`, to place your own drawing on the map. On the globe, a point on the far side gives `undefined`.
87
92
  - **`unproject(view, [x, y]): [lon, lat] | undefined`** is the opposite: the coordinate shown at a position in the image, e.g. where a user clicked. It gives `undefined` where the image shows no map: next to the globe, or beyond the poles of the mercator map (about ±85°).
88
93
  - **`clearCache()`** forgets the fetched tiles and sprite, e.g. after they were updated on the server.
@@ -100,19 +105,42 @@ const map = new SVGMapRenderer({
100
105
  });
101
106
  ```
102
107
 
103
- It must return a real `Response`, and the status counts: a 404 or 204 means the server has no such tile, and the renderer remembers that; any other error status, or a rejected promise, counts as failed, so the next render tries again. The renderer only asks for tiles and sprites: a style's TileJSON sources are resolved beforehand, e.g. by `inlineSources()` of `@versatiles/style`, which uses its own requests.
108
+ It must return a real `Response`, and the status counts: a 404 or 204 means the server has no such tile, and the renderer remembers that; any other error status, or a rejected promise, counts as failed, so the next render tries again. The renderer asks it for tiles, sprites, and the TileJSON documents and GeoJSON data of the style's sources.
104
109
 
105
- ### About `renderLabels`
110
+ ### About labels
106
111
 
107
- When `renderLabels` is set to `true`, symbol layers are rendered, including text labels and sprite-based icons. By default, this option is disabled.
112
+ Labels and icons (the style's symbol layers) are drawn with `labels`:
108
113
 
109
- The SVG names each label's font (`text-font`) and leaves resolving it to whatever displays the SVG, so labels use the intended typeface only where that font is installed or provided with `@font-face`.
114
+ | `labels` | Labels drawn as |
115
+ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
116
+ | `'none'` | nothing: no labels and no icons (the default) |
117
+ | `'text'` | `<text>` naming the style's fonts (`text-font`), which whatever displays the SVG resolves: the intended typeface only where it is installed |
118
+ | `'glyphs'` | the style's own glyphs (`glyphs`), the letter shapes MapLibre GL JS draws, traced into outlines: exact in every viewer and editor, but not text |
119
+ | `'glyphs-text'` | as `'glyphs'`, with the text laid invisibly over the outlines, so labels stay selectable and searchable |
120
+
121
+ With glyphs, each glyph is defined once in the SVG's `<defs>` and placed with `<use>`, and labels are laid out with MapLibre's own metrics. A style without `glyphs`, or a label whose glyphs cannot be loaded, is drawn as `'text'`, and reported through `onWarning`.
122
+
123
+ Labels are placed as in MapLibre GL JS: at points, inside each polygon at the point farthest from its edges, and along lines, glyph by glyph; labels and icons that would overlap one placed before are left out, from the top layer down; point labels break into lines of at most `text-max-width` ems.
124
+
125
+ The option `renderLabels: true` of earlier versions still works, as `labels: 'glyphs-text'`, but is deprecated.
110
126
 
111
127
  > [!WARNING]
112
- > The rendering of labels and icons is experimental and may produce imperfect results. Since we cannot use the original layouting engine of MapLibre GL JS, there are known limitations:
128
+ > Labels are drawn without MapLibre GL JS's own layout engine, so there are known differences:
113
129
  >
114
- > - **No collision detection:** Text labels are rendered without collision detection, so labels may overlap.
115
- > - **Simplified text placement:** Labels can not be positioned along lines.
130
+ > - **Text measured with Noto Sans:** With `labels: 'text'`, text is measured with the widths of Noto Sans (the font of the VersaTiles styles). In other fonts, labels keep a little too much or too little distance.
131
+ > - **Placement:** Labels along lines are placed from the lines on screen, not from the tiles as in MapLibre, so a few labels differ.
132
+ >
133
+ > Which MapLibre GL JS features are covered, and which are not yet: [MapLibre GL JS Coverage](https://github.com/versatiles-org/versatiles-svg-renderer#maplibre-gl-js-coverage).
134
+
135
+ ### Warnings
136
+
137
+ Parts of a style the renderer does not draw are reported, not skipped silently: layer types such as `fill-extrusion` or `heatmap`, properties such as `fill-pattern`, unsupported sources, and TileJSON documents or GeoJSON data that could not be loaded. Each message goes to `onWarning`, once per renderer, by default to `console.warn`:
138
+
139
+ ```ts
140
+ const map = new SVGMapRenderer({ style, onWarning: (message) => warnings.push(message) });
141
+ ```
142
+
143
+ Pass `onWarning: () => {}` to silence them. Properties that make no difference to a flat, north-up map (e.g. `*-pitch-alignment`) are not reported, nor are symbol layers unless `renderLabels` is set. The [MapLibre GL JS coverage table](https://github.com/versatiles-org/versatiles-svg-renderer#maplibre-gl-js-coverage) lists what is supported.
116
144
 
117
145
  ### Projections
118
146
 
package/dist/index.d.ts CHANGED
@@ -2705,6 +2705,9 @@ type Feature$1 = {
2705
2705
  readonly geometry?: Array<Array<Point2D$1>>;
2706
2706
  };
2707
2707
 
2708
+ /** Values of the style's global state, by name, as read by `global-state` expressions. */
2709
+ type GlobalState = Record<string, unknown>;
2710
+
2708
2711
  type Properties = Record<string, unknown>;
2709
2712
  type Patterns = Record<string, {
2710
2713
  min: string;
@@ -2747,44 +2750,26 @@ declare class Feature implements Feature$1 {
2747
2750
  doesOverlap(bbox: Bbox): boolean;
2748
2751
  }
2749
2752
 
2750
- /**
2751
- * The part of a `Response` the renderer uses. A real `Response` has all of it; the type
2752
- * is spelled out so that using this package needs neither the DOM nor Node's type
2753
- * definitions.
2754
- */
2755
- interface FetchResponse {
2756
- readonly ok: boolean;
2757
- readonly status: number;
2758
- readonly headers: {
2759
- get(name: string): string | null;
2760
- };
2761
- arrayBuffer(): Promise<ArrayBuffer>;
2762
- json(): Promise<unknown>;
2763
- }
2764
- /**
2765
- * Loads a URL, like `fetch`: the renderer loads tiles and sprites through it. It is called
2766
- * with the URL only, and should resolve to a `Response`.
2767
- */
2768
- type FetchFunction = (url: string) => Promise<FetchResponse>;
2769
-
2770
- interface SpriteEntry {
2771
- width: number;
2772
- height: number;
2773
- x: number;
2774
- y: number;
2775
- pixelRatio: number;
2776
- sdf: boolean;
2777
- sheetDataUri: string;
2778
- sheetWidth: number;
2779
- sheetHeight: number;
2780
- }
2781
- type SpriteAtlas = Map<string, SpriteEntry>;
2782
-
2783
2753
  interface ClipCircle {
2784
2754
  x: number;
2785
2755
  y: number;
2786
2756
  radius: number;
2787
2757
  }
2758
+ type Triangle = [[number, number], [number, number], [number, number]];
2759
+ /** A triangle of a raster tile's mesh on the globe, drawn with its own affine transform. */
2760
+ interface RasterTriangle {
2761
+ /** Corners of the triangle within the tile image, in tile units (0..1). */
2762
+ source: Triangle;
2763
+ /** The same corners on screen, in pixels. */
2764
+ target: Triangle;
2765
+ }
2766
+ /** Space around the map's center, as MapLibre's `padding`, in pixels. */
2767
+ interface Padding {
2768
+ top?: number;
2769
+ right?: number;
2770
+ bottom?: number;
2771
+ left?: number;
2772
+ }
2788
2773
 
2789
2774
  /**
2790
2775
  * A drawing surface the render pipeline paints a map onto. It only describes *how* to
@@ -2795,10 +2780,19 @@ interface ClipCircle {
2795
2780
  interface Renderer {
2796
2781
  readonly width: number;
2797
2782
  readonly height: number;
2798
- drawBackgroundFill(style: BackgroundStyle): void;
2799
- drawPolygons(id: string, features: [Feature, FillStyle][]): void;
2800
- drawLineStrings(id: string, features: [Feature, LineStyle][]): void;
2783
+ /**
2784
+ * These two may be asynchronous, like {@link Renderer.drawIcons}: a pattern is a sprite
2785
+ * image, which a raster backend has to decode first.
2786
+ */
2787
+ drawBackgroundFill(style: BackgroundStyle): void | Promise<void>;
2788
+ drawPolygons(id: string, features: [Feature, FillStyle][]): void | Promise<void>;
2789
+ /** May be asynchronous, as a line pattern is a sprite image (see `drawPolygons`). */
2790
+ drawLineStrings(id: string, features: [Feature, LineStyle][]): void | Promise<void>;
2801
2791
  drawCircles(id: string, features: [Feature, CircleStyle][]): void;
2792
+ /**
2793
+ * Draws text labels, and (below) icons. Each feature is a single point: the pipeline
2794
+ * has already placed the symbol, one per point, line or polygon.
2795
+ */
2802
2796
  drawLabels(id: string, features: [Feature, SymbolStyle][]): void;
2803
2797
  /**
2804
2798
  * These two may be asynchronous. Their images arrive as data URIs, which a raster
@@ -2816,13 +2810,65 @@ interface Renderer {
2816
2810
  */
2817
2811
  finish?(): void;
2818
2812
  }
2813
+ /**
2814
+ * How labels and icons are drawn: not at all, as `<text>`, as the style's glyphs traced
2815
+ * into outlines, or as those with invisible text over them.
2816
+ */
2817
+ type LabelMode = 'none' | 'text' | 'glyphs' | 'glyphs-text';
2818
+ /**
2819
+ * A glyph's outline, in pixels at 24 per em (`GLYPH_EM`), as closed rings (to be filled
2820
+ * even-odd): x from the middle of its advance, y from the middle of its line of text.
2821
+ */
2822
+ interface GlyphOutline {
2823
+ /** Identifies the outline, for renderers that define each once. */
2824
+ key: string;
2825
+ rings: [number, number][][];
2826
+ /** The glyph's advance, in ems. */
2827
+ advance: number;
2828
+ }
2829
+ /** A glyph of a label drawn as glyphs: its outline, its middle on screen, angle and scale. */
2830
+ interface PlacedGlyph {
2831
+ outline: GlyphOutline;
2832
+ /** Where the middle of the glyph's advance lies, in the middle of its line of text. */
2833
+ x: number;
2834
+ y: number;
2835
+ /** Clockwise, in degrees. */
2836
+ angle: number;
2837
+ /** From the outline's units (24 per em) to pixels: the text size / 24. */
2838
+ scale: number;
2839
+ }
2819
2840
  interface BackgroundStyle {
2820
2841
  color: Color;
2821
2842
  opacity: number;
2843
+ /** `background-pattern`: drawn instead of `color`. */
2844
+ pattern?: FillPattern;
2845
+ }
2846
+ /**
2847
+ * A sprite image repeated over an area (`fill-pattern`, `background-pattern`), at its
2848
+ * display size (`width / pixelRatio`) and anchored to the world, as in MapLibre.
2849
+ */
2850
+ interface FillPattern {
2851
+ /** The image's name in the sprite. */
2852
+ name: string;
2853
+ sprite: SpriteEntry;
2854
+ /**
2855
+ * A screen point where one copy of the image has its top-left corner: one aligned with
2856
+ * the world's origin, so the pattern moves with the map.
2857
+ */
2858
+ origin: [number, number];
2859
+ /** How far the pattern turns around `origin`, clockwise in degrees: with the map's bearing. */
2860
+ angle?: number;
2861
+ /**
2862
+ * How much larger than its display size the image is drawn (1 if not given): MapLibre
2863
+ * scales patterns with the map from the zoom level's integer part, as its tiles are.
2864
+ */
2865
+ scale?: number;
2822
2866
  }
2823
2867
  interface FillStyle {
2824
2868
  color: Color;
2825
2869
  opacity: number;
2870
+ /** `fill-pattern`: drawn instead of `color`. */
2871
+ pattern?: FillPattern;
2826
2872
  translate: [number, number];
2827
2873
  /**
2828
2874
  * fill-outline-color. When omitted the fill `color` is used (MapLibre's
@@ -2848,14 +2894,31 @@ interface LineStyle {
2848
2894
  opacity: number;
2849
2895
  translate: [number, number];
2850
2896
  width: number;
2897
+ /** `line-pattern`: drawn instead of `color` and `dasharray`. */
2898
+ pattern?: LinePattern;
2899
+ }
2900
+ /**
2901
+ * A sprite image repeated along a line (`line-pattern`), spanning its width, as in MapLibre
2902
+ * (see `line_pattern.ts`).
2903
+ */
2904
+ interface LinePattern {
2905
+ /** The image's name in the sprite. */
2906
+ name: string;
2907
+ sprite: SpriteEntry;
2908
+ /** How far one copy of the image reaches along the line, in pixels. */
2909
+ period: number;
2851
2910
  }
2852
2911
  interface CircleStyle {
2853
2912
  color: Color;
2913
+ /** Opacity of the fill only; the stroke has its own, as in MapLibre. */
2854
2914
  opacity: number;
2855
2915
  radius: number;
2856
2916
  translate: [number, number];
2857
2917
  strokeWidth: number;
2858
2918
  strokeColor: Color;
2919
+ strokeOpacity: number;
2920
+ /** `circle-blur`: the share of the outer radius over which the circle fades out. */
2921
+ blur?: number;
2859
2922
  }
2860
2923
  interface RasterStyle {
2861
2924
  opacity: number;
@@ -2866,7 +2929,41 @@ interface RasterStyle {
2866
2929
  contrast: number;
2867
2930
  resampling: 'linear' | 'nearest';
2868
2931
  }
2932
+ /** One character of a label along a line: its center, and its angle in degrees. */
2933
+ interface GlyphPlacement {
2934
+ text: string;
2935
+ x: number;
2936
+ y: number;
2937
+ angle: number;
2938
+ }
2869
2939
  interface SymbolStyle {
2940
+ /**
2941
+ * For a label along a line: where each of its characters goes. The label is drawn glyph
2942
+ * by glyph there, centered on each position, instead of at the feature's point.
2943
+ */
2944
+ path?: GlyphPlacement[];
2945
+ /**
2946
+ * For a label drawn as the style's glyphs: each glyph's outline, where it goes and how
2947
+ * large. Drawn instead of the text.
2948
+ */
2949
+ glyphs?: PlacedGlyph[];
2950
+ /** With `glyphs`: also the text, invisible, so the label stays selectable and searchable. */
2951
+ textOverlay?: boolean;
2952
+ /**
2953
+ * For a label of several lines: each line, and the point its alignment (`justify`)
2954
+ * refers to, vertically at the middle of the line. The anchor and offset are applied
2955
+ * already; `text` is all lines.
2956
+ */
2957
+ lines?: {
2958
+ text: string;
2959
+ x: number;
2960
+ y: number;
2961
+ width?: number;
2962
+ }[];
2963
+ /** How the `lines` align at their points (`text-justify`). */
2964
+ justify?: 'left' | 'center' | 'right';
2965
+ /** `text-letter-spacing`, in ems. */
2966
+ letterSpacing?: number;
2870
2967
  text: string;
2871
2968
  size: number;
2872
2969
  font: string[];
@@ -2889,6 +2986,11 @@ interface IconStyle {
2889
2986
  color: Color;
2890
2987
  haloColor: Color;
2891
2988
  haloWidth: number;
2989
+ /**
2990
+ * Fitted to its label (`icon-text-fit`): the box the icon spans, `[left, top, right,
2991
+ * bottom]` from its point, before `icon-size`; `anchor` and `offset` then do not apply.
2992
+ */
2993
+ fit?: [number, number, number, number];
2892
2994
  }
2893
2995
  interface RasterTile {
2894
2996
  x: number;
@@ -2901,38 +3003,96 @@ interface RasterTile {
2901
3003
  * triangles, each drawn with its own affine transform.
2902
3004
  */
2903
3005
  triangles?: RasterTriangle[];
3006
+ /**
3007
+ * An image on its own (an `image` source), with no neighbouring tiles to close seams
3008
+ * with: its triangles are drawn without the underlay that reaches beyond its border.
3009
+ */
3010
+ standalone?: boolean;
2904
3011
  }
2905
- type Triangle = [[number, number], [number, number], [number, number]];
2906
- interface RasterTriangle {
2907
- /** Corners of the triangle within the tile image, in tile units (0..1). */
2908
- source: Triangle;
2909
- /** The same corners on screen, in pixels. */
2910
- target: Triangle;
3012
+ /** An image of the style's sprite: where it is on its sprite sheet, and how it stretches. */
3013
+ interface SpriteEntry {
3014
+ width: number;
3015
+ height: number;
3016
+ x: number;
3017
+ y: number;
3018
+ pixelRatio: number;
3019
+ sdf: boolean;
3020
+ sheetDataUri: string;
3021
+ sheetWidth: number;
3022
+ sheetHeight: number;
3023
+ /** The columns of the image that stretch with `icon-text-fit`, as `[from, to]` pixels. */
3024
+ stretchX?: [number, number][];
3025
+ /** The rows of the image that stretch with `icon-text-fit`, as `[from, to]` pixels. */
3026
+ stretchY?: [number, number][];
3027
+ /** The part of the image `icon-text-fit` fits to the label: `[left, top, right, bottom]` pixels. */
3028
+ content?: [number, number, number, number];
3029
+ /** Whether a fitted image keeps the content's aspect ratio, horizontally. */
3030
+ textFitWidth?: TextFit;
3031
+ /** Whether a fitted image keeps the content's aspect ratio, vertically. */
3032
+ textFitHeight?: TextFit;
2911
3033
  }
3034
+ type TextFit = 'stretchOrShrink' | 'stretchOnly' | 'proportional';
3035
+ type SpriteAtlas = Map<string, SpriteEntry>;
3036
+
3037
+ /**
3038
+ * The part of a `Response` the renderer uses. A real `Response` has all of it; the type
3039
+ * is spelled out so that using this package needs neither the DOM nor Node's type
3040
+ * definitions.
3041
+ */
3042
+ interface FetchResponse {
3043
+ readonly ok: boolean;
3044
+ readonly status: number;
3045
+ readonly headers: {
3046
+ get(name: string): string | null;
3047
+ };
3048
+ arrayBuffer(): Promise<ArrayBuffer>;
3049
+ json(): Promise<unknown>;
3050
+ }
3051
+ /**
3052
+ * Loads a URL, like `fetch`: the renderer loads tiles, sprites, TileJSON documents and GeoJSON data through it. It is called
3053
+ * with the URL only, and should resolve to a `Response`.
3054
+ */
3055
+ type FetchFunction = (url: string) => Promise<FetchResponse>;
2912
3056
 
2913
3057
  /** Options for {@link SVGMapRenderer}: what stays the same for every view of the map. */
2914
3058
  interface SVGMapRendererOptions {
2915
3059
  /**
2916
3060
  * The MapLibre style to render.
2917
3061
  *
2918
- * Its sources must list their tile URLs directly (`tiles: [...]`). A style whose
2919
- * sources only point at a TileJSON document (`url: '.../tiles.json'`) renders as an
2920
- * **empty map without any error**, because the renderer does not fetch TileJSON. Hosted
2921
- * styles such as those of VersaTiles list their tiles; a style built with
2922
- * `@versatiles/style` needs its `inlineSources()` first.
3062
+ * A source may list its tile URLs (`tiles: [...]`) or point at a TileJSON document
3063
+ * (`url: '.../tiles.json'`), which is fetched once, like the sprite, with
3064
+ * {@link SVGMapRendererOptions.fetch}. As in MapLibre GL JS, `tiles`, `minzoom`,
3065
+ * `maxzoom` and the like come from the document unless the style sets them, and
3066
+ * relative tile URLs are resolved against the document's URL. A GeoJSON source's `data`
3067
+ * may be a URL too. A source whose TileJSON or GeoJSON cannot be loaded is left out, and
3068
+ * the next render tries again.
2923
3069
  */
2924
3070
  style: StyleSpecification;
2925
3071
  /**
2926
- * Draw the style's symbol layers: text labels and icons.
2927
- *
2928
- * Off by default, because labels are the least faithful part of the output. They are
2929
- * drawn as horizontal text at the middle of their feature: labels that MapLibre curves
2930
- * along a line (`symbol-placement: "line"`, typically street names) come out straight,
2931
- * and there is no collision detection, so crowded label layers can overlap.
2932
- *
2933
- * The SVG names each label's font (`text-font`) and leaves resolving it to whatever
2934
- * displays the SVG, so labels use the intended typeface only where that font is
2935
- * installed or provided with `@font-face`.
3072
+ * Whether and how to draw the style's symbol layers, its labels and icons:
3073
+ *
3074
+ * - `'none'`: neither labels nor icons.
3075
+ * - `'text'`: labels as `<text>` naming the style's fonts (`text-font`), which whatever
3076
+ * displays the SVG resolves, so they show in the intended typeface only where that font
3077
+ * is installed. In PNG output, with the fonts given by `fonts`.
3078
+ * - `'glyphs'`: labels as the style's own glyphs (`glyphs`), the letter shapes MapLibre
3079
+ * draws, traced into outlines: exact in every viewer and editor, but not text. Each
3080
+ * glyph is defined once and reused.
3081
+ * - `'glyphs-text'`: as `'glyphs'`, with the text laid invisibly over the outlines, so labels
3082
+ * stay selectable and searchable. (In PNG output, the same as `'glyphs'`.)
3083
+ *
3084
+ * Labels are placed as in MapLibre: at points, inside each polygon at the point farthest
3085
+ * from its edges, and along lines; labels and icons that would overlap one placed before
3086
+ * are left out, from the top layer down. With glyphs, they are laid out with MapLibre's
3087
+ * own metrics; as text, with the widths of Noto Sans. A style without `glyphs`, or glyphs
3088
+ * that cannot be loaded, fall back to `'text'`, reported through `onWarning`.
3089
+ * @defaultValue `'none'`, or as given by {@link SVGMapRendererOptions.renderLabels}
3090
+ */
3091
+ labels?: LabelMode;
3092
+ /**
3093
+ * Draw the style's labels and icons.
3094
+ * @deprecated Use {@link SVGMapRendererOptions.labels}: `true` is `'glyphs-text'`, `false`
3095
+ * is `'none'`.
2936
3096
  * @defaultValue `false`
2937
3097
  */
2938
3098
  renderLabels?: boolean;
@@ -2947,7 +3107,7 @@ interface SVGMapRendererOptions {
2947
3107
  */
2948
3108
  tileCacheSize?: number;
2949
3109
  /**
2950
- * Loads tiles and sprites, like `fetch`, which is the default. Pass your own to send
3110
+ * Loads tiles, sprites, TileJSON documents and GeoJSON data, like `fetch`, which is the default. Pass your own to send
2951
3111
  * headers, go through a proxy, or keep tiles in a cache on disk.
2952
3112
  *
2953
3113
  * It must return a real `Response`, and its status matters: a 404 or 204 means the
@@ -2957,6 +3117,24 @@ interface SVGMapRendererOptions {
2957
3117
  * @defaultValue the global `fetch`
2958
3118
  */
2959
3119
  fetch?: FetchFunction;
3120
+ /**
3121
+ * Called with a message for each part of the style the renderer does not draw: a layer
3122
+ * type, a layer property, a source, or a TileJSON document that could not be loaded. Each
3123
+ * message is reported once per instance. Pass `() => {}` to silence them.
3124
+ *
3125
+ * Properties that make no difference to a flat, north-up map (e.g. `*-pitch-alignment`)
3126
+ * are not reported, nor are symbol layers unless {@link SVGMapRendererOptions.labels}
3127
+ * is set.
3128
+ * @defaultValue `console.warn`
3129
+ */
3130
+ onWarning?: (message: string) => void;
3131
+ /**
3132
+ * Values for the style's global state, read by `global-state` expressions, e.g. to
3133
+ * switch the language or a theme of a style. They override the defaults in the style's
3134
+ * `state`, like `map.setGlobalStateProperty(name, value)` in MapLibre GL JS.
3135
+ * @defaultValue the defaults of the style's `state`
3136
+ */
3137
+ globalState?: GlobalState;
2960
3138
  }
2961
3139
  /** The view to render: size, centre and zoom. */
2962
3140
  interface ViewOptions {
@@ -2972,27 +3150,40 @@ interface ViewOptions {
2972
3150
  height?: number;
2973
3151
  /**
2974
3152
  * Longitude of the map centre, in degrees.
2975
- * @defaultValue `0`
3153
+ * @defaultValue the longitude of the style's `center`, else `0`
2976
3154
  */
2977
3155
  lon?: number;
2978
3156
  /**
2979
3157
  * Latitude of the map centre, in degrees.
2980
- * @defaultValue `0`
3158
+ * @defaultValue the latitude of the style's `center`, else `0`
2981
3159
  */
2982
3160
  lat?: number;
2983
3161
  /**
2984
3162
  * Zoom level, as in MapLibre: each step doubles the scale. Fractional values are
2985
3163
  * allowed.
2986
- * @defaultValue `2`
3164
+ * @defaultValue the style's `zoom`, else `2`
2987
3165
  */
2988
3166
  zoom?: number;
3167
+ /**
3168
+ * The compass direction that is up, in degrees, as in MapLibre: `90` puts east at the
3169
+ * top. The map turns around the image's center; labels stay upright.
3170
+ * @defaultValue the style's `bearing`, else `0`
3171
+ */
3172
+ bearing?: number;
3173
+ /**
3174
+ * Space around the map's center, in pixels, as MapLibre's `padding`: the center (`lon`,
3175
+ * `lat`) sits in the middle of the area inside it, e.g. to keep it clear of a panel laid
3176
+ * over the image. A number applies to all sides.
3177
+ * @defaultValue `0`
3178
+ */
3179
+ padding?: number | Padding;
2989
3180
  }
2990
3181
  /**
2991
3182
  * Renders many views of one MapLibre style as SVG.
2992
3183
  *
2993
3184
  * Does the work that depends only on the style once, when constructed or on the first
2994
3185
  * render, instead of on every call as {@link renderToSVG} does: the style is parsed once,
2995
- * and the sprite (the icons, needed with `renderLabels`) is fetched once. Tiles are kept
3186
+ * and the sprite (the icons, needed with `labels`) and the glyphs are fetched once. Tiles are kept
2996
3187
  * too (up to {@link SVGMapRendererOptions.tileCacheSize}), so overlapping views share
2997
3188
  * them. Use it to render a batch of views, e.g. thumbnails or a series of map sections.
2998
3189
  *
@@ -3013,8 +3204,8 @@ interface ViewOptions {
3013
3204
  declare class SVGMapRenderer {
3014
3205
  #private;
3015
3206
  /**
3016
- * @param options - The style, whether to draw labels, the tile cache size, and how to
3017
- * load tiles and sprites.
3207
+ * @param options - The style, whether to draw labels, the tile cache size, how to load
3208
+ * tiles and sprites, and where to report unsupported parts of the style.
3018
3209
  * @throws If `tileCacheSize` is negative or not a number.
3019
3210
  */
3020
3211
  constructor(options: SVGMapRendererOptions);
@@ -3028,7 +3219,7 @@ declare class SVGMapRenderer {
3028
3219
  */
3029
3220
  renderSVG(view?: ViewOptions): Promise<string>;
3030
3221
  /**
3031
- * Forgets the fetched tiles and sprite, so the next render fetches them again, e.g.
3222
+ * Forgets the fetched tiles, sprite, TileJSON documents and GeoJSON data, so the next render fetches them again, e.g.
3032
3223
  * after they were updated on the server, or to free their memory.
3033
3224
  */
3034
3225
  clearCache(): void;
@@ -3109,7 +3300,7 @@ interface RenderToSVGOptions extends Omit<SVGMapRendererOptions, 'tileCacheSize'
3109
3300
  *
3110
3301
  * @example Include labels and icons
3111
3302
  * ```ts
3112
- * const svg = await renderToSVG({ style, lon: 13.4, lat: 52.52, zoom: 14, renderLabels: true });
3303
+ * const svg = await renderToSVG({ style, lon: 13.4, lat: 52.52, zoom: 14, labels: 'glyphs-text' });
3113
3304
  * ```
3114
3305
  *
3115
3306
  * @param options - What to render and how large. Only `style` is required.
@@ -3119,4 +3310,4 @@ interface RenderToSVGOptions extends Omit<SVGMapRendererOptions, 'tileCacheSize'
3119
3310
  declare function renderToSVG(options: RenderToSVGOptions): Promise<string>;
3120
3311
 
3121
3312
  export { SVGMapRenderer, renderToSVG };
3122
- export type { FetchFunction, FetchResponse, RenderToSVGOptions, SVGMapRendererOptions, ViewOptions };
3313
+ export type { FetchFunction, FetchResponse, GlobalState, RenderToSVGOptions, SVGMapRendererOptions, ViewOptions };