Skip to content

Filter within a Layer

Filter a layer based on user input using setFilter().

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Filter within a Layer</title>
    <meta property="og:description" content="Filter a layer based on user input using setFilter()." />
    <meta charset='utf-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel='stylesheet' href='https://unpkg.com/maplibre-gl@4.2.0/dist/maplibre-gl.css' />
    <script src='https://unpkg.com/maplibre-gl@4.2.0/dist/maplibre-gl.js'></script>
    <style>
        body { margin: 0; padding: 0; }
        html, body, #map { height: 100%; }
    </style>
</head>
<body>
<style>
    html, body, #map {
        height: 100%;
    }

    .map-overlay {
        font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
        position: absolute;
        width: 31.8%;
        top: 0;
        left: 0;
        padding: 10px;
    }

    .map-overlay .map-overlay-inner {
        background-color: #fff;
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
        border-radius: 3px;
        padding: 10px;
        margin-bottom: 10px;
    }

    .map-overlay input {
        margin: 2px;
    }

    input[type=number] {
        width: 25%
    }

    #filter-result {
        font-size: 8px;
        font-family: "Courier New";
    }
</style>
<div id="map"></div>

<div class="map-overlay top">
    <div class="map-overlay-inner">
        <nav id="nav-filter">
            <fieldset>
                <legend>🫨 Earthquake <code>felt</code>?</legend>
                <div>
                    <input id="felt" type="checkbox" />
                    <label for="felt">Apply <code>felt</code> Filter</label>
                    <div>
                        <div>
                            <label for="operator-select">Operator:</label>
                            <select name="operator" id="operator-felt">
                                <option value=">">></option>
                                <option value="==" selected>==</option>
                                <option value="<"><</option>
                            </select>
                            <br>
                            <label for="range">Felt:</label>
                            <input type="number" id="range-felt" name="range" value="4" min="1.0" max="10000" />
                        </div>
                    </div>
            </fieldset>

            <fieldset>
                <legend>📈 Magnitude</legend>
                <div>
                    <input id="mag" type="checkbox" />
                    <label for="mag">Apply <code>magnitude</code> Filter</label>
                    <div>
                        <div>
                            <label for="operator-select">Operator:</label>
                            <select name="operator" id="operator-mag">
                                <option value=">">></option>
                                <option value="==" selected>==</option>
                                <option value="<"><</option>
                            </select>
                            <br>
                            <label for="range">Magnitude:</label>
                            <input type="number" id="range-mag" name="range" value="2.71" min="0.0" max="100" />
                        </div>
                    </div>
            </fieldset>

            <fieldset>
                <legend>🌊 Tsunami (0 or 1)</legend>
                <input id="tsunami" type="checkbox" /> <label>Apply <code>tsunami</code> filter</label>
                <div id="radio-tsunamis">
                    <input type="radio" id="t0" name="tsunami" value="0" /><label>0</label>
                    <input type="radio" id="t1" name="tsunami" value="1" /><label>1</label>
                </div>
            </fieldset>
        </nav>
        <hr />
        <div id='filter-result'>["all"]</div>
    </div>
</div>

<script>
    const data = {};

    const map = new maplibregl.Map({
        container: 'map',
        style: 'https://demotiles.maplibre.org/style.json',
        center: [-117, 32],
        zoom: 0,
    });

    map.on('load', () => {
        // add a clustered GeoJSON source for a sample set of earthquakes
        map.addSource('earthquakes', {
            'type': 'geojson',
            'data':
                'https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson'
        });
        // Basic circle and symbol layers earthquakes
        map.addLayer({
            'id': 'earthquakes',
            'type': 'circle',
            'source': 'earthquakes',
            'paint': {
                'circle-color': '#ff0000'
            }
        });
    });

    document.getElementById('nav-filter').addEventListener('change', (e) => {
        let filterOnValue = ['all'];
        let operator = '==';

        switch (e.target.id) {
            /// example: `map.setFilter("earthquakes", ["any", [">", "felt", 16.0]])`
            case 'felt':
                operatorFelt = document.getElementById('operator-felt');
                felt = document.getElementById('range-felt');
                operator = operatorFelt.value;

                // eslint-disable-next-line no-unused-expressions
                e.target.checked ? data.felt = Number(felt.value) : delete data['felt'];

                break;

            /// example: `map.setFilter("earthquakes", ["any", [">", "mag", 5.0]])`
            case 'mag':
                operatorMag = document.getElementById('operator-mag');
                mag = document.getElementById('range-mag');
                operator = operatorMag.value;

                // eslint-disable-next-line no-unused-expressions
                e.target.checked ? data.mag = Number(mag.value) : delete data['mag'];

                break;

            /// example: `map.setFilter("earthquakes", ["any", [">", "tsunami", 0]])`
            case 'tsunami':
                // eslint-disable-next-line @typescript-eslint/quotes
                tsunami = document.querySelector("input[type='radio'][name=tsunami]:checked");
                operator = '==';

                // eslint-disable-next-line no-unused-expressions
                e.target.checked ? data.tsunami = Number(tsunami.value) : delete data['tsunami'];

                break;
            default:
                console.log('default');
        }

        filterOnValue = Object.keys(data);

        mapLibreFilterSpread = ['all', ...filterOnValue.map(id => [operator, id, data[id]])];
        mapLibreFilter = mapLibreFilterSpread;

        document.getElementById('filter-result').textContent = JSON.stringify(mapLibreFilter);

        map.setFilter('earthquakes', mapLibreFilter);
    });

</script>
</body>
</html>