@versatiles/svg-renderer 2.0.0 → 2.2.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.
Files changed (4) hide show
  1. package/README.md +95 -47
  2. package/dist/index.d.ts +689 -50
  3. package/dist/index.js +15289 -11315
  4. package/package.json +4 -1
package/README.md CHANGED
@@ -29,81 +29,129 @@ No native code. The only dependency is `@types/geojson` (types only). The packag
29
29
 
30
30
  ## Usage
31
31
 
32
- ### Node.js
33
-
34
32
  ```typescript
35
33
  import { renderToSVG } from '@versatiles/svg-renderer';
36
- import { inlineSources, osm } from '@versatiles/style';
37
- import { writeFile } from 'node:fs/promises';
38
-
39
- // inlineSources() resolves the style's TileJSON sources into tile URLs, which the
40
- // renderer needs. Without it, the map comes out empty.
41
- const style = await inlineSources(osm({ theme: 'colorful' }));
42
34
 
43
- const svg = await renderToSVG({
44
- style,
45
- width: 800,
46
- height: 600,
47
- lon: 13.4,
48
- lat: 52.5,
49
- zoom: 10,
50
- });
35
+ const url = 'https://tiles.versatiles.org/assets/styles/colorful/style.json';
36
+ const style = await (await fetch(url)).json();
51
37
 
52
- await writeFile('map.svg', svg);
38
+ const svg = await renderToSVG({ style, lon: 13.4, lat: 52.5, zoom: 10 });
53
39
  ```
54
40
 
55
- > [!IMPORTANT]
56
- > The style's sources must list their tile URLs directly (`"tiles": [...]`). A source that only points at a TileJSON document (`"url": ".../tiles.json"`) is **not fetched, and the map comes out empty without an error**. Styles built with `@versatiles/style` are in that form, so pass them through its `inlineSources()` as above. Hosted style files usually list their tiles already.
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.
57
42
 
58
- ### Browser
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.
44
+
45
+ ### Many views of one style
46
+
47
+ `renderToSVG` starts from scratch on every call. To render many views of one style, create an `SVGMapRenderer` once: it parses the style once and keeps the tiles it has fetched.
59
48
 
60
49
  ```typescript
61
- import { renderToSVG } from '@versatiles/svg-renderer';
50
+ import { SVGMapRenderer } from '@versatiles/svg-renderer';
62
51
 
63
- const style = await fetch('https://tiles.versatiles.org/assets/styles/colorful/style.json').then(
64
- (r) => r.json(),
65
- );
52
+ const map = new SVGMapRenderer({ style });
66
53
 
67
- document.body.innerHTML = await renderToSVG({
68
- style,
69
- width: 800,
70
- height: 600,
71
- lon: 13.4,
72
- lat: 52.5,
73
- zoom: 10,
74
- });
54
+ const berlin = await map.renderSVG({ lon: 13.4, lat: 52.52, zoom: 12 });
55
+ const potsdam = await map.renderSVG({ lon: 13.06, lat: 52.4, zoom: 12 });
75
56
  ```
76
57
 
77
58
  ## API
78
59
 
79
60
  ### `renderToSVG(options): Promise<string>`
80
61
 
81
- | Option | Type | Default | Description |
82
- | -------------- | -------------------- | ------------ | ----------------------------------------- |
83
- | `style` | `StyleSpecification` | _(required)_ | MapLibre style specification |
84
- | `width` | `number` | `1024` | Output width in pixels |
85
- | `height` | `number` | `1024` | Output height in pixels |
86
- | `lon` | `number` | `0` | Center longitude |
87
- | `lat` | `number` | `0` | Center latitude |
88
- | `zoom` | `number` | `2` | Zoom level |
89
- | `renderLabels` | `boolean` | `false` | Enable rendering of text labels and icons |
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 |
76
+
77
+ ### `new SVGMapRenderer(options)`
78
+
79
+ Renders many views of one style. The options stay the same for every view:
80
+
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 |
89
+
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.
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`.
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°).
93
+ - **`clearCache()`** forgets the fetched tiles and sprite, e.g. after they were updated on the server.
94
+
95
+ Tiles are kept for the lifetime of the instance, regardless of their HTTP caching headers; beyond `tileCacheSize`, the least recently used ones are dropped. A tile the server does not have (404) is remembered as missing; a failed request (network error, server error) is not, so the next render tries again.
96
+
97
+ ### Loading tiles your own way
98
+
99
+ Tiles and sprites are loaded with the global `fetch`, unless you pass your own function as `fetch`: `(url: string) => Promise<Response>`. Use it to send headers (an API key, say), go through a proxy, or keep tiles on disk between runs — see [Caching tiles on disk](https://github.com/versatiles-org/versatiles-svg-renderer/blob/main/packages/png-renderer/README.md#caching-tiles-on-disk) for an example in Node.js.
100
+
101
+ ```typescript
102
+ const map = new SVGMapRenderer({
103
+ style,
104
+ fetch: (url) => fetch(url, { headers: { Authorization: `Bearer ${token}` } }),
105
+ });
106
+ ```
90
107
 
91
- ### About `renderLabels`
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.
92
109
 
93
- When `renderLabels` is set to `true`, symbol layers are rendered, including text labels and sprite-based icons. By default, this option is disabled.
110
+ ### About labels
94
111
 
95
- 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`.
112
+ Labels and icons (the style's symbol layers) are drawn with `labels`:
113
+
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.
96
126
 
97
127
  > [!WARNING]
98
- > 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:
99
129
  >
100
- > - **No collision detection:** Text labels are rendered without collision detection, so labels may overlap.
101
- > - **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.
102
144
 
103
145
  ### Projections
104
146
 
105
147
  The renderer follows the style's [`projection`](https://maplibre.org/maplibre-style-spec/projection/): `mercator` (the default), `vertical-perspective` and `globe`, which — like in MapLibre GL JS — shows the globe up to zoom 11 and blends into mercator between zoom 11 and 12. Zoom-dependent `step`/`interpolate` expressions are supported as well. On the globe, geometry is clipped at the horizon and the map is clipped to the globe's outline; everything outside of it stays transparent. Raster tiles are drawn as a mesh of small triangles, each mapped exactly onto the globe by its own affine transform.
106
148
 
149
+ ## Dependency Graph
150
+
151
+ <!--- This chapter is generated automatically --->
152
+
153
+ [![Dependency graph](https://raw.githubusercontent.com/versatiles-org/versatiles-svg-renderer/main/packages/svg-renderer/docs/dependency-graph.svg)](https://raw.githubusercontent.com/versatiles-org/versatiles-svg-renderer/main/packages/svg-renderer/docs/dependency-graph.svg)
154
+
107
155
  ## License
108
156
 
109
157
  MIT. Part of [VersaTiles](https://github.com/versatiles-org/versatiles-svg-renderer): source code, issues and the changelog are on GitHub.