---
title: Common Layers
source: https://vjmap.com/en/guide/usedlayers.html
---

# Common Layers

## Drawing Layer

```js
let data = {
    "type": "FeatureCollection",
    "features": [
        {
            "id": "1",
            "type": "Feature",
            "properties": {
                "name": "point1",
                "color": "#00ffff",
                "icon": "icon1"
            },
            "geometry": {
                "coordinates": [
                    6971.5378667085715,
                    14385.014943907285
                ],
                "type": "Point"
            }
        },
         {
            "id": "2",
            "type": "Feature",
            "properties": {
                "name": "polyline1",
                "color": "#00ffff"
            },
            "geometry": {
                "coordinates": [
                    [
                        3006.0952985307595,
                        15198.972934052355
                    ],
                    [
                        12606.64046359223,
                        9285.59366571666
                    ],
                    [
                        4745.324495099878,
                        3720.060236694666
                    ],
                    [
                        3006.0952985307595,
                        15198.972934052355
                    ]
                ],
                "type": "LineString"
            }
        },
        {
            "id": "3",
            "type": "Feature",
            "properties": {
                "name": "polygon1",
                "color": "#ff00ff"
            },
            "geometry": {
                "coordinates": [
                    [
                        [
                            19841.833921319492,
                            13807.589576796912
                        ],
                        [
                            19841.833921319492,
                            4207.0444117340885
                        ],
                        [
                            26172.62819683108,
                            4207.0444117340885
                        ],
                        [
                            26172.62819683108,
                            13807.589576796912
                        ],
                        [
                            19841.833921319492,
                            13807.589576796912
                        ]
                    ]
                ],
                "type": "Polygon"
            }
        }
    ]
}
map.getDrawLayer().set(map.toLngLat(data));
```
<mapFrameEx>
<pre>
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// Open map
let res = await svc.openMap({
    mapid: "sys_hello", // Map ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // Open with geometry render mode
    style: vjmap.openMapLightStyle() // When div has dark background, pass dark background style here
})
if (res.error) {
    // If open fails
    showError(res.error)
    return;
}
// Get map bounds
let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
// Create geometric projection coordinate system based on map bounds
let prj = new vjmap.GeoProjection(mapBounds);
//
// Map object
let map = new vjmap.Map({
    container: 'map', // DIV container ID
    style: svc.rasterStyle(), // Style, raster style here
    center: prj.toLngLat(mapBounds.center()), // Set map center
    zoom: 1, // Set map zoom level
    pitch: 0,
    renderWorldCopies: false // Do not display multi-screen map
});
// Associate service object and projection object
map.attach(svc, prj);
await map.onLoad();
let data = {
    "type": "FeatureCollection",
    "features": [
        {
            "id": "1",
            "type": "Feature",
            "properties": {
                "name": "point1",
                "color": "#00ffff",
                "icon": "icon1"
            },
            "geometry": {
                "coordinates": [
                    6971.5378667085715,
                    14385.014943907285
                ],
                "type": "Point"
            }
        },
         {
            "id": "2",
            "type": "Feature",
            "properties": {
                "name": "polyline1",
                "color": "#00ffff"
            },
            "geometry": {
                "coordinates": [
                    [
                        3006.0952985307595,
                        15198.972934052355
                    ],
                    [
                        12606.64046359223,
                        9285.59366571666
                    ],
                    [
                        4745.324495099878,
                        3720.060236694666
                    ],
                    [
                        3006.0952985307595,
                        15198.972934052355
                    ]
                ],
                "type": "LineString"
            }
        },
        {
            "id": "3",
            "type": "Feature",
            "properties": {
                "name": "polygon1",
                "color": "#ff00ff"
            },
            "geometry": {
                "coordinates": [
                    [
                        [
                            19841.833921319492,
                            13807.589576796912
                        ],
                        [
                            19841.833921319492,
                            4207.0444117340885
                        ],
                        [
                            26172.62819683108,
                            4207.0444117340885
                        ],
                        [
                            26172.62819683108,
                            13807.589576796912
                        ],
                        [
                            19841.833921319492,
                            13807.589576796912
                        ]
                    ]
                ],
                "type": "Polygon"
            }
        }
    ]
}
map.getDrawLayer().set(map.toLngLat(data));
</pre>
</mapFrameEx>
<br/>
<br/>


## Heatmap Layer

```js
let heatmapLayer = new vjmap.Heatmap({
    data: geoDatas,
    // heatmapWeight: contribution of a point to heatmap weight, heatmap should be more visible where contribution is greater
    heatmapWeight:  [
        'interpolate',
        ['linear'],
        ['get', 'value'],
        0, // Because 0,100 min and max above, normalize these to 0,1 range
        0,
        100,
        1
    ],
    // heatmapRadius: radius in pixels when calculating point weight for heatmap, default 30
    heatmapRadius: [
        'interpolate',
        ['linear'],
        ['zoom'],
        0, // level 0
        10, // radius 10
        9, // level 9 (other levels, linear interpolation)
        50 // radius 50
    ],
    // heatmapColor: heatmap color, set color at each heatmap value
    heatmapColor: [
        'interpolate',
        ['linear'],
        ['heatmap-density'],
        0,
        'rgba(33,102,172,0)',
        0.2,
        'rgb(103,169,207)',
        0.4,
        'rgb(209,229,240)',
        0.6,
        'rgb(253,219,199)',
        0.8,
        'rgb(239,138,98)',
        1,
        'rgb(178,24,43)'
    ],
    // heatmapIntensity: heatmap intensity, similar to heatmapWeight but sets overall heatmap intensity
    heatmapIntensity: [
        'interpolate',
        ['linear'],
        ['zoom'],
        0,
        1,
        9,
        3
    ],
    // heatmapOpacity: heatmap transparency
    heatmapOpacity: [
        'interpolate',
        ['linear'],
        ['zoom'],
        7,
        1,
        9,
        0
    ]
});
heatmapLayer.addTo(map);
```

<mapFrameEx>
<pre>
// --Heatmap Layer--
// Map service object
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// Open map
let res = await svc.openMap({
    mapid: 'sys_zp', // Map ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // Open with geometry render mode
    style: vjmap.openMapDarkStyle() // When div has dark background, pass dark background style here
})
if (res.error) {
    // If open fails
    message.error(res.error)
}
// Get map bounds
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
// Create geometric projection coordinate system based on map bounds
let prj = new vjmap.GeoProjection(mapExtent);
// Map object
let map = new vjmap.Map({
    container: 'map', // DIV container ID
    style: svc.rasterStyle(), // Style, raster style here
    center: prj.toLngLat(mapExtent.center()), // Set map center
    zoom: 2, // Set map zoom level
    renderWorldCopies: false // Do not display multi-screen map
});
// Associate service object and projection object
map.attach(svc, prj);
// Fit map to full extent based on map bounds
map.fitMapBounds();
await map.onLoad();
const mapBounds = map.getGeoBounds(0.6);
let geoDatas = []
for(let i = 0; i &lt; 1000; i++) {
    const pt = mapBounds.randomPoint();
    const data = {
        point:  map.toLngLat(pt),
        properties: {
           value: vjmap.randInt(0, 100)
        }
    }
    geoDatas.push(data)
}
let heatmapLayer = new vjmap.Heatmap({
    data: geoDatas,
    // heatmapWeight: contribution of a point to heatmap weight, heatmap should be more visible where contribution is greater
    heatmapWeight:  [
        'interpolate',
        ['linear'],
        ['get', 'value'],
        0, // Because 0,100 min and max above, normalize these to 0,1 range
        0,
        100,
        1
    ],
    // heatmapRadius: radius in pixels when calculating point weight for heatmap, default 30
    heatmapRadius: [
        'interpolate',
        ['linear'],
        ['zoom'],
        0, // level 0
        10, // radius 10
        9, // level 9 (other levels, linear interpolation)
        50 // radius 50
    ],
    // heatmapColor: heatmap color, set color at each heatmap value
    heatmapColor: [
        'interpolate',
        ['linear'],
        ['heatmap-density'],
        0,
        'rgba(33,102,172,0)',
        0.2,
        'rgb(103,169,207)',
        0.4,
        'rgb(209,229,240)',
        0.6,
        'rgb(253,219,199)',
        0.8,
        'rgb(239,138,98)',
        1,
        'rgb(178,24,43)'
    ],
    // heatmapIntensity: heatmap intensity, similar to heatmapWeight but sets overall heatmap intensity
    heatmapIntensity: [
        'interpolate',
        ['linear'],
        ['zoom'],
        0,
        1,
        9,
        3
    ],
    // heatmapOpacity: heatmap transparency
    heatmapOpacity: [
        'interpolate',
        ['linear'],
        ['zoom'],
        7,
        1,
        9,
        0
    ]
});
heatmapLayer.addTo(map);
// Click to query heatmap layer
map.on('click', heatmapLayer.layerId, (e) =&gt; {
    map.logInfo(`Features affecting heat value at this point: ${e.features.length}`);
});
</pre>
</mapFrameEx>
<br/>
<br/>



## Background Overlay Layer

```js
const background = new vjmap.BackgroundLayer({
        backgroundColor: "yellow",
        backgroundOpacity: 0.4
    }
)
background.addTo(map);
```

<mapFrameEx>
<pre>
// Map service object
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// Open map
let res = await svc.openMap({
    mapid: 'sys_zp', // Map ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // Open with geometry render mode
    style: vjmap.openMapDarkStyle() // When div has dark background, pass dark background style here
})
if (res.error) {
    // If open fails
    message.error(res.error)
}
// Get map bounds
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
// Create geometric projection coordinate system based on map bounds
let prj = new vjmap.GeoProjection(mapExtent);
// Map object
let map = new vjmap.Map({
    container: 'map', // DIV container ID
    style: svc.rasterStyle(), // Style, raster style here
    center: prj.toLngLat(mapExtent.center()), // Set map center
    zoom: 2, // Set map zoom level
    renderWorldCopies: false // Do not display multi-screen map
});
// Associate service object and projection object
map.attach(svc, prj);
// Fit map to full extent based on map bounds
map.fitMapBounds();
await map.onLoad();
const background = new vjmap.BackgroundLayer({
        backgroundColor: "yellow",
        backgroundOpacity: 0.4
    }
)
background.addTo(map);
</pre>
</mapFrameEx>
<br/>
<br/>


## Custom Watermark Background Layer

```js
// Note: image size must be power of 2, e.g. (2, 4, 8, ..., 512)
await map.loadImageEx(`testwatermark`, `https://vjmap.com/static/assets/images/testwatermark.png`);
let layers = map.getStyle().layers
const background = new vjmap.BackgroundLayer({
    backgroundPattern: "testwatermark",
    backgroundOpacity: 0.3
})
background.addTo(map);
// Move this layer to the bottom of all layers
map.moveLayer(background.layerId, layers[0].id)
```

<mapFrameEx>
<pre>
// Map service object
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// Open map
let res = await svc.openMap({
    mapid: 'sys_zp', // Map ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // Open with geometry render mode
    style: vjmap.openMapDarkStyle() // When div has dark background, pass dark background style here
})
if (res.error) {
    // If open fails
    message.error(res.error)
}
// Get map bounds
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
// Create geometric projection coordinate system based on map bounds
let prj = new vjmap.GeoProjection(mapExtent);
// Map object
let map = new vjmap.Map({
    container: 'map', // DIV container ID
    style: svc.rasterStyle(), // Style, raster style here
    center: prj.toLngLat(mapExtent.center()), // Set map center
    zoom: 2, // Set map zoom level
    renderWorldCopies: false // Do not display multi-screen map
});
// Associate service object and projection object
map.attach(svc, prj);
// Fit map to full extent based on map bounds
map.fitMapBounds();
await map.onLoad();
// Note: image size must be power of 2, e.g. (2, 4, 8, ..., 512)
await map.loadImageEx(`testwatermark`, `https://vjmap.com/static/assets/images/testwatermark.png`);
let layers = map.getStyle().layers
const background = new vjmap.BackgroundLayer({
    backgroundPattern: "testwatermark",
    backgroundOpacity: 0.3
})
background.addTo(map);
// Move this layer to the bottom of all layers
map.moveLayer(background.layerId, layers[0].id)
</pre>
</mapFrameEx>
<br/>
<br/>



## Raster Image Layer

```js
const mapBounds = map.getGeoBounds(0.001);
const coords = map.toLngLat(mapBounds.toPointArray());
map.addImageSource("park", {
    url: "https://vjmap.com/static/assets/images/park.jpg",
    coordinates: coords
})
map.addRasterLayer("parkLayer", "park", {
    rasterOpacity: 0.8,
    minzoom: 7,
    maxzoom: 20
})
```

<mapFrameEx>
<pre>
// --Raster Image Layer--
// Map service object
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// Open map
let res = await svc.openMap({
    mapid: 'sys_zp', // Map ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // Open with geometry render mode
    style: vjmap.openMapDarkStyle() // When div has dark background, pass dark background style here
})
if (res.error) {
    // If open fails
    message.error(res.error)
}
// Get map bounds
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
// Create geometric projection coordinate system based on map bounds
let prj = new vjmap.GeoProjection(mapExtent);
// Map object
let map = new vjmap.Map({
    container: 'map', // DIV container ID
    style: svc.rasterStyle(), // Style, raster style here
    center: prj.toLngLat(mapExtent.center()), // Set map center
    zoom: 9, // Set map zoom level
    pitch: 40, // Tilt angle
    renderWorldCopies: false // Do not display multi-screen map
});
// Associate service object and projection object
map.attach(svc, prj);
await map.onLoad();
const mapBounds = map.getGeoBounds(0.001);
const coords = map.toLngLat(mapBounds.toPointArray());
map.addImageSource("park", {
    url: "https://vjmap.com/static/assets/images/park.jpg",
    coordinates: coords
})
map.addRasterLayer("parkLayer", "park", {
    rasterOpacity: 0.8,
    minzoom: 7,
    maxzoom: 20
})
// Add four markers for position adjustment
const colors = ["#FF0000", "#00FF00", "#0000FF", "#FFFF00"];
const markers = []
for(let c = 0; c <  colors.length; c++) {
    const marker = new vjmap.Marker({
        color: colors[c],
        draggable: true
    });
    marker.setLngLat(coords[c]).addTo(map);
    marker.on('drag', e => {
        updateBounds();
    });
    markers.push(marker);
}
const updateBounds = () => {
    // Update position
    map.getSource("park").setCoordinates(markers.map(m => {
        let lngLat = m.getLngLat();
        return [lngLat.lng,lngLat.lat]
    }));
}
</pre>
</mapFrameEx>
<br/>
<br/>


## Sky Layer


```js
const sky = new vjmap.SkyLayer({
        skyType: 'gradient',
        skyGradient: [
            'interpolate',
            ['linear'],
            ['sky-radial-progress'],
            0.8,
            'rgba(135, 206, 235, 1.0)',
            1,
            'rgba(0,0,0,0.1)'
        ],
        skyGradientCenter: [0, 0],
        skyGradientRadius: 90,
        skyOpacity: [
            'interpolate',
            ['exponential', 0.1],
            ['zoom'],
            0,
            0.3,
            22,
            1
        ]
    }
)
sky.addTo(map);
```

<mapFrameEx>
<pre>
// --Sky Layer--
// Map service object
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// Open map
let res = await svc.openMap({
    mapid: 'sys_zp', // Map ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // Open with geometry render mode
    style: vjmap.openMapDarkStyle() // When div has dark background, pass dark background style here
})
if (res.error) {
    // If open fails
    message.error(res.error)
}
// Get map bounds
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
// Create geometric projection coordinate system based on map bounds
let prj = new vjmap.GeoProjection(mapExtent);
// Map object
let map = new vjmap.Map({
    container: 'map', // DIV container ID
    style: svc.rasterStyle(), // Style, raster style here
    center: prj.toLngLat(mapExtent.center()), // Set map center
    zoom: 2, // Set map zoom level,
    pitch: 80, // Tilt angle
    renderWorldCopies: false // Do not display multi-screen map
});
// Associate service object and projection object
map.attach(svc, prj);
// Fit map to full extent based on map bounds
map.fitMapBounds();
await map.onLoad();
const sky = new vjmap.SkyLayer({
        skyType: 'gradient',
        skyGradient: [
            'interpolate',
            ['linear'],
            ['sky-radial-progress'],
            0.8,
            'rgba(135, 206, 235, 1.0)',
            1,
            'rgba(0,0,0,0.1)'
        ],
        skyGradientCenter: [0, 0],
        skyGradientRadius: 90,
        skyOpacity: [
            'interpolate',
            ['exponential', 0.1],
            ['zoom'],
            0,
            0.3,
            22,
            1
        ]
    }
)
sky.addTo(map);
</pre>
</mapFrameEx>
<br/>
<br/>




## VJMap 3D Layer

[View online demo](https://vjmap.com/map3d/demo/#/demo/map/entity/custom/05entitycustommap2d)

<mapFrameEx>
<pre>
// --VJMap 2D map effect--VJMap 2D map as base, vjmap3d as 3D layer overlay
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// Open map
let res = await svc.openMap({
    mapid: env.exampleMapId, // Map ID
    // @ts-ignore
    mapopenway: vjmap.MapOpenWay.GeomRender, // Open with geometry render mode
    style: vjmap.openMapLightStyle() // When div has dark background, pass dark background style here
})
if (res.error) {
    // If open fails
    showError(res.error)
    return;
}
// Get map bounds
let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
// Create geometric projection coordinate system based on map bounds
let prj = new vjmap.GeoProjection(mapBounds);
let rasterStyle = svc.rasterStyle()
// @ts-ignore
rasterStyle.layers[0].paint = {
    // Custom map style, use dark color instead of default
    "raster-inverse": 1, //  0 no inverse 1 inverse
    "raster-monochrome": "#4586b6", // Modify map style, use solid color instead
    "raster-saturation": 0, // Saturation -1 to 1
    "raster-contrast": 0, // Contrast -1 to 1
}
//
// Map object
let map = new vjmap.Map({
    container: 'map', // DIV container ID
    style: rasterStyle, // Style, raster style here
    center: prj.toLngLat(mapBounds.center()), // Set map center
    zoom: 1, // Set map zoom level
    pitch: 45,
    antialias: true, // Anti-aliasing
    renderWorldCopies: false // Do not display multi-screen map
});
//
// Associate service object and projection object
map.attach(svc, prj);
await map.onLoad();
//
// Create 3D layer
let mapLayer = new vjmap3d.MapThreeLayer(map, {
    stat: {
        show: true,
        left: "0"
    }
});
map.addLayer(new vjmap.ThreeLayer({ context: mapLayer }))
let app = mapLayer.app;
// Create a pyramid
const createConeMesh = (co /*[number, number, number] | THREE.Vector3*/, opts /*{
    size?: number;
    height?: number;
    color?: THREE.ColorRepresentation;
    rotation?: boolean;
    rotationAxis?: THREE.Vector3;
    rotationSpeed?: number;
    jumpHeightScale?: number;
    jumpDuration?: number;
 }*/, app) =&gt; {
    opts ??= {};
    let size = opts.size || 0.5;
    let height = opts.height || size * 4; // Pyramid height
    // ConeGeometry with 4 divisions for pyramid effect
    let geometry = new THREE.ConeGeometry(size, height, 4);
    // Can rotate to specific angle as needed
    // geometry.rotateX(Math.PI);
    app ??= vjmap3d.Store.app;
    if (app.isMapMode) {
        geometry.rotateX(-Math.PI / 2);
        geometry.translate(0, 0, height / 2);
    } else {
        geometry.rotateX(Math.PI);
        geometry.translate(0, height / 2, 0);
    }
    // MeshBasicMaterial MeshLambertMaterial
    let material = new THREE.MeshLambertMaterial({
        color: opts.color || 0xffcc00,
    });
    let mesh = new THREE.Mesh(geometry, material);
    // Stack another pyramid on top
    let mesh2 = mesh.clone();
    if (app.isMapMode) {
        // 2D map mode
        mesh2.scale.z = 0.5;
        mesh2.position.z = height * (1 + mesh2.scale.z);
    } else {
        // 3D mode
        mesh2.scale.y = 0.5;
        mesh2.position.y = height * (1 + mesh2.scale.y);
    }
    mesh2.rotateX(Math.PI);
    mesh.add(mesh2);
    let entity =  vjmap3d.Entity.fromObject3d(mesh);
    let pt = vjmap3d.toVector3(co);
    entity.position.copy(pt);
    entity.addTo(app);
    // Rotation
    let mod = entity.addModule(vjmap3d.BehaviorModule);
    let rotateId = "";
    const startRotation = () =&gt; {
        // Start rotation
        stopRotation();
        rotateId = mod.addBehavior(vjmap3d.RotationBehavior.fromJSON({
            axis: opts.rotationAxis ?? (app.isMapMode ? new THREE.Vector3(0, 0, 1) : new THREE.Vector3(0, 1, 0)),
            speed: opts.rotationSpeed ?? 2,
            resetWhenFinalize: true // Reset to original angle when finished
        }))
    }
    const stopRotation = () =&gt; {
        // Stop rotation
        if (rotateId) {
            mod.removeBehaviorById(rotateId);
            rotateId = ""
        }
    }
    if (opts.rotation !== false) {
        startRotation()
    }
    // Jump
    opts.jumpHeightScale ??= 0.6;
    let jumpHeight = height * opts.jumpHeightScale;
    let anim;
    const startJump = () =&gt; {
        if (anim) {
            stopJump()
        }
        // Start jump
        anim = vjmap3d.animation({
            //from:  mesh.position,
            //to: new THREE.Vector3(mesh.position.x, mesh.position.y + dumpHeight, mesh.position.z),
            duration: opts.jumpDuration ?? 1000,
            onProgress(target, key, start, end, alpha, reversed) {
                if (app.isMapMode) {
                    mesh.position.z = pt.z + alpha * jumpHeight
                } else {
                    mesh.position.y = pt.y + alpha * jumpHeight
                }
            },
            yoyoForever: true
        })
    }
    const stopJump = () =&gt; {
        // Stop jump
        if (!anim) return;
        app.tweenManager.stopById(anim.id)
        anim = null;
        mesh.position.copy(pt);
    }
    if (jumpHeight != 0) {
        startJump()
    }
    return {
        entity,
        startRotation,
        stopRotation,
        startJump,
        stopJump
    };
}
// Create light wall
const createWall = (pts /*[number, number][]*/, opts /*{
    height?: number;
    flyline?: boolean;
    texture1?: string;
    repeatX?: number;
    repeatY?: number;
    color1?: THREE.ColorRepresentation;
    offsetX?: number;
    offsetY?: number;
    texture2?: string;
    color2?: THREE.ColorRepresentation;
    opacity?: number
 }*/, app /*vjmap3d.App*/) =&gt; {
    opts ??= {};
    let model = new THREE.Group(); // Group object for loaded 3D scene
    let c = [...pts];
    let geometry = new THREE.BufferGeometry(); // Empty geometry object
    let posArr = [];
    let uvrr = [];
    let h = opts.height || 1; // Wall extrusion height
    app ??= vjmap3d.Store.app;
    for (let i = 0; i &lt; c.length - 1; i++) {
        if (app.isMapMode) {
            // 2D map mode
            posArr.push(c[i][0], c[i][1], 0, c[i + 1][0], c[i + 1][1], 0, c[i + 1][0], c[i + 1][1], h);
            posArr.push(c[i][0], c[i][1], 0, c[i + 1][0], c[i + 1][1], h, c[i][0], c[i][1], h);
        } else {
            posArr.push(c[i][0], 0, c[i][1], c[i + 1][0], 0, c[i + 1][1], c[i + 1][0], h, c[i + 1][1]);
            posArr.push(c[i][0], 0, c[i][1], c[i + 1][0], h, c[i + 1][1], c[i][0], h, c[i][1]);
        }
        // All points expanded, x from 0 to 1, rectangles fill texture
        uvrr.push(i / c.length, 0, i / c.length + 2 / c.length, 0, i / c.length + 2 / c.length, 1);
        uvrr.push(i / c.length, 0, i / c.length + 2 / c.length, 1, i / c.length, 1);
    }
    // Set geometry position attribute
    geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(posArr), 3);
    // Set geometry uv attribute
    geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array(uvrr), 2);
    geometry.computeVertexNormals();
    if (opts.flyline !== false) {
        let texture = vjmap3d.ResManager.loadTexture(opts.texture1 ?? env.assetsPath + "textures/wall1.png");
        // Set wrap mode to RepeatWrapping
        texture.wrapS = THREE.RepeatWrapping
        texture.wrapT = THREE.RepeatWrapping
        texture.repeat.x = opts.repeatX || 3;// x direction repeat
        texture.repeat.y = opts.repeatY || 3;// y direction repeat
        let material = new vjmap3d.FlowMaterial({
            color: new THREE.Color(opts.color1 || 0xffff00),
            uvOffset: new THREE.Vector2(opts.offsetX || -1, opts.offsetY || 0),
            uvScale: new THREE.Vector2(texture.repeat.x, texture.repeat.y), // repeat
            map: texture,
            side: THREE.DoubleSide, // Both sides visible
            transparent: true, // Enable transparency for shader
        }, {time: app.commonUniforms.time});
        let mesh = new THREE.Mesh(geometry, material); // Mesh object
        model.add(mesh);
    }
    let texture2 =  vjmap3d.ResManager.loadTexture(opts.texture2 ??  env.assetsPath + "textures/wall2.png");
    let material2 = new THREE.MeshLambertMaterial({
        color: opts.color2 || 0x00ffff,
        map: texture2,
        side: THREE.DoubleSide, // Both sides visible
        transparent: true, // Enable transparency for shader
        opacity: opts.opacity || 0.5,// Overall opacity
        depthTest: false,
    });
    let mesh2 = new THREE.Mesh(geometry, material2); // Mesh object
    model.add(mesh2);
    let entity =  vjmap3d.Entity.fromObject3d(model);
    entity.addTo(app);
    return entity
}
// Create wave light wall
const createWaveWall = (pt /*[number, number, number] | THREE.Vector3*/, opts /* {
    size?: number;
    height?: number;
 }*/, app /*vjmap3d.App*/) =&gt; {
    opts ??= {};
    app ??= vjmap3d.Store.app;
    let model = new THREE.Group(); // Group object
    // Rectangle plane with transparent PNG texture
    let len = opts.size ?? 10;
    let height = opts.height ?? len / 10;
    const geometry = new THREE.CylinderGeometry(len, len, height, 32, 1, true);
    geometry.translate(0, height / 2, 0);
    if (app.isMapMode) {
        // 2D map mode
        geometry.rotateX(Math.PI / 2.0);
    }
    let texture = vjmap3d.ResManager.loadTexture(opts.texture ?? env.assetsPath + "textures/wavewall.png")
    let material = new THREE.MeshLambertMaterial({
        color: opts.color || 0x00ffff,
        map: texture,
        side: THREE.DoubleSide, // Both sides visible
        transparent: true, // Enable transparency for shader
        opacity: opts.opacity || 0.5,// Overall opacity
        depthTest: false,
    });
    let mesh = new THREE.Mesh(geometry, material); // Mesh object
    model.add(mesh); // Add mesh to model
    model.position.copy(vjmap3d.toVector3(pt));
    let entity =  vjmap3d.Entity.fromObject3d(model);
    entity.addTo(app);
    entity.addAnimationAction({
        duration: 2000,
        repeatForever: true
    }, ({ alpha}) =&gt; {
        model.scale.set(alpha, alpha, alpha)
    })
    return entity
}
// Get map bounds
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
// Query light wall line coordinates
let query = await svc.conditionQueryFeature({
    condition: `objectid='9BE'`, // Only write SQL condition after where, see "Server condition query and expression query" doc
    // Query all text (single-line, multi-line, block annotation, attribute text)
    // condition: `name='12' or name='13' or name='26' or name='27'`,
    fields: ""
})
let linePoints = [];
if (query.error) {
    showError(query.error)
    return
} else {
    if (query.recordCount &gt; 0) {
        let points = query.result[0].points; // Get point sequence
        linePoints = points.split(";").map(p =&gt; vjmap.GeoPoint.fromString(p));
        // Close
        linePoints.push(linePoints[0]);
        linePoints = linePoints.map(p =&gt; vjmap3d.toWorld(p).toArray())
        createWall(linePoints, {
            height: vjmap3d.toDist(mapExtent.width() / 20)
        }, app)
    }
}
let c = mapExtent.center();
linePoints[0] = vjmap3d.toWorld(c).toArray();
for(let i = 0; i &lt; linePoints.length; i++) {
    if (i != 0) {
        // Add flyline from center to point
        vjmap3d.createFlyline({
            source: [linePoints[0][0], linePoints[0][1], 0],
            target: [linePoints[i][0], linePoints[i][1], 0],
            count: 1000,
            range: 200,
            height: vjmap3d.toDist(mapExtent.width() / 5),
            color: vjmap.randomColor(),
            color2: vjmap.randomColor(),
            size: 5000
        }, app);
    }
    let pt = linePoints[i];
    pt.z = 1000;
    // Create a pyramid
    createConeMesh(pt, {
        size: 10240,
        color: vjmap.randomColor()
    }, app);
}
// Create wave ring
createWaveWall(linePoints[0], {
    color: "#ffff00",
    size: vjmap3d.toDist(mapExtent.width() / 10),
    height: vjmap3d.toDist(mapExtent.width() / 20),
}, app);
// Add fireworks background
for(let i = 0; i &lt; 20; i++) {
    let pt = vjmap3d.toWorld(mapExtent.randomPoint().toArray());
    vjmap3d.createFlyline({
        source: [pt.x, pt.y, 0],
        target: [pt.x, pt.y, vjmap3d.toDist(mapExtent.width() / 10)],
        count: 1000,
        range: 500,
        height: 0,
        color: vjmap.randomColor(),
        color2: vjmap.randomColor(),
        speed: 1,
        size: 3,
        opacity: 1.0,
    }, app);
}
</pre>
</mapFrameEx>
<br/>
<br/>



## Video Layer

```js
map.addSource("butterfly_src", {
    type: 'video',
    urls: ["https://vjmap.com/static/assets/video/butterfly.mp4"],
    coordinates: map.toLngLat(mapBounds.toPointArray())
});
map.addLayer({
    'id': 'video_layer',
    'type': 'raster',
    'source': 'butterfly_src'
});
let video = map.getSource('butterfly_src');
// Play
// video.play();
// Pause
// video.pause();
```

<mapFrameEx>
<pre>
// --Video Layer--
// Map service object
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// Open map
let res = await svc.openMap({
    mapid: 'sys_zp', // Map ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // Open with geometry render mode
    style: vjmap.openMapDarkStyle() // When div has dark background, pass dark background style here
})
if (res.error) {
    // If open fails
    message.error(res.error)
}
// Get map bounds
let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
// Create geometric projection coordinate system based on map bounds
let prj = new vjmap.GeoProjection(mapExtent);
// Map object
let map = new vjmap.Map({
    container: 'map', // DIV container ID
    style: svc.rasterStyle(), // Style, raster style here
    center: prj.toLngLat(mapExtent.center()), // Set map center
    zoom: 4, // Set map zoom level
    renderWorldCopies: false // Do not display multi-screen map
});
// Associate service object and projection object
map.attach(svc, prj);
// Fit map to full extent based on map bounds
map.fitMapBounds();
await map.onLoad();
const mapBounds = map.getGeoBounds(0.1);
map.addSource("butterfly_src", {
    type: 'video',
    urls: ["https://vjmap.com/static/assets/video/butterfly.mp4"],
    coordinates: map.toLngLat(mapBounds.toPointArray())
});
map.addLayer({
    'id': 'video_layer',
    'type': 'raster',
    'source': 'butterfly_src'
});
let video = map.getSource('butterfly_src');
// Play
// video.play();
// Pause
// video.pause();
</pre>
</mapFrameEx>
<br/>
<br/>
