Skip to content
Display a Hybrid Satellite Map with Terrain Elevation

Display a Hybrid Satellite Map with Terrain Elevation

A hybrid satellite map in Maptoolkit Maps JS combines a raster tile source for satellite imagery with a raster-dem source for terrain elevation. Enabling the terrain option on the map then drapes the satellite imagery over the 3D terrain surface. Use this for outdoor navigation, route planning, or any application where both photographic context and elevation are needed at the same time.

const API_KEY = 'YOUR_API_KEY';

    const map = new maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        zoom: 12,
        center: [11.39085, 47.27574],
        pitch: 70,
        maxPitch: 85,
        attributionControl: { compact: false }
    });

    map.setStyle(`https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`, {
        transformStyle: (previousStyle, nextStyle) => {
            nextStyle.sources = {
                ...nextStyle.sources,
                // Sentinel-2 cloudless satellite imagery from EOX
                satelliteSource: {
                    type: 'raster',
                    tiles: [
                        'https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless-2020_3857/default/g/{z}/{y}/{x}.jpg'
                    ],
                    tileSize: 256
                },
                // Maptoolkit terrarium-encoded DEM for 3D terrain
                terrainSource: {
                    type: 'raster-dem',
                    tiles: [`https://vtc-cdn.maptoolkit.net/terrain/{z}/{x}/{y}.webp?api_key=${API_KEY}`],
                    tileSize: 256,
                    minzoom: 5,
                    maxzoom: 12,
                    encoding: 'terrarium'
                },
                hillshadeSource: {
                    type: 'raster-dem',
                    tiles: [`https://vtc-cdn.maptoolkit.net/terrain/{z}/{x}/{y}.webp?api_key=${API_KEY}`],
                    tileSize: 256,
                    minzoom: 5,
                    maxzoom: 12,
                    encoding: 'terrarium'
                }
            };

            nextStyle.terrain = {
                source: 'terrainSource',
                exaggeration: 1
            };

            // Add hillshade on top of satellite for terrain relief
            nextStyle.layers.push({
                id: 'hills',
                type: 'hillshade',
                source: 'hillshadeSource',
                layout: { visibility: 'visible' },
                paint: { 'hillshade-shadow-color': '#473B24' }
            });

            // Insert satellite layer below the first non-fill, non-background layer
            // so that vector labels and roads render on top of the satellite imagery
            const firstNonFillLayer = nextStyle.layers.find(
                layer => layer.type !== 'fill' && layer.type !== 'background'
            );
            nextStyle.layers.splice(nextStyle.layers.indexOf(firstNonFillLayer), 0, {
                id: 'satellite',
                type: 'raster',
                source: 'satelliteSource',
                layout: { visibility: 'visible' },
                paint: { 'raster-opacity': 1 }
            });

            return nextStyle;
        }
    });

    map.addControl(new maptoolkit.NavigationControl({
        visualizePitch: true,
        showZoom: true,
        showCompass: true
    }), 'top-right');
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Display a Hybrid Satellite Map with Terrain Elevation - Maptoolkit Maps JS</title>
    <meta property="og:description" content="Combine raster satellite tiles with 3D terrain elevation." />
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://unpkg.com/@maptoolkit/[email protected]/dist/maptoolkit.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/@maptoolkit/[email protected]/dist/maptoolkit.css" />
    <style>
        html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
        #map { width: 100%; height: 100%; }
    </style>
</head>
<body>
<div id="map"></div>
<script>
    const API_KEY = 'YOUR_API_KEY';

    const map = new maptoolkit.Map({
        container: 'map',
        apiKey: API_KEY,
        zoom: 12,
        center: [11.39085, 47.27574],
        pitch: 70,
        maxPitch: 85,
        attributionControl: { compact: false }
    });

    map.setStyle(`https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`, {
        transformStyle: (previousStyle, nextStyle) => {
            nextStyle.sources = {
                ...nextStyle.sources,
                // Sentinel-2 cloudless satellite imagery from EOX
                satelliteSource: {
                    type: 'raster',
                    tiles: [
                        'https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless-2020_3857/default/g/{z}/{y}/{x}.jpg'
                    ],
                    tileSize: 256
                },
                // Maptoolkit terrarium-encoded DEM for 3D terrain
                terrainSource: {
                    type: 'raster-dem',
                    tiles: [`https://vtc-cdn.maptoolkit.net/terrain/{z}/{x}/{y}.webp?api_key=${API_KEY}`],
                    tileSize: 256,
                    minzoom: 5,
                    maxzoom: 12,
                    encoding: 'terrarium'
                },
                hillshadeSource: {
                    type: 'raster-dem',
                    tiles: [`https://vtc-cdn.maptoolkit.net/terrain/{z}/{x}/{y}.webp?api_key=${API_KEY}`],
                    tileSize: 256,
                    minzoom: 5,
                    maxzoom: 12,
                    encoding: 'terrarium'
                }
            };

            nextStyle.terrain = {
                source: 'terrainSource',
                exaggeration: 1
            };

            // Add hillshade on top of satellite for terrain relief
            nextStyle.layers.push({
                id: 'hills',
                type: 'hillshade',
                source: 'hillshadeSource',
                layout: { visibility: 'visible' },
                paint: { 'hillshade-shadow-color': '#473B24' }
            });

            // Insert satellite layer below the first non-fill, non-background layer
            // so that vector labels and roads render on top of the satellite imagery
            const firstNonFillLayer = nextStyle.layers.find(
                layer => layer.type !== 'fill' && layer.type !== 'background'
            );
            nextStyle.layers.splice(nextStyle.layers.indexOf(firstNonFillLayer), 0, {
                id: 'satellite',
                type: 'raster',
                source: 'satelliteSource',
                layout: { visibility: 'visible' },
                paint: { 'raster-opacity': 1 }
            });

            return nextStyle;
        }
    });

    map.addControl(new maptoolkit.NavigationControl({
        visualizePitch: true,
        showZoom: true,
        showCompass: true
    }), 'top-right');
</script>
</body>
</html>

Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.

Use the Maptoolkit Connector. Create an interactive map with zoom level 12, centered around [11.39085, 47.27574], with pitch 70. Combine satellite imagery with 3D terrain elevation and hillshade.