Layer Masks

Layer Masks

Layer masks allow you to control the visibility of different parts of a layer by using another layer as a mask. This is useful for creating complex visual effects, such as revealing or hiding parts of a layer based on the shape or content of another layer. By using administrative layers as masks, you can create more dynamic and context-aware maps that highlight specific regions or features.

When configuring a mask for a MapsGL layer, the layer being used as the mask must have already been added to the map. You can then specify the mask layer in the layer's configuration options.

Configuration

The following LayerMaskSpecification options are available when configuring a mask for a layer:

type LayerMaskSpecification = {
    /**
     * Layer identifiers of the layers to use for masking this layer's output. Use this in combination with the
     * `mode` property to control how the masks are applied to the layer's final output.
     */
    layerIds: Array<string>;
 
    /**
     * Whether to invert the mask, meaning the layer will be visible outside of the mask area.
     */
    invert?: boolean;
 
    /**
     * The mode to use applying the masks, either 'all' (default) or 'any'.
     * - 'all': The layer will be visible only if all the masks are visible (intersection of all masks).
     * - 'any': The layer will be visible if any of the masks are visible (union of all masks).
     */
    mode?: 'all' | 'any';
};

Example usage

The following example demonstrates how to mask a raster tile layer using an administrative boundary layer provided as GeoJSON as the mask:

// add the GeoJSON source for the administrative boundary
controller.addSource('admin-boundaries', {
    type: 'geojson',
    data: {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "properties": {},
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [
                        [
                            [-9.034817674180246, 41.88057058365967],
                            [-8.67194576662672, 42.13468943945496],
                            [-8.263856980817792, 42.28046865495034],
                            [-8.013174607769912, 41.790886135417125],
                            [-7.422512986673795, 41.79207469335983],
                            [-7.251308966490824, 41.91834605566505],
                            [-6.668605515967656, 41.883386949219584],
                            [-6.389087693700915, 41.381815497394655],
                            [-6.851126674822552, 41.11108266861753],
                            [-6.864019944679385, 40.33087189387483],
                            [-7.026413133156595, 40.184524237624245],
                            [-7.066591559263529, 39.71189158788277],
                            [-7.498632371439725, 39.62957103124181],
                            [-7.098036668313128, 39.03007274022378],
                            [-7.374092169616318, 38.37305858006492],
                            [-7.029281175148796, 38.07576406508977],
                            [-7.166507941099865, 37.803894354802225],
                            [-7.537105475281024, 37.42890432387623],
                            [-7.453725551778092, 37.09778758396607],
                            [-7.855613165711985, 36.83826854099627],
                            [-8.382816127953689, 36.97888011326246],
                            [-8.898856980820327, 36.86880931248078],
                            [-8.746101446965554, 37.65134552667661],
                            [-8.839997524439879, 38.26624339451761],
                            [-9.287463751655224, 38.3584858261586],
                            [-9.526570603869715, 38.73742910415491],
                            [-9.446988898140232, 39.39206614842837],
                            [-9.048305223008427, 39.75509308527877],
                            [-8.977353481471681, 40.15930613866581],
                            [-8.768684047877102, 40.76063894303019],
                            [-8.79085323733031, 41.18433401139126],
                            [-8.99078935386757, 41.54345937760364],
                            [-9.034817674180246, 41.88057058365967]
                        ]
                    ]
                }
            }
        ]
    }
});
 
// add the administrative boundary layer to use as a mask
controller.addLayer('admin-mask', {
    type: 'fill',
    source: 'admin-boundaries'
});
 
// add the raster data tile source 
controller.addSource('satellite-geocolor', {
    type: 'raster',
    url: `https://maps{s}.aerisapi.com/${CLIENT_ID}_${CLIENT_SECRET}/satellite-geocolor/{z}/{x}/{y}/0@2x.png`
});
 
// add the raster layer and associate it with the data source
controller.addLayer('satellite', {
    type: 'raster',
    source: 'satellite-geocolor',
    paint: {
        opacity: 0.7
    },
	mask: {
		layerIds: ['admin-mask']
	}
});

Masking weather layers

Weather layers in MapsGL can also utilize layer masks to control their visibility based on administrative boundaries or other geographic features. This is particularly useful for displaying weather data only within specific regions, such as countries, states, or cities. By applying masks to weather layers, you can create more focused and relevant visualizations that highlight weather conditions in targeted areas.

Basic land and water masks

MapsGL provides built-in land and water mask layers that can be used to mask weather layers based on land and water boundaries. This is useful for displaying weather data only over land or water areas.

For example, the following masks temperatures to show only over land areas:

controller.addWeatherLayer('temperatures', {
	mask: { type: 'land' }
});

Conversely, you can mask temperatures to show only over water areas:

controller.addWeatherLayer('temperatures', {
    mask: { type: 'water' }
});

Custom mask layers

For example, you can mask a temperature layer to show temperatures only within the boundaries of a specific country or state, enhancing the clarity and usefulness of the weather data presented on the map:

controller.addWeatherLayer('temperatures', {
    mask: {
        layerIds: ['admin-mask']
    }
});

You can also invert the mask to display weather data outside of the specified region:

controller.addWeatherLayer('temperatures', {
    mask: {
        layers: [{ id: 'admin-mask' }],
		invert: true
    }
});

If you have a layer instance returned by your map controller when calling addLayer, you can also use that layer instance's to specify the mask:

// add the administrative boundary layer to use as a mask
const adminLayer = controller.addLayer('admin-mask', {
    type: 'fill',
    source: 'admin-boundaries'
});
 
controller.addWeatherLayer('temperatures', {
    mask: {
        layers: [{ id: adminLayer.id }],
		invert: true
    }
});

Multiple masks using modes

You can also use multiple layers as masks for a another layer by specifying multiple layer IDs in the layerIds array. By default, the mode is set to 'all', meaning the target layer will be visible only if all the masks are visible (intersection of all masks). You can change the mode to 'any' to make the target layer visible if any of the masks are visible (union of all masks).

For instance, you may want to mask temperatures to just major highways in the US. For this, you'd need a data source and layer for administrative boundaries as polygons, and the MapsGL road-motorway layer for the highways. You can then use both layers as masks for the temperature layer:

// Use a custom vector tile source generated from a custom Natural Earth vector tileset containing 
// country polygons
const countriesSource = controller.addSource('naturalearth', {
	type: 'vector',
	url: '/path/to/naturalearth/{z}/{x}/{y}.pbf',
	maxZoom: 7
});
const countriesLayer = controller.addLayer('countries', {
	type: 'fill',
	source: 'naturalearth',
	sourceLayer: 'countries',
	filter: ['==', 'ISO_A2', 'US'], // Filter to only show the United States
});
 
// Add the MapsGL layer for motorways with a custom thickness
const highwaysLayer = controller.addWeatherLayer('road-motorway', {
	paint: {
		stroke: {
			thickness: 5
		}
	}
});
 
// Add the temperature layer with a mask that only shows the intersecting areas between the 
// countries and motorways layers
controller.addWeatherLayer('temperatures', {
	mask: {
		layers: [
			{ id: countriesLayer.id }, 
			{ id: highwaysLayer.id }
		],
		mode: 'all'
	}
});

Using the any mode will show temperatures anywhere either of the mask layers are visible, so you'd see temperatures over the entire US as well as along the highways globally outside of the US:

// Add the temperature layer with a mask that shows anywhere the countries or motorways layers are visible
controller.addWeatherLayer('temperatures', {
	mask: {
		layers: [
			{ id: countriesLayer.id }, 
			{ id: highwaysLayer.id }    
		],
		mode: 'any'
	}
});