Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image fallback expressions within paint properties #11049

Merged
merged 21 commits into from Oct 18, 2021

Conversation

SnailBones
Copy link
Contributor

@SnailBones SnailBones commented Sep 22, 2021

Closes https://github.com/mapbox/gl-js-team/issues/209

A coalesce expression wrapping image is currently the only way for an expression to check whether an icon sprite is present or absent in the style. In its simplest form, the check looks like this:

['coalesce', ['image', 'rocket-15'], 'missing']

Currently, this functionality is available in the text-field layout property, but not in paint properties. This PR adds it to most paint properties.

Example:

   layout: {
        "text-field": ["to-string",
            ["coalesce",
                ["image", ["concat", ["get", "icon"], "-15"]],
                "missing" // Display "missing" if icon is unavailable
            ]
        ]
    },
    paint: {
        "text-color": [ "case",
            ["==", "missing",
                ["to-string", ["coalesce",
                    ["image", ["concat", ["get", "icon"], "-15"]],
                    "missing"
                ]]
            ],
            'red', // if missing
            "blue" // if available
        ]
    }

Before:
image

After:
image

This is tested to work with the following paint properties:

  • text-color
  • text-opacity
  • icon-color
  • icon-opacity
  • circle-color
  • circle-radius
  • circle-opacity
  • line-color
  • line-width
  • line-opacity

Other paint properties don't work:

  • line-dasharray always returns the fallback.
  • fill-pattern throws an error.

The changes here do NOT add any additional support for layout properties. Most layout properties will continue to not recognize images and always return the fallback, including the following:

  • text-size
  • icon-size
  • text-offset

Many of the failures in the commit history passed on npm run watch but on running tests in CI mode (npm run test). This was caused because one Worker is created to run the entire test suite, and Worker.isSpriteLoaded is a boolean set to true with the first call of spriteLoaded and thereafter initializing all worker sources with isSpriteLoaded incorrectly set to true. 13e6238 fixes that bug by changingi isSpriteLoaded to an object with mapIDs as keys.

Benchmark:

Screenshot Capture - 2021-10-14 - 11-15-46

Launch Checklist

  • fix the brokens
  • write render tests
  • post benchmark scores
  • include before/after visuals or gifs if this PR includes visual changes
  • manually test the debug page
  • tagged @mapbox/gl-native if this PR includes shader changes or needs a native port
  • apply changelog label ('bug', 'feature', 'docs', etc) or use the label 'skip changelog'
  • add an entry inside this element for inclusion in the mapbox-gl-js changelog: <changelog>Added support for conditionally styling most paint properties according to the presence or absence of specific sprites in the style. </changelog>

@mapbox/gl-native @mapbox/map-design-team

@SnailBones SnailBones marked this pull request as ready for review October 5, 2021 00:02
@SnailBones SnailBones changed the title Using image expressions to case condition paint properties Image fallback expressions within paint properties Oct 8, 2021
@arindam1993
Copy link
Contributor

Nice work on this @SnailBones
I think you're missing another spot where the availableImages needs to be passed in, and thats in updatePaintArrays(

updatePaintArrays(featureStates: FeatureStates, vtLayer: VectorTileLayer, layers: $ReadOnlyArray<TypedStyleLayer>, imagePositions: {[_: string]: ImagePosition}) {
).

This paint property update pipeline is used for feature-state which is used to do things like style a feature based on hove and for map.setPaintProperty(). In both of these cases, the update happens on the main-thread so the availableImages needs to come from a different place ( I think the ImageManager)

@SnailBones
Copy link
Contributor Author

I think you're missing another spot where the availableImages needs to be passed in, and thats in updatePaintArrays

Fixed now, thanks @arindam1993!

Copy link
Contributor

@arindam1993 arindam1993 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me now, just one more nit, otherwise its good to go!

@@ -455,7 +457,7 @@ class Tile {
const sourceLayerStates = states[sourceLayerId];
if (!sourceLayer || !sourceLayerStates || Object.keys(sourceLayerStates).length === 0) continue;

bucket.update(sourceLayerStates, sourceLayer, this.imageAtlas && this.imageAtlas.patternPositions || {});
bucket.update(sourceLayerStates, sourceLayer, painter.style._availableImages, this.imageAtlas && this.imageAtlas.patternPositions || {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a bit sketchy passing in a direct reference to this here, if something downstream mutates it it would result in weird sideffects. Could you add a getter to Style that returns a shallow copy of the array instead?

and to prevent excessive copies you could lift out the call to the getter to outside this loop.

@SnailBones SnailBones requested review from arindam1993 and removed request for tristen October 14, 2021 16:38
Copy link
Contributor

@arindam1993 arindam1993 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@SnailBones SnailBones merged commit f95b8fa into main Oct 18, 2021
@SnailBones SnailBones deleted the aidan/image-paint-properties branch October 18, 2021 18:39
katydecorah pushed a commit that referenced this pull request Oct 20, 2021
* main:
  Add touch pan blocker to gesture handling for touch devices (#11116)
  Address accessibility issues (#11064)
  add support for non-mercator projections (#11124)
  Image fallback expressions within paint properties (#11049)
  Replaces EPSG:4326 with OGC:CRS84 in GL JS `LngLat` doc (#11072)
  Add globe view support to heatmap shaders (#11120)
  Exclude flaky test (#11118)
  consistify YOUR_MAPBOX_ACCESS_TOKEN as placeholder string (#11113)
  Allow adding multiple layers to `map.on()` event handler (h/t @omerbn) (#11114)
  render-test-flakiness:clear worker storage (#11111)
  upgrade to supercluster v7.1.4, earcut v2.2.3, vt-pbf v3.1.3, geojson-rewind v0.5.1 (#11110)
  Added v1.13.2 changelog entry (#11108)
  One weird JSON.parse() trick (#11098)
  Fixed doc usage of map.getCenter (#11093)
  s̶y̶m̶b̶o̶l̶-̶c̶l̶i̶p̶ dynamic-filtering with `pitch` and `distance-from-camera` expressions (#10795)
  Update link to transpiling guide (#11096)
  Cherry pick 2.5.1 changelog (#11099)
  Fix an iOS15 issue where Safari tab bar interrupts panning (#11084)
  Fix conditional check for isFullscreen to accommodate Safari (#11086)
  Render tests for #11041 (#11070)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants