Skip to content

Instantly share code, notes, and snippets.

@springmeyer
Created October 30, 2013 02:52
Show Gist options
  • Save springmeyer/7226517 to your computer and use it in GitHub Desktop.
Save springmeyer/7226517 to your computer and use it in GitHub Desktop.
Possible CartoCSS advances for more dynamic styling with zoom

Zoom as a dynamic token in selectors

Common usage of the special zoom filter keyword looks like:

#roads[zoom=10] {
   line-width:2;
}

Which tells the renderer to, only at zoom level 10, query the roads layer and draw lines with the width of 2.

But this becomes verbose and repetative when you want to vary the line width for every (or at least many different zoom levels):

#roads {
   [zoom=1] {
     line-width:2;
   }
   [zoom=2] {
     line-width:3;
   }
   [zoom=3] {
     line-width:4;
   }
}

So, what if we could accomplish this same exact styling as above, for example, where the line-width is one larger than the zoom without having to be explicit in code for every single zoom level?

We could allow line-width to be an expression so that you could do:

#roads {
     line-width:@zoom+1;
}

The @ sign here in front of the zoom keyword here indicates something important: That zoom in this context is not a filter that controls whether a layer is actually queried for a given zoom level but rather is a token that would be dynamically evalated for every single feature that is processed for that layer.

So this works, but only in the case where we want to show lines for all zoom levels. Maybe it only makes sense to show lines for zoom levels between 2-10. In that case you would need to use zoom in both ways:

#roads[zoom>2][zoom<10] {
     line-width:@zoom+1;
}

Finally, some clever cartographers edit their data such that a certain field represents an integer for whether a certain feature should be shown at a certain zoom level, like the natural earth SCALERANK: http://www.naturalearthdata.com/forums/topic/definition-of-scale-rank-appropriate-attribute-for-size-based-styling/

For the purposes of this example let's asssume that we are styling data now that has a SCALERANK field and SCALERANK maps 1:1 to zoom level, so a feature with a value of 10 should only be shown at zoom level 10 and below.

Combining the above example with scalerank could look like:

#roads {
     [zoom>2][zoom<10][[SCALERANK]<10] {
       line-width:@zoom+1;
     }
     [zoom>10][zoom<20][[SCALERANK]<20] {
       line-width:@zoom+2;
     }
}

But maybe we could do even better and potentially save even more typing. What if the zoom filter selector were comparable to a data field such that we could accomplish the above styling in just a few lines:

#roads[zoom>2][zoom<20] {
     [zoom<=[SCALERANK]] {
       line-width:@zoom+1;
     }
}
@tmcw
Copy link

tmcw commented Oct 30, 2013

Cool, so my initial thought is that it would also seem natural for @zoom to be a field or a special field, like [zoom] or {zoom}, or to reserve a special syntax for builtin variables like $zoom. It would be nice consistency-wise to avoid the case in which a user writes

@zoom: 4;

But may not be absolutely necessary.

@springmeyer
Copy link
Author

agree. maybe we can get away with zoom everywhere?

@pergustas
Copy link

Just want to express my support adding the the possibility to use @zoom (or zoom). After having completed a few stylesheets this is my #1 most wanted feature. It would really make many stylesheets more easy to maintain.

@stevage
Copy link

stevage commented May 9, 2014

+1 here too. Each of the syntaxes (zoom, @zoom, $zoom, [zoom]) seems to have pros and cons.

The raw zoom syntax does introduce this inconsistency:

[zoom > 10] { line-width: zoom; }
[myval > 10] { line-width: [myval]; }

From memory, Merkaartor had some pretty sophisticated syntax for zoom styling, but the website seems to be down. IIRC, you could do stuff like this:

line-width[12-18]: 3,3.5,4,4.5,5,6,8;

In other words, succinctly map a whole set of values onto a zoom range. (But that's just my vague recollection, having used it a couple of years ago.) I've been doing similar stuff atm, and it comes out very verbose:

  line-width: @roadsize;
  [zoom = 10] { line-width: @roadsize * @roadzoom10; }
  [zoom = 11] { line-width: @roadsize * @roadzoom11; }
  [zoom = 12] { line-width: @roadsize * @roadzoom12; }
  [zoom = 13] { line-width: @roadsize * @roadzoom13; }
  [zoom = 14] { line-width: @roadsize * @roadzoom14; }
  [zoom >= 15] { line-width: @roadsize * @roadzoom15; }
...

@springmeyer
Copy link
Author

For now I've settled on an easy solution that we can revisit in the future. Basically Carto.js will skip evaluating strings, while Mapnik 3.x now understands variables with the @ so we can - without any modifications to carto - take advantage of this by doing:

#layer {
   line-width:'@zoom';
}

Since gists do not support comment notifications, let's discuss over at mapbox/carto#269

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