@versatiles/png-renderer 2.0.0 → 2.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.
Files changed (4) hide show
  1. package/README.md +72 -28
  2. package/dist/index.d.ts +597 -64
  3. package/dist/index.js +5135 -4600
  4. package/package.json +4 -1
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  /// <reference types="geojson" />
2
+ import { Canvas } from '@napi-rs/canvas';
3
+ export { Canvas } from '@napi-rs/canvas';
4
+
2
5
  //#endregion
3
6
  //#region src/types.g.d.ts
4
7
  type ColorSpecification = string;
@@ -2559,19 +2562,407 @@ type BackgroundLayerSpecification = {
2559
2562
  };
2560
2563
  };
2561
2564
  type LayerSpecification = FillLayerSpecification | LineLayerSpecification | SymbolLayerSpecification | CircleLayerSpecification | HeatmapLayerSpecification | FillExtrusionLayerSpecification | RasterLayerSpecification | HillshadeLayerSpecification | ColorReliefLayerSpecification | BackgroundLayerSpecification;
2565
+ //#endregion
2566
+ //#region src/expression/types/color_spaces.d.ts
2567
+ /**
2568
+ * @param r Red component 0..1
2569
+ * @param g Green component 0..1
2570
+ * @param b Blue component 0..1
2571
+ * @param alpha Alpha component 0..1
2572
+ */
2573
+ type RGBColor = [r: number, g: number, b: number, alpha: number];
2574
+ /**
2575
+ * @param h Hue as degrees 0..360
2576
+ * @param c Chroma 0..~230
2577
+ * @param l Lightness as percentage 0..100
2578
+ * @param alpha Alpha component 0..1
2579
+ */
2580
+ type HCLColor = [h: number, c: number, l: number, alpha: number];
2581
+ /**
2582
+ * @param l Lightness as percentage 0..100
2583
+ * @param a A axis value -125..125
2584
+ * @param b B axis value -125..125
2585
+ * @param alpha Alpha component 0..1
2586
+ */
2587
+ type LABColor = [l: number, a: number, b: number, alpha: number];
2588
+ //#endregion
2589
+ //#region src/expression/types/color.d.ts
2590
+ type InterpolationColorSpace = 'rgb' | 'hcl' | 'lab';
2591
+ /**
2592
+ * Color representation used by WebGL.
2593
+ * Defined in sRGB color space and pre-blended with alpha.
2594
+ * @private
2595
+ */
2596
+ declare class Color {
2597
+ readonly r: number;
2598
+ readonly g: number;
2599
+ readonly b: number;
2600
+ readonly a: number;
2601
+ /**
2602
+ * @param r Red component premultiplied by `alpha` 0..1
2603
+ * @param g Green component premultiplied by `alpha` 0..1
2604
+ * @param b Blue component premultiplied by `alpha` 0..1
2605
+ * @param [alpha=1] Alpha component 0..1
2606
+ * @param [premultiplied=true] Whether the `r`, `g` and `b` values have already
2607
+ * been multiplied by alpha. If `true` nothing happens if `false` then they will
2608
+ * be multiplied automatically.
2609
+ */
2610
+ constructor(r: number, g: number, b: number, alpha?: number, premultiplied?: boolean);
2611
+ static black: Color;
2612
+ static white: Color;
2613
+ static transparent: Color;
2614
+ static red: Color;
2615
+ /**
2616
+ * Parses CSS color strings and converts colors to sRGB color space if needed.
2617
+ * Officially supported color formats:
2618
+ * - keyword, e.g. 'aquamarine' or 'steelblue'
2619
+ * - hex (with 3, 4, 6 or 8 digits), e.g. '#f0f' or '#e9bebea9'
2620
+ * - rgb and rgba, e.g. 'rgb(0,240,120)' or 'rgba(0%,94%,47%,0.1)' or 'rgb(0 240 120 / .3)'
2621
+ * - hsl and hsla, e.g. 'hsl(0,0%,83%)' or 'hsla(0,0%,83%,.5)' or 'hsl(0 0% 83% / 20%)'
2622
+ *
2623
+ * @param input CSS color string to parse.
2624
+ * @returns A `Color` instance, or `undefined` if the input is not a valid color string.
2625
+ */
2626
+ static parse(input: Color | string | undefined | null): Color | undefined;
2627
+ /**
2628
+ * Used in color interpolation and by 'to-rgba' expression.
2629
+ *
2630
+ * @returns Gien color, with reversed alpha blending, in sRGB color space.
2631
+ */
2632
+ get rgb(): RGBColor;
2633
+ /**
2634
+ * Used in color interpolation.
2635
+ *
2636
+ * @returns Gien color, with reversed alpha blending, in HCL color space.
2637
+ */
2638
+ get hcl(): HCLColor;
2639
+ /**
2640
+ * Used in color interpolation.
2641
+ *
2642
+ * @returns Gien color, with reversed alpha blending, in LAB color space.
2643
+ */
2644
+ get lab(): LABColor;
2645
+ /**
2646
+ * Lazy getter pattern. When getter is called for the first time lazy value
2647
+ * is calculated and then overwrites getter function in given object instance.
2648
+ *
2649
+ * @example:
2650
+ * const redColor = Color.parse('red');
2651
+ * let x = redColor.hcl; // this will invoke `get hcl()`, which will calculate
2652
+ * // the value of red in HCL space and invoke this `overwriteGetter` function
2653
+ * // which in turn will set a field with a key 'hcl' in the `redColor` object.
2654
+ * // In other words it will override `get hcl()` from its `Color` prototype
2655
+ * // with its own property: hcl = [calculated red value in hcl].
2656
+ * let y = redColor.hcl; // next call will no longer invoke getter but simply
2657
+ * // return the previously calculated value
2658
+ * x === y; // true - `x` is exactly the same object as `y`
2659
+ *
2660
+ * @param getterKey Getter key
2661
+ * @param lazyValue Lazily calculated value to be memoized by current instance
2662
+ * @private
2663
+ */
2664
+ private overwriteGetter;
2665
+ /**
2666
+ * Used by 'to-string' expression.
2667
+ *
2668
+ * @returns Serialized color in format `rgba(r,g,b,a)`
2669
+ * where r,g,b are numbers within 0..255 and alpha is number within 1..0
2670
+ *
2671
+ * @example
2672
+ * var purple = new Color.parse('purple');
2673
+ * purple.toString; // = "rgba(128,0,128,1)"
2674
+ * var translucentGreen = new Color.parse('rgba(26, 207, 26, .73)');
2675
+ * translucentGreen.toString(); // = "rgba(26,207,26,0.73)"
2676
+ */
2677
+ toString(): string;
2678
+ static interpolate(from: Color, to: Color, t: number, spaceKey?: InterpolationColorSpace): Color;
2679
+ }
2680
+ //#endregion
2681
+ //#region src/point2d.d.ts
2682
+ interface Point2D$1 {
2683
+ x: number;
2684
+ y: number;
2685
+ }
2686
+ //#endregion
2687
+ //#region src/expression/index.d.ts
2688
+ type Feature$1 = {
2689
+ readonly type: 0 | 1 | 2 | 3 | 'Unknown' | 'Point' | 'MultiPoint' | 'LineString' | 'MultiLineString' | 'Polygon' | 'MultiPolygon';
2690
+ readonly id?: any;
2691
+ readonly properties: {
2692
+ [_: string]: any;
2693
+ };
2694
+ readonly patterns?: {
2695
+ [_: string]: {
2696
+ min: string;
2697
+ mid: string;
2698
+ max: string;
2699
+ };
2700
+ };
2701
+ readonly dashes?: {
2702
+ [_: string]: {
2703
+ min: string;
2704
+ mid: string;
2705
+ max: string;
2706
+ };
2707
+ };
2708
+ readonly geometry?: Array<Array<Point2D$1>>;
2709
+ };
2710
+
2711
+ type Properties = Record<string, unknown>;
2712
+ type Patterns = Record<string, {
2713
+ min: string;
2714
+ mid: string;
2715
+ max: string;
2716
+ }>;
2717
+ type Geometry = Point2D[][];
2718
+ type Bbox = [number, number, number, number];
2719
+ declare class Point2D implements Point2D$1 {
2720
+ x: number;
2721
+ y: number;
2722
+ constructor(x: number, y: number);
2723
+ isZero(): boolean;
2724
+ scale(factor: number): this;
2725
+ translate(offset: Point2D): this;
2726
+ getProject2Pixel(): Point2D;
2727
+ }
2728
+ declare class Feature implements Feature$1 {
2729
+ #private;
2730
+ readonly type: 'LineString' | 'Point' | 'Polygon';
2731
+ readonly id: unknown;
2732
+ readonly properties: Properties;
2733
+ readonly patterns?: Patterns;
2734
+ readonly geometry: Geometry;
2735
+ /**
2736
+ * The boundary of a polygon for the fill-antialias outline, as open polylines — when it
2737
+ * differs from the polygon's rings, i.e. when the polygon was clipped to its tile: the
2738
+ * clipped edges along the tile border are not part of the outline.
2739
+ */
2740
+ readonly outline?: Geometry;
2741
+ constructor(opt: {
2742
+ type: 'LineString' | 'Point' | 'Polygon';
2743
+ id?: unknown;
2744
+ properties: Properties;
2745
+ patterns?: Patterns;
2746
+ geometry: Geometry;
2747
+ outline?: Geometry;
2748
+ });
2749
+ getBbox(): Bbox;
2750
+ doesOverlap(bbox: Bbox): boolean;
2751
+ }
2752
+
2753
+ /**
2754
+ * The part of a `Response` the renderer uses. A real `Response` has all of it; the type
2755
+ * is spelled out so that using this package needs neither the DOM nor Node's type
2756
+ * definitions.
2757
+ */
2758
+ interface FetchResponse {
2759
+ readonly ok: boolean;
2760
+ readonly status: number;
2761
+ readonly headers: {
2762
+ get(name: string): string | null;
2763
+ };
2764
+ arrayBuffer(): Promise<ArrayBuffer>;
2765
+ json(): Promise<unknown>;
2766
+ }
2767
+ /**
2768
+ * Loads a URL, like `fetch`: the renderer loads tiles and sprites through it. It is called
2769
+ * with the URL only, and should resolve to a `Response`.
2770
+ */
2771
+ type FetchFunction = (url: string) => Promise<FetchResponse>;
2772
+
2773
+ interface SpriteEntry {
2774
+ width: number;
2775
+ height: number;
2776
+ x: number;
2777
+ y: number;
2778
+ pixelRatio: number;
2779
+ sdf: boolean;
2780
+ sheetDataUri: string;
2781
+ sheetWidth: number;
2782
+ sheetHeight: number;
2783
+ }
2784
+ type SpriteAtlas = Map<string, SpriteEntry>;
2785
+
2786
+ interface ClipCircle {
2787
+ x: number;
2788
+ y: number;
2789
+ radius: number;
2790
+ }
2791
+
2792
+ /**
2793
+ * A drawing surface the render pipeline paints a map onto. It only describes *how* to
2794
+ * draw; how the result is handed back is up to the backend, because those differ in
2795
+ * kind: the SVG backend serializes to a string ({@link StringRenderer}), while a raster
2796
+ * backend produces an encoded image buffer.
2797
+ */
2798
+ interface Renderer {
2799
+ readonly width: number;
2800
+ readonly height: number;
2801
+ drawBackgroundFill(style: BackgroundStyle): void;
2802
+ drawPolygons(id: string, features: [Feature, FillStyle][]): void;
2803
+ drawLineStrings(id: string, features: [Feature, LineStyle][]): void;
2804
+ drawCircles(id: string, features: [Feature, CircleStyle][]): void;
2805
+ drawLabels(id: string, features: [Feature, SymbolStyle][]): void;
2806
+ /**
2807
+ * These two may be asynchronous. Their images arrive as data URIs, which a raster
2808
+ * backend has to decode before it can paint them, while the SVG backend simply embeds
2809
+ * the URI and stays synchronous. The pipeline awaits both, so layers keep their order
2810
+ * either way.
2811
+ */
2812
+ drawIcons(id: string, features: [Feature, IconStyle][], spriteAtlas: SpriteAtlas): void | Promise<void>;
2813
+ drawRasterTiles(id: string, tiles: RasterTile[], style: RasterStyle): void | Promise<void>;
2814
+ /** Restricts all drawing to a circle (the silhouette of the globe). */
2815
+ setClipCircle?(circle: ClipCircle): void;
2816
+ /**
2817
+ * Called once the map is complete: undoes any state set up for drawing it (such as the
2818
+ * clip circle), so that whoever gets the result can keep drawing on it.
2819
+ */
2820
+ finish?(): void;
2821
+ }
2822
+ interface BackgroundStyle {
2823
+ color: Color;
2824
+ opacity: number;
2825
+ }
2826
+ interface FillStyle {
2827
+ color: Color;
2828
+ opacity: number;
2829
+ translate: [number, number];
2830
+ /**
2831
+ * fill-outline-color. When omitted the fill `color` is used (MapLibre's
2832
+ * default: the antialias outline is drawn in the fill color).
2833
+ */
2834
+ outlineColor?: Color;
2835
+ /**
2836
+ * fill-antialias. When truthy, a ~1px edge outline in `outlineColor` is drawn
2837
+ * over all fills of the layer (a separate pass) so a lower feature's border
2838
+ * composites on top of later overlapping fills — matching MapLibre for
2839
+ * choropleths and stacked polygons.
2840
+ */
2841
+ antialias?: boolean;
2842
+ }
2843
+ interface LineStyle {
2844
+ blur: number;
2845
+ cap: 'butt' | 'round' | 'square';
2846
+ color: Color;
2847
+ dasharray?: number[];
2848
+ join: 'bevel' | 'miter' | 'round';
2849
+ miterLimit: number;
2850
+ offset: number;
2851
+ opacity: number;
2852
+ translate: [number, number];
2853
+ width: number;
2854
+ }
2855
+ interface CircleStyle {
2856
+ color: Color;
2857
+ opacity: number;
2858
+ radius: number;
2859
+ translate: [number, number];
2860
+ strokeWidth: number;
2861
+ strokeColor: Color;
2862
+ }
2863
+ interface RasterStyle {
2864
+ opacity: number;
2865
+ hueRotate: number;
2866
+ brightnessMin: number;
2867
+ brightnessMax: number;
2868
+ saturation: number;
2869
+ contrast: number;
2870
+ resampling: 'linear' | 'nearest';
2871
+ }
2872
+ interface SymbolStyle {
2873
+ text: string;
2874
+ size: number;
2875
+ font: string[];
2876
+ anchor: string;
2877
+ offset: [number, number];
2878
+ rotate: number;
2879
+ color: Color;
2880
+ opacity: number;
2881
+ haloColor: Color;
2882
+ haloWidth: number;
2883
+ }
2884
+ interface IconStyle {
2885
+ image: string;
2886
+ size: number;
2887
+ anchor: string;
2888
+ offset: [number, number];
2889
+ rotate: number;
2890
+ opacity: number;
2891
+ sdf: boolean;
2892
+ color: Color;
2893
+ haloColor: Color;
2894
+ haloWidth: number;
2895
+ }
2896
+ interface RasterTile {
2897
+ x: number;
2898
+ y: number;
2899
+ width: number;
2900
+ height: number;
2901
+ dataUri: string;
2902
+ /**
2903
+ * On the globe a tile cannot be drawn as a rectangle. Instead it is split into a mesh of
2904
+ * triangles, each drawn with its own affine transform.
2905
+ */
2906
+ triangles?: RasterTriangle[];
2907
+ }
2908
+ type Triangle = [[number, number], [number, number], [number, number]];
2909
+ interface RasterTriangle {
2910
+ /** Corners of the triangle within the tile image, in tile units (0..1). */
2911
+ source: Triangle;
2912
+ /** The same corners on screen, in pixels. */
2913
+ target: Triangle;
2914
+ }
2562
2915
 
2563
- /** Options for {@link renderToSVG}. */
2564
- interface RenderToSVGOptions {
2916
+ /** Options for {@link SVGMapRenderer}: what stays the same for every view of the map. */
2917
+ interface SVGMapRendererOptions {
2565
2918
  /**
2566
2919
  * The MapLibre style to render.
2567
2920
  *
2568
2921
  * Its sources must list their tile URLs directly (`tiles: [...]`). A style whose
2569
2922
  * sources only point at a TileJSON document (`url: '.../tiles.json'`) renders as an
2570
- * **empty map without any error**, because the renderer does not fetch TileJSON. Styles
2571
- * built with `@versatiles/style` come in that form, so pass them through its
2572
- * `inlineSources()` first — see the examples on {@link renderToSVG}.
2923
+ * **empty map without any error**, because the renderer does not fetch TileJSON. Hosted
2924
+ * styles such as those of VersaTiles list their tiles; a style built with
2925
+ * `@versatiles/style` needs its `inlineSources()` first.
2573
2926
  */
2574
2927
  style: StyleSpecification;
2928
+ /**
2929
+ * Draw the style's symbol layers: text labels and icons.
2930
+ *
2931
+ * Off by default, because labels are the least faithful part of the output. They are
2932
+ * drawn as horizontal text at the middle of their feature: labels that MapLibre curves
2933
+ * along a line (`symbol-placement: "line"`, typically street names) come out straight,
2934
+ * and there is no collision detection, so crowded label layers can overlap.
2935
+ *
2936
+ * The SVG names each label's font (`text-font`) and leaves resolving it to whatever
2937
+ * displays the SVG, so labels use the intended typeface only where that font is
2938
+ * installed or provided with `@font-face`.
2939
+ * @defaultValue `false`
2940
+ */
2941
+ renderLabels?: boolean;
2942
+ /**
2943
+ * How much memory the fetched tiles may take, in bytes. Tiles are kept so that
2944
+ * rendering an overlapping view does not fetch them again; beyond this size, the least
2945
+ * recently used ones are dropped. `0` keeps none.
2946
+ *
2947
+ * Tiles are kept for the lifetime of the instance, regardless of the HTTP caching
2948
+ * headers they were served with; see {@link SVGMapRenderer.clearCache}.
2949
+ * @defaultValue `134217728` (128 MB)
2950
+ */
2951
+ tileCacheSize?: number;
2952
+ /**
2953
+ * Loads tiles and sprites, like `fetch`, which is the default. Pass your own to send
2954
+ * headers, go through a proxy, or keep tiles in a cache on disk.
2955
+ *
2956
+ * It must return a real `Response`, and its status matters: a 404 or 204 means the
2957
+ * server has no such tile, which is remembered (see {@link SVGMapRendererOptions.tileCacheSize}); any other
2958
+ * error status, or a rejected promise, counts as failed, and the next render tries
2959
+ * again.
2960
+ * @defaultValue the global `fetch`
2961
+ */
2962
+ fetch?: FetchFunction;
2963
+ }
2964
+ /** The view to render: size, centre and zoom. */
2965
+ interface ViewOptions {
2575
2966
  /**
2576
2967
  * Width of the image in pixels.
2577
2968
  * @defaultValue `1024`
@@ -2598,20 +2989,100 @@ interface RenderToSVGOptions {
2598
2989
  * @defaultValue `2`
2599
2990
  */
2600
2991
  zoom?: number;
2992
+ }
2993
+ /**
2994
+ * Renders many views of one MapLibre style as SVG.
2995
+ *
2996
+ * Does the work that depends only on the style once, when constructed or on the first
2997
+ * render, instead of on every call as {@link renderToSVG} does: the style is parsed once,
2998
+ * and the sprite (the icons, needed with `renderLabels`) is fetched once. Tiles are kept
2999
+ * too (up to {@link SVGMapRendererOptions.tileCacheSize}), so overlapping views share
3000
+ * them. Use it to render a batch of views, e.g. thumbnails or a series of map sections.
3001
+ *
3002
+ * The style is read when the instance is created and is expected not to change
3003
+ * afterwards; to render a changed style, create a new instance. Renders may run
3004
+ * concurrently.
3005
+ *
3006
+ * @example Render several views of one style
3007
+ * ```ts
3008
+ * import { SVGMapRenderer } from '@versatiles/svg-renderer';
3009
+ *
3010
+ * const map = new SVGMapRenderer({ style });
3011
+ *
3012
+ * const berlin = await map.renderSVG({ lon: 13.4, lat: 52.52, zoom: 12 });
3013
+ * const potsdam = await map.renderSVG({ lon: 13.06, lat: 52.4, zoom: 12 });
3014
+ * ```
3015
+ */
3016
+ declare class SVGMapRenderer {
3017
+ #private;
2601
3018
  /**
2602
- * Draw the style's symbol layers: text labels and icons.
3019
+ * @param options - The style, whether to draw labels, the tile cache size, and how to
3020
+ * load tiles and sprites.
3021
+ * @throws If `tileCacheSize` is negative or not a number.
3022
+ */
3023
+ constructor(options: SVGMapRendererOptions);
3024
+ /**
3025
+ * Renders one view of the map as SVG. The result is a single self-contained SVG
3026
+ * document, as described on {@link renderToSVG}.
2603
3027
  *
2604
- * Off by default, because labels are the least faithful part of the output. They are
2605
- * drawn as horizontal text at the middle of their feature: labels that MapLibre curves
2606
- * along a line (`symbol-placement: "line"`, typically street names) come out straight,
2607
- * and there is no collision detection, so crowded label layers can overlap.
3028
+ * @param view - Size, centre and zoom. All optional.
3029
+ * @returns The SVG document, as a string beginning with `<svg`.
3030
+ * @throws If `width` or `height` is not positive.
3031
+ */
3032
+ renderSVG(view?: ViewOptions): Promise<string>;
3033
+ /**
3034
+ * Forgets the fetched tiles and sprite, so the next render fetches them again, e.g.
3035
+ * after they were updated on the server, or to free their memory.
3036
+ */
3037
+ clearCache(): void;
3038
+ /** Draws `view` onto `renderer`, which must already have the view's size. */
3039
+ protected draw<R extends Renderer>(renderer: R, view: ViewOptions): Promise<R>;
3040
+ /**
3041
+ * Where a coordinate lands in the image of `view`: its position in the units of
3042
+ * `width` and `height`, the same in the SVG and on the canvas of `renderCanvas`. Use
3043
+ * it to place your own drawing on the map.
2608
3044
  *
2609
- * The SVG names each label's font (`text-font`) and leaves resolving it to whatever
2610
- * displays the SVG, so labels use the intended typeface only where that font is
2611
- * installed or provided with `@font-face`.
2612
- * @defaultValue `false`
3045
+ * On the globe, a coordinate on its far side is hidden; then this returns `undefined`.
3046
+ * A position outside the image is returned as is, so it can lie beyond `width` and
3047
+ * `height`, or be negative. The map ends at about ±85.05° latitude, as in MapLibre: a
3048
+ * latitude beyond that, up to the poles, gives the position of the map's edge.
3049
+ *
3050
+ * @example Mark a place on a rendered canvas
3051
+ * ```ts
3052
+ * const view = { lon: 13.4, lat: 52.52, zoom: 12 };
3053
+ * const canvas = await map.renderCanvas(view);
3054
+ * const [x, y] = map.project(view, [13.3777, 52.5163])!; // Brandenburg Gate
3055
+ * const ctx = canvas.getContext('2d');
3056
+ * ctx.beginPath();
3057
+ * ctx.arc(x, y, 6, 0, 2 * Math.PI);
3058
+ * ctx.fill();
3059
+ * ```
3060
+ *
3061
+ * @param view - The view the image was rendered with.
3062
+ * @param lonLat - Longitude and latitude, in degrees.
3063
+ * @returns `[x, y]`, or `undefined` for a point hidden on the globe, and for a
3064
+ * longitude or latitude that is not a finite number.
3065
+ * @throws If `width` or `height` is not positive.
2613
3066
  */
2614
- renderLabels?: boolean;
3067
+ project(view: ViewOptions, lonLat: [number, number]): [number, number] | undefined;
3068
+ /**
3069
+ * The opposite of {@link SVGMapRenderer.project}: the coordinate shown at a position in
3070
+ * the image of `view`, e.g. where a user clicked on it.
3071
+ *
3072
+ * Where the image shows no map — next to the globe, or beyond the poles of the mercator
3073
+ * map (about ±85°) — this returns `undefined`. The longitude is between -180 and 180.
3074
+ *
3075
+ * @param view - The view the image was rendered with.
3076
+ * @param xy - A position in the units of `width` and `height`.
3077
+ * @returns `[lon, lat]` in degrees, or `undefined` where there is no map, and for a
3078
+ * position that is not a finite number.
3079
+ * @throws If `width` or `height` is not positive.
3080
+ */
3081
+ unproject(view: ViewOptions, xy: [number, number]): [number, number] | undefined;
3082
+ }
3083
+
3084
+ /** Options for {@link renderToSVG}: the style and the view, in one object. */
3085
+ interface RenderToSVGOptions extends Omit<SVGMapRendererOptions, 'tileCacheSize'>, ViewOptions {
2615
3086
  }
2616
3087
  /**
2617
3088
  * Renders a MapLibre style to an SVG image.
@@ -2625,38 +3096,18 @@ interface RenderToSVGOptions {
2625
3096
  * `@versatiles/svg-renderer`, `@versatiles/png-renderer` and
2626
3097
  * `@versatiles/maplibre-svg-export`; the examples import it from the first.
2627
3098
  *
2628
- * @example Render a map in Node.js
2629
- * ```ts
2630
- * import { renderToSVG } from '@versatiles/svg-renderer';
2631
- * import { inlineSources, osm } from '@versatiles/style';
2632
- * import { writeFile } from 'node:fs/promises';
2633
- *
2634
- * // inlineSources() resolves the style's TileJSON sources into tile URLs,
2635
- * // which the renderer needs. Without it the map comes out empty.
2636
- * const style = await inlineSources(osm({ theme: 'colorful' }));
2637
- *
2638
- * const svg = await renderToSVG({
2639
- * style,
2640
- * width: 800,
2641
- * height: 600,
2642
- * lon: 13.4, // Berlin
2643
- * lat: 52.52,
2644
- * zoom: 12,
2645
- * });
3099
+ * Each call starts from scratch: it parses the style and fetches the tiles and the
3100
+ * sprite again. To render many views of one style, use {@link SVGMapRenderer}, which
3101
+ * keeps them between renders.
2646
3102
  *
2647
- * await writeFile('berlin.svg', svg);
2648
- * ```
2649
- *
2650
- * @example Render a hosted style in the browser
3103
+ * @example Render a map
2651
3104
  * ```ts
2652
3105
  * import { renderToSVG } from '@versatiles/svg-renderer';
2653
3106
  *
2654
- * // This hosted style already lists its tile URLs, so it can be used directly.
2655
- * const style = await fetch(
2656
- * 'https://tiles.versatiles.org/assets/styles/colorful/style.json',
2657
- * ).then((response) => response.json());
3107
+ * const url = 'https://tiles.versatiles.org/assets/styles/colorful/style.json';
3108
+ * const style = await (await fetch(url)).json();
2658
3109
  *
2659
- * document.body.innerHTML = await renderToSVG({ style, width: 800, height: 600, zoom: 3 });
3110
+ * const svg = await renderToSVG({ style, lon: 13.4, lat: 52.52, zoom: 12 });
2660
3111
  * ```
2661
3112
  *
2662
3113
  * @example Include labels and icons
@@ -2704,6 +3155,10 @@ interface RenderToPNGOptions extends RenderToSVGOptions {
2704
3155
  * directly instead of producing SVG — no browser or SVG rasterizer needed. Satellite and
2705
3156
  * other raster tiles in WebP work too, which many SVG rasterizers cannot decode.
2706
3157
  *
3158
+ * Each call starts from scratch: it parses the style and fetches the tiles and the
3159
+ * sprite again. To render many views of one style, use {@link PNGMapRenderer}, which
3160
+ * keeps them between renders.
3161
+ *
2707
3162
  * **Node.js only.** It draws with the native canvas backend `@napi-rs/canvas`, a dependency
2708
3163
  * of this package that npm installs together with it, including the prebuilt binary for
2709
3164
  * the platform. If that binary is missing (e.g. installed with `--omit=optional`, or
@@ -2713,40 +3168,24 @@ interface RenderToPNGOptions extends RenderToSVGOptions {
2713
3168
  * @example Render a map to a file
2714
3169
  * ```ts
2715
3170
  * import { renderToPNG } from '@versatiles/png-renderer';
2716
- * import { inlineSources, osm } from '@versatiles/style';
2717
3171
  * import { writeFile } from 'node:fs/promises';
2718
3172
  *
2719
- * // As with renderToSVG, the style's sources must list their tile URLs.
2720
- * const style = await inlineSources(osm({ theme: 'colorful' }));
2721
- *
2722
- * const png = await renderToPNG({
2723
- * style,
2724
- * width: 800,
2725
- * height: 600,
2726
- * lon: 13.4, // Berlin
2727
- * lat: 52.52,
2728
- * zoom: 12,
2729
- * scale: 2, // 1600 × 1200 pixels, for high-resolution screens
2730
- * });
3173
+ * const url = 'https://tiles.versatiles.org/assets/styles/colorful/style.json';
3174
+ * const style = await (await fetch(url)).json();
2731
3175
  *
3176
+ * const png = await renderToPNG({ style, lon: 13.4, lat: 52.52, zoom: 12 });
2732
3177
  * await writeFile('berlin.png', png);
2733
3178
  * ```
2734
3179
  *
2735
3180
  * @example Draw labels in the style's own fonts
2736
3181
  * ```ts
2737
- * // The VersaTiles styles use Noto Sans. One source is `npm install @fontsource/noto-sans`
2738
- * // (its "latin" files cover Western European scripts; it ships others alongside).
2739
- * const files = 'node_modules/@fontsource/noto-sans/files';
2740
3182
  * const png = await renderToPNG({
2741
3183
  * style,
2742
3184
  * lon: 13.4,
2743
3185
  * lat: 52.52,
2744
3186
  * zoom: 14,
2745
3187
  * renderLabels: true,
2746
- * fonts: {
2747
- * noto_sans_regular: `${files}/noto-sans-latin-400-normal.woff2`,
2748
- * noto_sans_bold: `${files}/noto-sans-latin-700-normal.woff2`,
2749
- * },
3188
+ * fonts: { noto_sans_regular: 'fonts/NotoSans-Regular.ttf', noto_sans_bold: 'fonts/NotoSans-Bold.ttf' },
2750
3189
  * });
2751
3190
  * ```
2752
3191
  *
@@ -2759,6 +3198,100 @@ interface RenderToPNGOptions extends RenderToSVGOptions {
2759
3198
  * positive, or if a font in `fonts` cannot be loaded.
2760
3199
  */
2761
3200
  declare function renderToPNG(options: RenderToPNGOptions): Promise<Uint8Array>;
3201
+ /**
3202
+ * Renders a MapLibre style onto a canvas, to draw on top of it or to encode it in another
3203
+ * format than PNG. Takes the options of {@link renderToPNG}; the canvas is described on
3204
+ * {@link PNGMapRenderer.renderCanvas}.
3205
+ *
3206
+ * @example Save a map as JPEG
3207
+ * ```ts
3208
+ * const canvas = await renderToCanvas({ style, lon: 13.4, lat: 52.52, zoom: 12 });
3209
+ * await writeFile('berlin.jpg', await canvas.encode('jpeg', 85));
3210
+ * ```
3211
+ *
3212
+ * @returns A `Canvas` of `@napi-rs/canvas`.
3213
+ * @throws As {@link renderToPNG}.
3214
+ */
3215
+ declare function renderToCanvas(options: RenderToPNGOptions): Promise<Canvas>;
3216
+ /** Options for {@link PNGMapRenderer}: those of {@link SVGMapRenderer}, plus fonts. */
3217
+ interface PNGMapRendererOptions extends SVGMapRendererOptions {
3218
+ /**
3219
+ * Font files to draw labels with, as `{ "<text-font name>": "<path to a font file>" }`.
3220
+ * See {@link RenderToPNGOptions.fonts}.
3221
+ */
3222
+ fonts?: Record<string, string>;
3223
+ }
3224
+ /** The view to render as PNG: {@link ViewOptions}, plus the pixel density. */
3225
+ interface PNGViewOptions extends ViewOptions {
3226
+ /**
3227
+ * Pixel density, like a device pixel ratio. The image is `width * scale` by
3228
+ * `height * scale` pixels, while the map is laid out as if it were `width` by `height`.
3229
+ * See {@link RenderToPNGOptions.scale}.
3230
+ * @defaultValue `1`
3231
+ */
3232
+ scale?: number;
3233
+ }
3234
+ /**
3235
+ * Renders many views of one MapLibre style, as PNG or SVG.
3236
+ *
3237
+ * An {@link SVGMapRenderer} that can also render PNG, sharing everything it keeps between
3238
+ * the two formats: the parsed style, the sprite and the tiles. For PNG it also keeps the
3239
+ * decoded tile and sprite images. Like {@link renderToPNG}, PNG rendering works in Node.js
3240
+ * only.
3241
+ *
3242
+ * @example Render several views of one style
3243
+ * ```ts
3244
+ * import { PNGMapRenderer } from '@versatiles/png-renderer';
3245
+ *
3246
+ * const map = new PNGMapRenderer({ style });
3247
+ *
3248
+ * const berlin = await map.renderPNG({ lon: 13.4, lat: 52.52, zoom: 12 });
3249
+ * const potsdam = await map.renderPNG({ lon: 13.06, lat: 52.4, zoom: 12 });
3250
+ * const svg = await map.renderSVG({ lon: 13.4, lat: 52.52, zoom: 12 });
3251
+ * ```
3252
+ */
3253
+ declare class PNGMapRenderer extends SVGMapRenderer {
3254
+ #private;
3255
+ /** @param options - The style, whether to draw labels, the tile cache size, and fonts. */
3256
+ constructor(options: PNGMapRendererOptions);
3257
+ /**
3258
+ * Renders one view of the map as PNG.
3259
+ *
3260
+ * @param view - Size, centre, zoom and pixel density. All optional.
3261
+ * @returns The encoded PNG file, as described on {@link renderToPNG}.
3262
+ * @throws If the `@napi-rs/canvas` binary cannot be loaded, if `width`, `height` or
3263
+ * `scale` is not positive, or if a font in `fonts` cannot be loaded.
3264
+ */
3265
+ renderPNG(view?: PNGViewOptions): Promise<Uint8Array>;
3266
+ /**
3267
+ * Renders one view of the map onto a canvas, to draw on top of it or to encode it in
3268
+ * another format.
3269
+ *
3270
+ * The canvas is `width × scale` by `height × scale` pixels. Its 2D context is in its
3271
+ * default state, except that it is scaled by `scale`: draw in the same units as
3272
+ * `width` and `height`. Encode it with `canvas.encode('webp' | 'jpeg' | 'avif' | 'png')`
3273
+ * or `canvas.toBuffer(…)`.
3274
+ *
3275
+ * The canvas records what is drawn and only paints the pixels when they are needed,
3276
+ * usually when it is encoded, so drawing more on it costs little. Reading pixels
3277
+ * (`getImageData`) paints everything recorded so far, every time it is called.
3278
+ *
3279
+ * @example Save a map as WebP
3280
+ * ```ts
3281
+ * const canvas = await map.renderCanvas({ lon: 13.4, lat: 52.52, zoom: 12 });
3282
+ * await writeFile('berlin.webp', await canvas.encode('webp'));
3283
+ * ```
3284
+ *
3285
+ * To draw on the map, see the example on {@link SVGMapRenderer.project}.
3286
+ *
3287
+ * @param view - Size, centre, zoom and pixel density. All optional.
3288
+ * @returns A `Canvas` of `@napi-rs/canvas`.
3289
+ * @throws As {@link PNGMapRenderer.renderPNG}.
3290
+ */
3291
+ renderCanvas(view?: PNGViewOptions): Promise<Canvas>;
3292
+ /** Forgets the fetched tiles and sprite, and the decoded images. */
3293
+ clearCache(): void;
3294
+ }
2762
3295
 
2763
- export { renderToPNG, renderToSVG };
2764
- export type { RenderToPNGOptions, RenderToSVGOptions };
3296
+ export { PNGMapRenderer, SVGMapRenderer, renderToCanvas, renderToPNG, renderToSVG };
3297
+ export type { FetchFunction, FetchResponse, PNGMapRendererOptions, PNGViewOptions, RenderToPNGOptions, RenderToSVGOptions, SVGMapRendererOptions, ViewOptions };