Skip to content

Instantly share code, notes, and snippets.

@makella
Last active February 5, 2024 22:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save makella/950a7baee78157bf1c315a7c2ea191e7 to your computer and use it in GitHub Desktop.
Save makella/950a7baee78157bf1c315a7c2ea191e7 to your computer and use it in GitHub Desktop.
protomaps.js styling filters

A place to collect handy tips and tricks for style-based filters

Simple color by type

In this case, there are multiple types filtered from the same layer. I want to assign each type a unique color.

Try 1: fail

If/else conditions with a fallback. (following this example)

  {
    dataLayer: `admin`,
    symbolizer: new LineSymbolizer({
      color: (p:any) => {
        if (p.admin_level === 0){
          return "hsl(0,100%,50%)"
        } else {
          if (p.admin_level === 1) return "hsl(80,80%,80%)"
          return "hsl(242,100%,50%)"
        }
      },
      width: 2,
      opacity: 0.4,
    }),
    filter: (f) => 
      f.admin_level === 0 ||
      f.admin_level === 1,
  },

Try 2: fail

Maybe try a condition with a fallback. Like this

{
  dataLayer: `admin`,
  symbolizer: new LineSymbolizer({
    color: (p:any) => {
      if (p.admin_level === 0) return "hsl(0,100%,50%)"
      return "hsl(80,80%,80%)"
    },
    width: 2,
    opacity: 0.4,
  }),
  filter: (f) => 
    f.admin_level === 0 ||
    f.admin_level === 1,
},

Try 3: fail

Maybe I have to use a color varaible instead of hsl.

{
   dataLayer: `admin`,
   symbolizer: new LineSymbolizer({
     color: (p:any) => {
       if (p.admin_level === 0) return params.boundaries0
       return params.boundaries1
     },
     width: 2,
     opacity: 0.4,
   }),
   filter: (f) => 
     f.admin_level === 0 ||
     f.admin_level === 1,
 },

Try 4: fail

Should it be:

  • color: (p) =>
  • color: (p:any) => { if (p["admin_level"] == 0 { ...
  • does it matter if it is == or ===?

Tried a few other variations of the same idea. Really not sure what I'm doing wrong!

@makella
Copy link
Author

makella commented Aug 30, 2021

@bdon maybe my brackets? Also tried p.admin_level < 1 I've tried everything.

@bdon
Copy link

bdon commented Aug 31, 2021

@makella brackets look fine; the issue with LineSymbolizer is that I haven't used the LineSymbolizer in that way with dynamic properties yet, but the code ought to work exactly like you tried in parts 1,2, and 3 :) It needs to be implemented, and will work on that tomorrow.

the :any is only important if you're inside a TypeScript project. Are you editing the style in a .ts file? There may be some benefits to the cartographer to see type-checking errors, to catch common mistakes like switching the order of zoom and properties in a function definition, for example. (Are you working on the command line or inside an IDE?)

The difference between == and === is subtle, see https://stackoverflow.com/questions/359494/which-equals-operator-vs-should-be-used-in-javascript-comparisons but I'd say in most protomaps.js contexts where you are comparing it to a constant like "leisure" or the number 5 you can just use ==.

@makella
Copy link
Author

makella commented Aug 31, 2021

@bdon, ok! thanks for clarifying!

  • is this available in PolygonSymbolizer and CircleSymbolizer? classic that i may have started testing with the one that isn't implemented
  • can this be implemented for other properties like opacity, could also see it useful for images mapped to poi types, etc.

the :any is only important if you're inside a TypeScript project. Are you editing the style in a .ts file? There may be some benefits to the cartographer to see type-checking errors, to catch common mistakes like switching the order of zoom and properties in a function definition, for example.

  • yes! i am editing style directly in a .ts file. I am using VS Code for editing and building. I have been checking the Problems tab but is there something here that I am missing?

The difference between == and === is subtle,

  • totally! in my haste of why isn't this working, I was questioning everything LOL... but thank you for the advice on == v === that is something that was in this legacy code i've been refactoring so will go through and adjust all instances.

@makella
Copy link
Author

makella commented Aug 31, 2021

@bdon doesn't seem like this works on PolygonSymbolizer

Case: change color of features by zoom.

{
    dataLayer: `landuse`,
    symbolizer: new PolygonSymbolizer({
      fill:(z:number,p:any) => {
        if (z > 16) return "hsl(100,50%,50%)"
        return "hsl(10,50%,50%)"
    },
    }),
    filter: (f) =>
      f.class == `school` ||
      f.class == `kindergarten` ||
      f.class == `university` ||
      f.class == `college`,
  },

also, i was trying to combine zoom and class:

  {
    dataLayer: `landuse`,
    symbolizer: new PolygonSymbolizer({
      fill:(z:number,p:any) => {
        if (p.class == "university"){
          if (z > 16) return "hsl(100,50%,50%)"
        } else {
          return "hsl(10,50%,50%)"
        }
      }
    }),
    filter: (f) =>
      f.class == `school` ||
      f.class == `kindergarten` ||
      f.class == `university` ||
      f.class == `college`,
  },

any words of wisdom? :) :)

@bdon
Copy link

bdon commented Aug 31, 2021

@makella I'm working on unifying all those styling functions which I am now calling "Attributes" - the issue is that "Evaluated Attributes" where you pass a function instead of a constant is only implemented in a few places. It makes the most sense to refactor all the Symbolizers in one fell sweep to fix this, so I'm tracking the progress here! protomaps/protomaps-leaflet#43

@makella
Copy link
Author

makella commented Aug 31, 2021

YAY!! so good @bdon!!

@bdon
Copy link

bdon commented Aug 31, 2021

@makella can you update to version 1.0.0? https://observablehq.com/d/c2a626a37f791866 has some examples of doing zoom and feature dependent styling for PolygonSymbolizer and LineSymbolizer. From now on, the convention will always be the (zoom,feature) parameters: you'll need to see the tags of Feature via feature.props (shortened from properties to save typing ?? :) )

There's also feature.geomType which you may need for some styling if your vector tiles layer have heterogeneous geometry. In the protomaps vector tiles it's always homogenous, but other vendors mix up multiple kinds in one layer

@bdon
Copy link

bdon commented Aug 31, 2021

@makella filters are also (z,f) now so you can write filter: (z,f) => { return true } (bad example, but you get the point) - so the way of writing zoom and feature dependent stuff is consistent! I expect this will have caused the rendering to get a bit slower but hope to fix that in protomaps/protomaps-leaflet#44 . Also, I realized that Observable notebook may not be publicly accessible... looking at that...

@bdon
Copy link

bdon commented Aug 31, 2021

@makella
Copy link
Author

makella commented Aug 31, 2021

hi @bdon! awesome, way to go!

yes, notebook isn't accessible will look into getting updated to the lates version and do some testing!

@makella
Copy link
Author

makella commented Aug 31, 2021

thanks!!

@makella
Copy link
Author

makella commented Aug 31, 2021

i will look at this more closely in the morning! updated version and most basemap features are gone so i'll have to do a detailed walkthrough with fresh eyes

thanks again brandon look forward to testing it all out

@bdon
Copy link

bdon commented Sep 1, 2021

I started a Changelog describing breaking changes between major versions: https://github.com/protomaps/protomaps.js/blob/master/CHANGELOG.md (this is the first breaking version)

you'll need to slightly modify all of your filters and styling functions, but it should be straightforward.

@makella
Copy link
Author

makella commented Sep 1, 2021

@bdon ok! gonna take a stab at it today.

in the notebook, it looks like the admin line color is not working... it looks as though both 0's and 1's are using the color defined for 1. If you switch to another color (not blue try hsl(113,100%,50%)) you'll see what i mean.

will report back later on how its going. thanks again!

@bdon
Copy link

bdon commented Sep 1, 2021

I updated the notebook; I forgot the typical values for pmap:min_admin_level, which is generally 2 or 4 for countries/states.

@makella
Copy link
Author

makella commented Sep 1, 2021

ahhh! ok,awesome

@bdon
Copy link

bdon commented Sep 3, 2021

@makella any luck with the dynamic attributes and filters? There's a couple more attributes I haven't made dynamic yet, and also planning to do some cleanup around text label symbolizers so they all have the same capabilities (LineLabelSymbolizer is the exception - need to think about curved text, etc)

@makella
Copy link
Author

makella commented Sep 3, 2021

hiii @bdon! yes! i'm currently just going through all my filters and refactoring with the new pattern to get the map rendering again. you wanna meet next week? i also wanted to talk to you about some thoughts around opacity... i'm composing an email but it is still in draft form might be easier to talk through. let me know!

@bdon
Copy link

bdon commented Sep 3, 2021

@makella you can send me your rough thoughts on the opacity... I noticed it can get a little funky when zoom transitions happen with partial opacity polygons because there can be two tiles simultaneously blended and it looks darker.

@gio0v
Copy link

gio0v commented Jul 21, 2023

@makella you can send me your rough thoughts on the opacity... I noticed it can get a little funky when zoom transitions happen with partial opacity polygons because there can be two tiles simultaneously blended and it looks darker.

hi there, thanks for all your hard work! I wonder if you ever managed to solve that issue with opacity? I can still very much notice it in my maps rendered with your protomaps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment