YAML is an object-based data format with a flexible structure. Its specification includes a huge array of crazy abilities, but we only use a few of them.
Here are the most important YAML features to know about when writing Tangram scene files.
YAML's structure is made up of "mappings," known elsewhere as "key/value pairs" – we usually call these parameters.
parameter: value
When nested, YAML calls this a "collection" – we usually call it an element:
element:
parameter1: value
parameter2: value
At a given level, key names must be unique.
element:
parameter1: value
parameter1: value # not allowed, parameter name is repeated
A parameter can't also be an element:
# THIS WON'T WORK
parameter: value
subparameter: value
In this documentation, we refer to both parameters and elements as "objects".
YAML supports two kinds of syntax when writing nested objects: whitespace syntax and bracket syntax.
####whitespace syntax
Whitespace syntax requires each level of an object to be indented with spaces – any number of spaces or tabs is allowed, as long as it's consistent throughout the file. It is relatively easy to read, though it tends to result in longer files.
element:
subelement:
parameter1: value1
parameter2: value2
subelement2:
parameter1: value1
parameter2: value2
Note: if you mix spaces and tabs, the parser will throw an error like this one:
YAMLException {name: "YAMLException", reason: "bad indentation of a mapping entry"}
Here is the same object as the first example above, written in the more compact bracket syntax:
element1: { subelement1: { parameter1: value1, parameter2: value2 }, subelement2: { parameter1: value1, parameter2: value2 } }
In some cases, this syntax may be harder to read, but it's usually fine for shorter objects, or patterns which are repeated frequently:
roads:
data: { source: local, filter: roads }
For further examples, check out our many fine demos!
Lists are written differently in each of the above syntax styles.
element:
parameter:
- item 1
- item 2
- item 3
####bracket lists
element: { parameter: [ item1, item2, item3 ] }
Whitespace syntax can enclose bracket syntax, but not the other way around – once you start an object in bracket syntax, you have to finish it before you can move back into whitespace syntax.
element:
parameter: { [ item1, item2, item3 ] }
element:
parameter:
- item1
- item2: { parameter1: value1, parameter2: value2 }
- item3
# THIS WILL NOT WORK
element: { parameter:
- item1
- item2
- item3
Tangram's data types are based on YAML's functionality, but we've extended them a bit in certain contexts.
YAML interprets data types automatically:
5 # that's an int
5.2 # that's a float
duck # obviously a string
5.2 ducks # mixed-type => string
[1.0, .5, .75] # array of floats (aka a parade)
Our "stops" data structure is a way to define a relationship between two ranges of values. It is defined as an array of two-item arrays, like so:
[[12, 3], [14, 6], [16, 9]]
The first value in each pair is always a zoom level. The second value in each pair is interpreted contextually, with all of the constraints of the particular parameter. At other zoom levels, values will be interpolated linearly.
For instance, in a width
block, if no units are specified, each pair is interpreted as [zoom, meters]
. The above example will define a value of 3m at zoom 12, 6m at zoom 14, and 9m at zoom 16. At zoom 13, the value will be 4.5m.
Stops may be used in color
, width
, focal_length
, and fov
.
color: [[10, [0.3, 0.4, 0.3]], [14, [0.5, 0.825, 0.5]]]
width: [[13, 0px], [14, 3px], [16, 5px], [18, 10px]]
Note that stops define settings to be used when tile geometry is built. Typically, this only happens when the tile is loaded, at a tile integer change (or sometimes halfway between integer zooms) – so incremental zooming won't cause style changes until the next tile is loaded.
Our YAML parser detects certain keywords contextually based on the element or parameter in which they are used.
Additionally, all of the named scene file elements are officially reserved keywords and can't be used as element names.
Strings starting with function
will be passed to the style builder as JavaScript in certain contexts: color
, width
, order
, interactive
, visible
, filter
, size
, fill
, and stroke
.
# Single-line Javascript example:
width: function () { return 2.5 * Math.log(zoom); }
There are two keywords with a $
prefix, available for use in [[filters]].
The $zoom
keyword may be used to define filters with optional min
and max
parameters.
outline:
filter: { $zoom: { min: 15, max: 20 } }
The $geometry
keyword can specify a filter to match a specific geometry type, for cases with a FeatureCollection includes multiple geometry types:
labels:
filter: { $geometry: point }
One of the reasons we chose YAML is its ability to handle multi-line strings with a minimum of fuss. In whitespace syntax only, start an parameter's value with a "pipe" character (|
) followed by a newline, and everything that isn't indented less after that will be treated as a single string value, newlines included:
element:
parameter: |
This is my multi-line string.
There are many like it,
But this one is mine.
It even has an empty line!
Sublime.
Indents don't matter
As long as they don't
start at the parent's level
or less.
This lets us put code straight into an attribute value, and it still looks like code:
# Multi-line Javascript example:
style:
color: [0.5, 0.5, 0.5]
width: |
function () {
return 2.5 * Math.log(zoom);
}
# GLSL Example:
elevator:
extends: polygons
animated: true
shaders:
blocks:
position: |
// Elevator buildings
if (position.z > 0.01) {
position.z *= (sin(position.z + u_time) + 1.0);
}
# This is a YAML comment.
# There is no way to write block comments in YAML, sorry.
# To quote http://stackoverflow.com/questions/2276572/how-do-you-do-block-comment-in-yaml:
# If you want to write
# a block-commented Haiku
# you'll need three pound signs
Bear in mind that in multi-line strings, pound signs lose their ability to comment code! You'll have to use the comment convention in whatever language you're writing. (This can be a pain when using auto-commenting in text editors.)
url
attributes may be used to link to external YAML files in the styles
element, as well as in the shader block elements globals
, color
, and position
. This allows for more modular shader construction as well as easy sharing of styles.
dots:
url: styles/dots.yaml
When the url
parameter is used in the styles
block, the linked .yaml
file should include a replica of the entire style element as it would appear in the scene file, including the style name:
# styles/dots.yaml
dots:
extends: polygons
shaders:
uniforms:
...
shaders:
blocks:
globals:
url: functions/hsv2rgb.glsl
When the url
attribute is used in place of any blocks
parameters, the linked .glsl
file should only include GLSL code, as it would appear in the parameter's value:
// hsv2rgb.glsl
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
YAML's capabilities are officially a superset of JSON, which makes conversion between the two formats a cinch.