Embed functions in @deck.gl/json module. Setting accessors as objects at declarative language.
{
"description": "Style example",
"initialViewState": {...},
"views": [...],
"layers": [
{
"@@type": "CartoSQLLayer",
"data": "SELECT the_geom_webmercator, gn_pop FROM populated_places",
"getFillColor": {
"@@function": "colorBins",
"prop": "gn_pop",
"breaks": [100, 200, 300],
"colors": [[225, 83, 131], [241, 109, 122], [250, 138, 118], [255, 166, 121]]
}
}
]
}
Introduce functions at the configuration:
const configuration = {
classes: Object.assign({}, require('@deck.gl/layers'), require('@deck.gl/aggregation-layers')),
functions: Object.assign({}, colorBins)
};
During the first pass for deck.gl/json it should transform @@function:
{
"description": "Style example",
"initialViewState": {...},
"views": [...],
"layers": [
{
"@@type": "CartoSQLLayer",
"data": "SELECT the_geom_webmercator, gn_pop FROM populated_places",
"getFillColor": colorBins({
"prop": "gn_pop",
"breaks": [100, 200, 300],
"colors": [[225, 83, 131], [241, 109, 122], [250, 138, 118], [255, 166, 121]]
})
}
]
}
And then apply @@type:
{
"description": "Style example",
"initialViewState": {...},
"views": [...],
"layers": [
{
new CartoSQLLayer({
"data": "SELECT the_geom_webmercator, gn_pop FROM populated_places",
"getFillColor": colorBins({
"prop": "gn_pop",
"breaks": [100, 200, 300],
"colors": [[225, 83, 131], [241, 109, 122], [250, 138, 118], [255, 166, 121]]
})
})
}
]
}
colorsBins could be something like this:
import {scaleThreshold} from 'd3-scale';
function colorsBins({breaks, colors) {
const color = scaleThreshold()
.domain(breaks)
.range(colors);
return d => {
return color(d);
};
}
Currently, using json module, the way to dynamically style data is using @@= prefix on accessors interpreting the rest of the string as a function:
"getFillColor": "@@=properties.pop > 1000 ? [0, 0, 0] : [255, 255, 255]"
The main idea of this RFC is to be able to config data accessors as objects, and pass the thrown data to a function.
I think it could be better to convert this into something more generic: Add @@function to interpret a string as a JavaScript function. It opens a new way to include functions for accesors at declarative language
{
"description": "Style example",
"initialViewState": {...},
"views": [...],
"layers": [
{
"@@type": "CartoSQLLayer",
"data": "SELECT the_geom_webmercator, gn_pop FROM populated_places",
"getFillColor": {
"@@function": "colorBins",
"prop": "gn_pop",
"breaks": [100, 200, 300],
"colors": [[225, 83, 131], [241, 109, 122], [250, 138, 118], [255, 166, 121]]
}
}
]
}
Introduce functions at the configuration:
const configuration = {
classes: Object.assign({}, require('@deck.gl/layers'), require('@deck.gl/aggregation-layers')),
functions: Object.assign({}, colorBins)
};
During the first pass for deck.gl/json it should transform @@function:
{
"description": "Style example",
"initialViewState": {...},
"views": [...],
"layers": [
{
"@@type": "CartoSQLLayer",
"data": "SELECT the_geom_webmercator, gn_pop FROM populated_places",
"getFillColor": colorBins({
"prop": "gn_pop",
"breaks": [100, 200, 300],
"colors": [[225, 83, 131], [241, 109, 122], [250, 138, 118], [255, 166, 121]]
})
}
]
}
And then apply @@type:
{
"description": "Style example",
"initialViewState": {...},
"views": [...],
"layers": [
{
new CartoSQLLayer({
"data": "SELECT the_geom_webmercator, gn_pop FROM populated_places",
"getFillColor": colorBins({
"prop": "gn_pop",
"breaks": [100, 200, 300],
"colors": [[225, 83, 131], [241, 109, 122], [250, 138, 118], [255, 166, 121]]
})
})
}
]
}
colorsBins could be something like this:
import {scaleThreshold} from 'd3-scale';
function colorsBins({breaks, colors) {
const color = scaleThreshold()
.domain(breaks)
.range(colors);
return d => {
return color(d);
};
}
At the current version, the syntax at declarative language to apply a getFillColor with more than 2 colors using @@= is quite verbose:
"getFillColor": "@@= properties.aggregated_total > 1000000 ? [207, 89, 126] : properties.aggregated_total > 100000 ? [232, 133, 113] : properties.aggregated_total > 10000 ? [238, 180, 121] : properties.aggregated_total > 1000 ? [233, 226, 156] : properties.aggregated_total > 100 ? [156, 203, 134] : properties.aggregated_total > 10 ? [57, 177, 133] : [0, 147, 146]",
@AdriSolid can you update your RFC to reflect the changes
@Pessimistress done
As mentioned, we do have support for @@type that handles any constructor
It is a little bit hacky, but you probably get things to work already today if you use the JS trick that a class constructor can return anything (instead of this).
class ColorBin {
constructor({breaks, colors) {
const color = scaleThreshold()
.domain(breaks)
.range(colors);
return d => color(d);
}
}
"getFillColor": {
"@@type": "ColorBins",
"breaks": [100, 200, 300],
"colors": [[225, 83, 131], [241, 109, 122], [250, 138, 118], [255, 166, 121]]
}
}
Also just to make sure I didn't miss something, I don't see the "prop" prop being used in the examples.
"prop": "gn_pop",
@ibgreen thanks for your comments! Returning anything from a class constructor is a good start
The thing with "prop" is that we need to grab some "prop" (i.e: properties.gn_pop) to pass it through the (style) class
As mentioned, we do have support for @@type that handles any constructor
@ibgreen It was the first approach, but as you mention, it's a little bit hacky to create a class to return a function instead of this.