Combine case and zoom expressions in Mapbox

Viewed 711

I have a layer that renders Polygon features in a Geojson source as filled area. Here's an example of one of the features:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "id": 12345,
        "name": 'exampleName',
        "hasCities": true | false,
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [[...], [...]]
      }
    }, {...}
  ]
  "properties": {
    "parent_name": "parent1",
    "parent_id": 23,
  }
}

I would like to reach this fill opacity logic:

const isRed = true | false
const redOpacity = 0.5
const notRedOpacity = 0.8

if (hasCities) {
  opacity = 0
} else if (isRed) {
  if(zoom <= 14) // use linear interpolation
    opacity = redOpacity
  else if(zoom >= 19)
    opacity = 0.03
  else 
    interpolate
} else {
  if(zoom <= 14) // use linear interpolation
    opacity = notRedOpacity
  else if(zoom >= 19)
    opacity = 0.03
  else 
    interpolate
}

This is my starting point:

const opacity = [
  'case',
  ['boolean', ['get', 'hasCities']],
  0,
  isRed ? redOpacity : notRedOpacity,
]

Then I don't know how to implement the zoom part. I think I need something like this:

const zoomOpacity = ['interpolate', ['linear'], ['zoom'], 14, notRedOpacity | redOpacity, 19, 0.03]

but I'm not sure and how can I combine these two expressions? I know that "zoom" expression may only be used as input to a top-level "step" or "interpolate" expression." so I think I need to use step operator.

Can someone help me please?

2 Answers

You need to start by flipping your logic so the zoom is the first decision:

if(zoom <= 14) // use linear interpolation
    if (hasCities)
        opacity = 0
    else if (isRed) {
        opacity = redOpacity
    else
        opacity = notRedOpacity
if (zoom >= 19)
    if (hasCities)
        opacity = 0
    else 
        opacity = 0.03

Then express it as a function that computes opacity, from zoom and the other options:

opacity = interpolate (zoom)
    14:     
        if (hasCities)
            0
        else if (isRed) {
            redOpacity
        else
            notRedOpacity
    19:
        if (hasCities)
            0
        else 
            0.03

Finally, in actual Mapbox GL language:

"fill-opacity": ["interpolate", ["zoom"],
    14,     
        ["case", ["get", "hasCities"],
            0
            ["case", ["get", "isRed"],
                redOpacity,
                notRedOpacity
            ]
        ]
    19,
        ["case", ["get", "hasCities"],
            0,
            0.03
        ]
]

I solved in this way:

const baseValue = [
  'case',
  ['boolean', ['get', 'hasDistricts']],
  0,
  isRed ? redOpacity : notRedOpacity,
]
const fillOpacity = [
  'interpolate',
  ['linear'],
  ['zoom'],
  14,
  baseValue,
  19,
  ['case', ['boolean', ['get', 'hasCities']], 0, 0.03],
]
Related