Skip to content

Commit fb25245

Browse files
NathanMOlsonHarelM
andauthored
Color Relief (#1089)
* spec changes for color-relief layer * change color relief default to black at sea level, white at Mt. Everest * fix some integration tests * add tests for color-relief-color * add Native issue links * update changelog * add missing test * Clarify docs, remove color-relief-color default * fix "syntax" syntax * update documentation --------- Co-authored-by: Harel M <[email protected]>
1 parent 7c590fe commit fb25245

File tree

13 files changed

+232
-9
lines changed

13 files changed

+232
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## main
22

33
### ✨ Features and improvements
4+
- Add new layer type: `color-relief ([#1067](https://github.com/maplibre/maplibre-style-spec/issues/1067))
45
- _...Add new stuff here..._
56

67
### 🐞 Bug fixes

build/generate-style-spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ export type ExpressionSpecification =
265265
| ['zoom'] // number
266266
// Heatmap
267267
| ['heatmap-density'] // number
268+
// Elevation
269+
| ['elevation'] // number
268270
// Global state
269271
| ['global-state', string] // unknown
270272

src/expression/compound_expression.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,11 @@ CompoundExpression.register(expressions, {
289289
[],
290290
(ctx) => ctx.globals.heatmapDensity || 0
291291
],
292+
'elevation': [
293+
NumberType,
294+
[],
295+
(ctx) => ctx.globals.elevation || 0
296+
],
292297
'line-progress': [
293298
NumberType,
294299
[],
@@ -695,7 +700,7 @@ function isExpressionConstant(expression: Expression) {
695700

696701
return isFeatureConstant(expression) &&
697702
isGlobalPropertyConstant(expression,
698-
['zoom', 'heatmap-density', 'line-progress', 'accumulated', 'is-supported-script']);
703+
['zoom', 'heatmap-density', 'elevation', 'line-progress', 'accumulated', 'is-supported-script']);
699704
}
700705

701706
function isFeatureConstant(e: Expression) {

src/expression/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export type FeatureState = {[_: string]: any};
5959
export type GlobalProperties = Readonly<{
6060
zoom: number;
6161
heatmapDensity?: number;
62+
elevation?: number;
6263
lineProgress?: number;
6364
isSupportedScript?: (_: string) => boolean;
6465
accumulated?: Value;

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import {classifyRings} from './util/classify_rings';
4444
import {ProjectionDefinition} from './expression/types/projection_definition';
4545

4646
type ExpressionType = 'data-driven' | 'cross-faded' | 'cross-faded-data-driven' | 'color-ramp' | 'data-constant' | 'constant';
47-
type ExpressionParameters = Array<'zoom' | 'feature' | 'feature-state' | 'heatmap-density' | 'line-progress'>;
47+
type ExpressionParameters = Array<'zoom' | 'feature' | 'feature-state' | 'heatmap-density' | 'elevation' | 'line-progress'>;
4848

4949
type ExpressionSpecificationDefinition = {
5050
interpolated: boolean;

src/reference/v8.json

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@
230230
"required": true,
231231
"type": "array",
232232
"value": "layer",
233-
"doc": "A style's `layers` property lists all the layers available in that style. The type of layer is specified by the `type` property, and must be one of `background`, `fill`, `line`, `symbol`, `raster`, `circle`, `fill-extrusion`, `heatmap`, `hillshade`.\n\nExcept for layers of the `background` type, each layer needs to refer to a source. Layers take the data that they get from a source, optionally filter features, and then define how those features are styled.",
233+
"doc": "A style's `layers` property lists all the layers available in that style. The type of layer is specified by the `type` property, and must be one of `background`, `fill`, `line`, `symbol`, `raster`, `circle`, `fill-extrusion`, `heatmap`, `hillshade`, `color-relief`.\n\nExcept for layers of the `background` type, each layer needs to refer to a source. Layers take the data that they get from a source, optionally filter features, and then define how those features are styled.",
234234
"example": [
235235
{
236236
"id": "coastline",
@@ -797,6 +797,16 @@
797797
}
798798
}
799799
},
800+
"color-relief": {
801+
"doc": "Client-side elevation coloring based on DEM data. The implementation supports Mapbox Terrain RGB, Mapzen Terrarium tiles and custom encodings.",
802+
"sdk-support": {
803+
"basic functionality": {
804+
"js": "https://github.com/maplibre/maplibre-gl-js/issues/5666",
805+
"android": "https://github.com/maplibre/maplibre-native/issues/3408",
806+
"ios": "https://github.com/maplibre/maplibre-native/issues/3408"
807+
}
808+
}
809+
},
800810
"background": {
801811
"doc": "The background color or pattern of the map.",
802812
"sdk-support": {
@@ -860,6 +870,7 @@
860870
"layout_symbol",
861871
"layout_raster",
862872
"layout_hillshade",
873+
"layout_color-relief",
863874
"layout_background"
864875
],
865876
"layout_background": {
@@ -2687,6 +2698,26 @@
26872698
"property-type": "constant"
26882699
}
26892700
},
2701+
"layout_color-relief": {
2702+
"visibility": {
2703+
"type": "enum",
2704+
"values": {
2705+
"visible": {
2706+
"doc": "The layer is shown."
2707+
},
2708+
"none": {
2709+
"doc": "The layer is not shown."
2710+
}
2711+
},
2712+
"default": "visible",
2713+
"doc": "Whether this layer is displayed.",
2714+
"sdk-support": {
2715+
"basic functionality": {
2716+
}
2717+
},
2718+
"property-type": "constant"
2719+
}
2720+
},
26902721
"filter": {
26912722
"type": "array",
26922723
"value": "*",
@@ -4187,6 +4218,26 @@
41874218
}
41884219
}
41894220
},
4221+
"elevation": {
4222+
"doc": "Gets the elevation of a pixel (in meters above the vertical datum reference of the `raster-dem` tiles) from a `raster-dem` source. Can only be used in the `color-relief-color` property of a `color-relief` layer.",
4223+
"syntax": {
4224+
"overloads": [
4225+
{
4226+
"parameters": [],
4227+
"output-type": "number"
4228+
}
4229+
]
4230+
},
4231+
"example": ["elevation"],
4232+
"group": "Color Relief",
4233+
"sdk-support": {
4234+
"basic functionality": {
4235+
"js": "https://github.com/maplibre/maplibre-gl-js/issues/5666",
4236+
"android": "https://github.com/maplibre/maplibre-native/issues/3408",
4237+
"ios": "https://github.com/maplibre/maplibre-native/issues/3408"
4238+
}
4239+
}
4240+
},
41904241
"line-progress": {
41914242
"doc": "Gets the progress along a gradient line. Can only be used in the `line-gradient` property.",
41924243
"syntax": {
@@ -5610,6 +5661,7 @@
56105661
"paint_symbol",
56115662
"paint_raster",
56125663
"paint_hillshade",
5664+
"paint_color-relief",
56135665
"paint_background"
56145666
],
56155667
"paint_fill": {
@@ -7633,6 +7685,57 @@
76337685
"property-type": "data-constant"
76347686
}
76357687
},
7688+
"paint_color-relief": {
7689+
"color-relief-opacity": {
7690+
"type": "number",
7691+
"default": 1,
7692+
"minimum": 0,
7693+
"maximum": 1,
7694+
"doc": "The opacity at which the color-relief will be drawn.",
7695+
"transition": true,
7696+
"sdk-support": {
7697+
"basic functionality": {
7698+
"js": "https://github.com/maplibre/maplibre-gl-js/issues/5666",
7699+
"android": "https://github.com/maplibre/maplibre-native/issues/3408",
7700+
"ios": "https://github.com/maplibre/maplibre-native/issues/3408"
7701+
}
7702+
},
7703+
"expression": {
7704+
"interpolated": true,
7705+
"parameters": [
7706+
"zoom"
7707+
]
7708+
},
7709+
"property-type": "data-constant"
7710+
},
7711+
"color-relief-color": {
7712+
"type": "color",
7713+
"doc": "Defines the color of each pixel based on its elevation. Should be an expression that uses `[\"elevation\"]` as input.",
7714+
"example": [
7715+
"interpolate",
7716+
["linear"],
7717+
["elevation"],
7718+
0, "black",
7719+
8849, "white"
7720+
],
7721+
"transition": false,
7722+
"sdk-support": {
7723+
"basic functionality": {
7724+
"js": "https://github.com/maplibre/maplibre-gl-js/issues/5666",
7725+
"android": "https://github.com/maplibre/maplibre-native/issues/3408",
7726+
"ios": "https://github.com/maplibre/maplibre-native/issues/3408"
7727+
},
7728+
"data-driven styling": {}
7729+
},
7730+
"expression": {
7731+
"interpolated": true,
7732+
"parameters": [
7733+
"elevation"
7734+
]
7735+
},
7736+
"property-type": "color-ramp"
7737+
}
7738+
},
76367739
"paint_background": {
76377740
"background-color": {
76387741
"type": "color",

src/validate/validate_layer.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,14 @@ export function validateLayer(options) {
6868
errors.push(new ValidationError(key, layer.source, `layer "${layer.id}" requires a raster source`));
6969
} else if (sourceType !== 'raster-dem' && type === 'hillshade') {
7070
errors.push(new ValidationError(key, layer.source, `layer "${layer.id}" requires a raster-dem source`));
71+
} else if (sourceType !== 'raster-dem' && type === 'color-relief') {
72+
errors.push(new ValidationError(key, layer.source, `layer "${layer.id}" requires a raster-dem source`));
7173
} else if (sourceType === 'raster' && type !== 'raster') {
7274
errors.push(new ValidationError(key, layer.source, `layer "${layer.id}" requires a vector source`));
7375
} else if (sourceType === 'vector' && !layer['source-layer']) {
7476
errors.push(new ValidationError(key, layer, `layer "${layer.id}" must specify a "source-layer"`));
75-
} else if (sourceType === 'raster-dem' && type !== 'hillshade') {
76-
errors.push(new ValidationError(key, layer.source, 'raster-dem source can only be used with layer type \'hillshade\'.'));
77+
} else if (sourceType === 'raster-dem' && (type !== 'hillshade' && type !== 'color-relief')) {
78+
errors.push(new ValidationError(key, layer.source, 'raster-dem source can only be used with layer type \'hillshade\' or \'color-relief\'.'));
7779
} else if (type === 'line' && layer.paint && layer.paint['line-gradient'] &&
7880
(sourceType !== 'geojson' || !source.lineMetrics)) {
7981
errors.push(new ValidationError(key, layer, `layer "${layer.id}" specifies a line-gradient, which requires a GeoJSON source with \`lineMetrics\` enabled.`));
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"propertySpec": {
3+
"type": "color",
4+
"default": {
5+
"stops": [
6+
[
7+
0,
8+
"black"
9+
],
10+
[
11+
8849,
12+
"white"
13+
]
14+
]
15+
},
16+
"property-type": "data-driven",
17+
"expression": {
18+
"parameters": [
19+
"zoom",
20+
"feature"
21+
]
22+
}
23+
},
24+
"expression": [
25+
"interpolate",
26+
[
27+
"linear"
28+
],
29+
[
30+
"elevation"
31+
],
32+
0,
33+
"#000000",
34+
1,
35+
"#ff0000"
36+
],
37+
"inputs": [
38+
[
39+
{
40+
"elevation": 0.5
41+
},
42+
{}
43+
]
44+
],
45+
"expected": {
46+
"compiled": {
47+
"result": "success",
48+
"isFeatureConstant": true,
49+
"isZoomConstant": true,
50+
"type": "color"
51+
},
52+
"outputs": [
53+
[
54+
0.5,
55+
0,
56+
0,
57+
1
58+
]
59+
]
60+
}
61+
}

test/integration/style-spec/tests/functions.input.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,22 @@
986986
]
987987
}
988988
}
989+
}, {
990+
"id": "invalid expression - color-relief-color must be zoom constant",
991+
"type": "color-relief",
992+
"source": "source",
993+
"source-layer": "layer",
994+
"paint": {
995+
"color-relief-color": ["step", ["zoom"], ["step", ["elevation"], "red", 0.5, "blue"], 1, ["step", ["elevation"], "yellow", 0.5, "green"]]
996+
}
997+
}, {
998+
"id": "invalid expression - color-relief-color must not be a function",
999+
"type": "color-relief",
1000+
"source": "source",
1001+
"source-layer": "layer",
1002+
"paint": {
1003+
"color-relief-color": { "stops": [[0, "red"], [1, "blue"]] }
1004+
}
9891005
}
9901006
]
9911007
}

test/integration/style-spec/tests/functions.output-api-supported.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,5 +202,21 @@
202202
{
203203
"message": "layers[56].paint.line-width.stops[0][1]: expressions are not allowed in function stops.",
204204
"line": 981
205+
},
206+
{
207+
"message": "layers[57]: layer \"invalid expression - color-relief-color must be zoom constant\" requires a raster-dem source",
208+
"line": 992
209+
},
210+
{
211+
"message": "layers[57].paint.color-relief-color: zoom expressions not supported",
212+
"line": 995
213+
},
214+
{
215+
"message": "layers[58]: layer \"invalid expression - color-relief-color must not be a function\" requires a raster-dem source",
216+
"line": 1000
217+
},
218+
{
219+
"message": "layers[58].paint.color-relief-color: zoom functions not supported",
220+
"line": 1003
205221
}
206222
]

0 commit comments

Comments
 (0)