Skip to main content

Map Styles Symbol

Map styles with a symbol style marker layer on a styledata event.

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <link rel="stylesheet" href="https://maps-sdk.trimblemaps.com/v4/trimblemaps-4.2.3.css" />
        <script src="https://maps-sdk.trimblemaps.com/v4/trimblemaps-4.2.3.js"></script>
        <style>
            body { margin: 0; padding: 0; }

            #map {
                position: absolute;
                top: 0;
                bottom: 0;
                width: 100%;
            }

            #menu {
                position: absolute;
                background: #fff;
                padding: 10px;
                font-family: 'Open Sans', sans-serif;
            }
        </style>
    </head>
    <body>
        <div id="map"></div>
        <div id="menu">
            <input id="transportation" type="radio" name="rtoggle" value="TRANSPORTATION" checked="checked" />
            <label for="transportation">transportation</label>
            <input id="basic" type="radio" name="rtoggle" value="BASIC" />
            <label for="basic">basic</label>
            <input id="datalight" type="radio" name="rtoggle" value="DATALIGHT" />
            <label for="datalight">datalight</label>
            <input id="datadark" type="radio" name="rtoggle" value="DATADARK" />
            <label for="datadark">datadark</label>
            <input id="terrain" type="radio" name="rtoggle" value="TERRAIN" />
            <label for="terrain">terrain</label>
            <input id="satellite" type="radio" name="rtoggle" value="SATELLITE" />
            <label for="satellite">satellite</label>
        </div>

        <script>
            // This example shows how to switch map styles with a custom symbol layer.

            TrimbleMaps.setAPIKey('YOUR_API_KEY_HERE');
            const map = new TrimbleMaps.Map({
            container: 'map', // container id
            style: TrimbleMaps.Common.Style.TRANSPORTATION, // initial style
            center: [-74.60018, 40.36144], // starting position [lng, lat]
            zoom: 12 // starting zoom
        });

        // Data for our custom marker
        const geoJsonData = {
            type: 'geojson',
            data: {
                type: 'FeatureCollection',
                features: [{
                    type: 'Feature',
                    properties: {
                        name: 'Trimble Maps HQ'
                    },
                    geometry: {
                        type: 'Point',
                        coordinates: [-74.60018, 40.36144]
                    }
                }]
            }
        };

        // Function to switch map styles
        function switchLayer(event) {
            const styleId = event.target.value;
            map.setStyle(TrimbleMaps.Common.Style[styleId]);
        }

        // Attach event listener to radio buttons
        const layerList = document.getElementById('menu');
        const inputs = layerList.getElementsByTagName('input');
        for (const input of inputs) {
            input.onclick = switchLayer;
        }

        // This event fires when the map's style has been successfully loaded or updated.
        // We make the callback function 'async' to use 'await'.
        map.on('styledata', async (e) => {
            const layerId = 'hqPoints';
            const sourceId = 'hqSource';
            const markerIconId = 'marker-icon';
            const markerImageUrl = 'https://developer.trimblemaps.com/maps-sdk/img/marker_blue.png';

            try {
                // 1. Add the data source only if it doesn't already exist.
                if (!map.getSource(sourceId)) {
                    map.addSource(sourceId, geoJsonData);
                }

                // 2. Load the image asynchronously. 'await' pauses the function
                //    until the image is loaded, preventing a race condition.
                const image = await map.loadImage(markerImageUrl);

                // 3. Add the loaded image to the map's style with a unique ID,
                //    but only if it hasn't been added before.
                if (!map.hasImage(markerIconId)) {
                    // The resolved object from loadImage has a 'data' property
                    // which contains the image data needed by addImage.
                    map.addImage(markerIconId, image.data);
                }

                // 4. Finally, add the symbol layer. Because of 'await', this code
                //    only runs after the image is successfully loaded and added.
                if (!map.getLayer(layerId)) {
                    map.addLayer({
                        id: layerId,
                        type: 'symbol',
                        source: sourceId, // link to the source
                        layout: {
                            'icon-image': markerIconId, // use the image we just added
                            'icon-size': 1,
                            'icon-allow-overlap': true, // prevent icon from disappearing on zoom
                            'text-field': ['get', 'name'], // Get the 'name' property from the GeoJSON
                            'text-offset': [0, 1.25],
                            'text-anchor': 'top'
                        },
                        paint: {
                           'text-color': '#000000',
                           'text-halo-color': '#ffffff',
                           'text-halo-width': 1
                        }
                    });
                }
            } catch (error) {
                console.error('An error occurred while adding the custom layer:', error);
            }
        });
        </script>
    </body>
</html>
Last updated June 12, 2025.