# SVG Overlay

SvgOverlay creates an SVG overlay within a geographic bounds that scales with zoom level.

SVG overlays have lower performance and are not recommended for heavy use. They may also cause distortion at very high zoom levels and become invisible at large pitch angles.

# Type Definitions

// Creates an SVG overlay within a geographic bounds that scales with zoom level
export  class SvgOverlay {
    constructor(options?: {
        /** Maximum zoom level to display */
        minZoom?: number;
        /** Minimum zoom level to display */
        maxZoom?: number;
        /** Maximum pitch angle to display */
        maxPitch?: number;
        /** Auto-update div size (set to true if SVG needs to scale) */
        updateDivSize?: boolean;
        /** Maximum div size when scaling; beyond this, pixel scaling is used */
        maxDivSize?: number;
        /** Do not auto-update bounds when move ends */
        noUpdateBoundsWhenMoveend?: boolean;
        /** Class name */
        divClassName?: string;
        /** Maximum pixel width when initializing SVG, default 1000 */
        svgMaxWidth?: number;
        /** Maximum pixel height when initializing SVG, default 1000 */
        svgMaxHeight?: number;
        /** SVG initialization offset in pixels, default 100 */
        svgOffset?: number;
    });
    addTo(map: Map, insertId?: string | HTMLElement): void;
    remove(): void;
    /**
     * Add SVG element
     * @param element Element to add
     * @param noUpdate Do not update immediately
     */
    addElement(element: SvgElementOptions | string, noUpdate?: boolean): void;
    /**
     * Add SVG elements
     * @param elements Multiple element contents
     * @param noUpdate Do not update immediately
     */
    addElements(elements: SvgElementOptions[], noUpdate?: boolean): void;
    /**
     * Get SVG container
     */
    getSvgContainer(): SVGSVGElement;
    /**
     * Get all elements
     */
    getElements(): SvgElementOptions[];
    /**
     * Remove element(s)
     */
    removeElements(id: string[] | string): SvgElementOptions[];
    /**
     * Update element(s)
     */
    updateElements(elements: SvgElementOptions[] | SvgElementOptions): SvgElementOptions[];
    /**
     * Circle center-radius attribute string
     */
    static attr_cx_cy_r(cx: number, cy: number, r: number): string;
    /**
     * Ellipse center-radius attribute string
     */
    static attr_cx_cy_rx_ry(cx: number, cy: number, rx: number, ry: number): string;
    /**
     * Line coordinate attribute string
     */
    static attr_x1_y1_x2_y2(x1: number, y1: number, x2: number, y2: number): string;
    /**
     * Coordinate width-height attribute string
     */
    static attr_x_y_w_h(x: number, y: number, w: number, h: number): string;
    /**
     * Coordinate attribute string
     */
    static attr_x_y(x: number, y: number): string;
    /**
     * Font attribute string
     */
    static attr_fontsize(fontsize: number): string;
    /**
     * Length-related attribute string
     */
    static attr_length(len: number): string;
    /**
     * Point coordinate attribute string
     */
    static attr_point(point: GeoPoint, joinComma?: boolean): string;
    /**
     * Coordinate sequence attribute string
     */
    static attr_points(points: GeoPoint[]): string;
    /**
     * Path sequence attribute string
     */
    static attr_path(points: GeoPoint[]): string;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

# Usage

let mapBounds = map.getGeoBounds();
let len = mapBounds.width() / 5;
let svgOverlay = new vjmap.SvgOverlay({
    maxZoom: 5, // All hidden at zoom level 5
    maxPitch: 60 // Hidden when pitch exceeds 60 degrees
});
svgOverlay.addTo(map);

// Add a gradient
svgOverlay.addElement(`
          <defs>
            <radialGradient id="grad1" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
              <stop offset="0%" style="stop-color:${vjmap.randomColor()};stop-opacity:0.8" />
              <stop offset="100%" style="stop-color:${vjmap.randomColor()};stop-opacity:1" />
            </radialGradient>
          </defs>
        `)
const getStyle = (style = {}) => {
    return Object.keys(style).map(key => `${key}:${style[key]}`).join(";")
}
const svgCircleHtml = (id, point, r, style) => {
    return ` <circle id="${id}" ${vjmap.SvgOverlay.attr_cx_cy_r(point.x, point.y, r)} style="${getStyle(style)}"/>`
}

const addCircle = ()=> {
    let id = "circle" + vjmap.RandomID();
    return svgOverlay.addElement({
        html: svgCircleHtml(id, mapBounds.randomPoint(), len / 4, {fill: "url(#grad1)", stroke: vjmap.randomColor(), "stroke-width": 3, cursor: "pointer"}),
        event: svgParentElement => {
            // Event callback
            let ele = svgParentElement.getElementById(id);
            if (!ele) return;
            ele.addEventListener("mouseover", e => ele.style.opacity = 0.6);
            ele.addEventListener("mouseout", e => ele.style.opacity = 1.0);
            ele.addEventListener("click", e => message.info("Clicked circle " + id));
        }
    })
}
for(let i = 0; i < 10; i++) {
    addCircle();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
显示代码
全屏显示


# Examples

image-20240928104939832

SVG overlay (opens new window)

SVG events (opens new window)

SVG animation (opens new window)