mapbox-gl-js icon indicating copy to clipboard operation
mapbox-gl-js copied to clipboard

A new fill paint property to mask areas outside region of interest or polygon

Open planemad opened this issue 6 years ago • 17 comments

This is a feature request. The implementation details are unknown.

Motivation

As a map designer, a common cartographic technique for me to focus the user attention to an area of interest is to mask/hide details of the areas outside the region of interest.

To accomplish such an effect with the existing GL style-spec, one needs to create a polygon covering the whole world with a hole of the desired region. This is a cumbersome and tedious data creation process for a map designer.

Design Alternatives

  • A new fill-region: 'inside'<default> | 'outside' property for the existing fill layer. The 'outside' value would apply all the paint properties to all areas outside of the polygons in the layer if set https://github.com/mapbox/mapbox-gl-js/issues/6267#issuecomment-369928098
  • ~~A new inverted-fill layer type which will apply a fill style to all areas outside of a polygon~~
  • ~~New inverted-fill-x properties for all existing fill properties~~

Design

Adding a new fill-region paint property is simple and communicates clearly to the user where the paint is applied in relation with the polygon.

~~Feel that maybe a new inverted-fill layer type might be the simplest with respect to keeping the style-spec clean~~

Mock-Up

What will this design look like to developers?

map.addLayer({
	'id': 'area of interest',
	'source': 'aoi',
        'type': 'fill',
	'layout': {},
	'paint': {
                'fill-region': 'outside',
                ...any supported fill properties
	}
});

What will this design look like to end users? Image source: inverted fill in qgis

Concepts

fill-region is the new concept that would be introduced. This feature is called "inverted polygon" in qgis.

planemad avatar Mar 02 '18 11:03 planemad

Another alternative would be to add a fill-region: 'inside' | 'outside' property to the existing fill layer.

anandthakker avatar Mar 02 '18 14:03 anandthakker

This feature would make it easier for the map designer, avoiding the need to use turf.mask. See prior discussion for this feature at #993 and #6257.

andrewharvey avatar Mar 02 '18 23:03 andrewharvey

While masks work great for maps without symbol layers, one challenge are labels near the borders of the mask. This proposal is likely out-of-scope for this particular bug, but it's important to consider when creating masks.

ryanbaumann avatar Mar 03 '18 16:03 ryanbaumann

We need this feature in our application and I am willing to work on it to make it happen.

What we need is to invert the 'water' layer - we need a 'land' layer to place above a custom layer where we render wave height and ocean currents. And because it is such a large dataset for the water layer, we would really like to not have to download, invert offline and upload it again.

(Or, we would need a way to mask our custom layer with the water layer, but I think an inverted fill is more usable.)

I have implemented a proof-of-concept (/hack) of @mourner 's suggestion from https://github.com/mapbox/mapbox-gl-js/issues/993#issuecomment-74130793 to invert geojson on-the-fly inside mapbox, but did not manage to do the same with vector tiles.. Would that be feasible?

I would like some guidance so that I don't develop something that cannot be merged.

markusjohnsson avatar Nov 22 '18 10:11 markusjohnsson

I've worked on this a bit more, taking another approach than the one mentioned above. Instead I'm using the stencil buffer to mask the polygon and then fill the tile.

image

Still lots of work to be done, but at least this looks like it will work.

markusjohnsson avatar Nov 28 '18 12:11 markusjohnsson

Is this feature planned?

waissbluth avatar Feb 22 '19 23:02 waissbluth

@waissbluth I've received no indication that this would be accepted if I opened a pull request. I still need this for a customer project, but it is on low priority right now.

markusjohnsson avatar Mar 13 '19 13:03 markusjohnsson

@markusjohnsson Do you have a public fork you'd be willing to share? Looks like a wondrous start—is the "inverse" fill styled the same way I would style a fill-type layer?

unitof avatar Mar 13 '19 18:03 unitof

This is orthogonal to the request for masking, but in case anyone is coming here looking for a way to effectively dim the background layer outside an area of interest without blotting it out entirely, I shared a simple approach towards the end of this workshop at State of the Map U.S. 2018.

detroit

1ec5 avatar Apr 26 '19 18:04 1ec5

Is there any feature like this (mask areas outside polygon) in Mapbox iOS SDK ? Appreciate for the answer!

pavelko3lov avatar Mar 27 '20 20:03 pavelko3lov

The new within expression https://blog.mapbox.com/introducing-gl-js-v1-9-0-and-the-within-expression-6268f6c32be3 may help with some of the use cases here. It will let you apply a different style to points and lines outside a polygon.

andrewharvey avatar Mar 27 '20 23:03 andrewharvey

The new within expression https://blog.mapbox.com/introducing-gl-js-v1-9-0-and-the-within-expression-6268f6c32be3 may help with some of the use cases here. It will let you apply a different style to points and lines outside a polygon.

Sorry, but I mean in Mapbox iOS SDK, not in mapbox-gl-sj.

pavelko3lov avatar Mar 28 '20 18:03 pavelko3lov

Sorry, but I mean in Mapbox iOS SDK, not in mapbox-gl-sj.

I was commenting in general about this thread, not in response to your question.

andrewharvey avatar Mar 28 '20 21:03 andrewharvey

@fvonk, as of mapbox/mapbox-gl-native-ios#184 and v5.8.0, you can use a SELF IN %@ expression, which is equivalent to the within expression seen here. However, it only works on whole features in shape or vector tile sources; it won’t cut up a single feature that happens to be partially inside your region of interest.

1ec5 avatar Apr 30 '20 17:04 1ec5

You can use turf's mask function for that https://www.npmjs.com/package/@turf/mask Simply create a "cover polygon" with the extend of your (Map)View (or the whole world) and use your Polygon. Turf even creates the "cover polygon" for you, if you do not provide one. https://github.com/Turfjs/turf/blob/master/packages/turf-mask/index.js#L58

Stophface avatar Dec 28 '21 17:12 Stophface

Has there been any update to the status of this feature request?

We are currently using the solution that leverages turf-mask to invert the geometries. But in our case, it is too cumbersome to render on the client, so we are processing them as tilesets.

In order to avoid the high cost of processing 10m resolution tiles that span the entire planet, we are forced to use a maxzoom value of 5. This results in a 300m resolution, which is not ideal.

This feature seems like the only solution to our problem.

s11richard avatar Sep 08 '22 11:09 s11richard

It's a shame that within can only be applied to point and line geometries, not everything else.

boredland avatar Jan 10 '23 19:01 boredland

@boredland yeah, and that lines won't get clipped to the bounds either, they're either all in or out :(

chriszrc avatar Sep 07 '23 14:09 chriszrc