I. Introduction

interpolate is the expression used for interpolation in the mapboxgl map style, which can interpolate colors and numbers.

There are two types of application scenarios:

  1. Color-stretched rendering of map data . Common application scenarios are: heat map, trajectory map, model mesh rendering, etc.
  2. Interpolate graphics properties when the map is zoomed . Specifically, with the zooming of the map, when changing attributes such as icon size, building height, graphic color, etc., the attributes are interpolated to achieve a smooth transition effect.

This article introduces the common application scenarios of the interpolation tool in mapboxgl interpolate .

2. Grammar

First look at the syntax of the interpolate interpolation tool.

interpolate有5个参数, 表达式名称插值类型输入值判断值输出值 .

 ["interpolate",        //表达式名称
    interpolation: ["linear"] | ["exponential", base] | ["cubic-bezier", x1, y1, x2, y2 ],  //插值类型
    input: number,    //输入值
    stop_input_1: number, stop_output_1: OutputType,        //一组判断值和输出值
    stop_input_n: number, stop_output_n: OutputType, ...    //一组判断值和输出值
]: OutputType (number, array<number>, or Color)        //返回插值完的结果

Among them 插值类型 will be introduced in detail later, so I won't talk about it here.

判断值 , 输出值 are "set" relationships, and they must appear in pairs.

Another point to note is that 判断值 must follow the ascending order .

It will be easier for us to understand the actual scene below. Let’s talk about the first type of application scenario: color-stretching rendering of map data.

3. Stretch rendering of map colors

This is the same as the color stretch rendering of raster data in ArcGIS.

The essence of map color stretching rendering is to set the color of the grid according to the attribute value of the grid. When the grid is small enough and dense enough, it is easy to produce the effect of smooth color transition.

As mentioned earlier, common application scenarios are: heat map, trajectory map, model mesh rendering, etc.

In mapboxgl, although heat maps and trajectory maps do not look like they are composed of grids, under the framework of computer graphics, any content displayed on the screen is presented by pixels, and pixels are Regularly arranged grids, so heatmaps and trajectories can also be seen as composed of grids.

This is especially obvious when developing WebGL, because you need to write your own fragment shader to define the color of each pixel.

mapboxgl provides pixel attribute value calculation tools for heat maps and trajectory maps:

  • The heatmap is heatmap-density expression, which is used to calculate the heat value of each pixel on the heatmap.
  • The trajectory line is line-progress expression, which is used to calculate the travel percentage of each pixel on the current line segment.

When the model mesh is rendered, the mesh needs to be generated by itself, and the attribute values in the mesh need to be calculated by itself. Usually, these are completed by the model in the project, such as: EFDC hydrodynamic model, Gaussian plume air pollution diffusion model, etc.

The result of the model output is a grid with attribute values, interpolate the task of the expression is still to color the grid according to the grid's attribute values.

1. Heatmap

Realize the effect:

The data uses the number of barrier-free facilities in parks and green spaces in Beijing.

The code is:

 //添加图层
map.addLayer({
    "id": "park",
    "type": "heatmap",
    "minzoom": 0,
    "maxzoom": 24,
    "source": "park",
    "paint": {
        "heatmap-weight": 1,
        "heatmap-intensity": 1,
        'heatmap-opacity':0.4,
        'heatmap-color': [//热力图颜色
            'interpolate',
            ['linear'],
            ['heatmap-density'],
            0,'rgba(255,255,255,0)',
            0.2,'rgb(0,0,255)',
            0.4, 'rgb(117,211,248)',
            0.6, 'rgb(0, 255, 0)',
            0.8, 'rgb(255, 234, 0)',
            1, 'rgb(255,0,0)',
        ]
    }
});

In the above code, use the interpolate expression for linear interpolation, the input value is heatmap-density the heat map density, the value of the heat map density is between 0-1, and the output value is the heat map The color of each pixel.

 'heatmap-color': [
    'interpolate',
    ['linear'],
    ['heatmap-density'],
    0,'rgba(255,255,255,0)',
    0.2,'rgb(0,0,255)',
    0.4, 'rgb(117,211,248)',
    0.6, 'rgb(0, 255, 0)',
    0.8, 'rgb(255, 234, 0)',
    1, 'rgb(255,0,0)',
]

Expression details:

  • The density is 0或小于0 , the output color 'rgba(255,255,255,0)'
  • The density is 0-0.2 and the output color is between 'rgba(255,255,255,0)' and 'rgb(0,0,255)'
  • The density is 0.2 and the output color 'rgb(0,0,255)'
  • The density is 0.2-0.4 and the output color is 'rgb(0,0,255)' and 'rgb(117,211,248)'之间
  • The density is 0.4 and the output color 'rgb(117,211,248)'
  • The density is 0.4-0.6 and the output color is between 'rgb(117,211,248)' and 'rgb(0, 255, 0)'
  • The density is 0.6 , the output color 'rgb(0, 255, 0)'
  • The density is 0.6-0.8 and the output color is between 'rgb(0, 255, 0)' and 'rgb(255,0,0)'
  • The density is 0.8 , the output color 'rgb(255, 234, 0)'
  • The density is 0.8-1 and the output color is between 'rgb(255, 234, 0)' and 'rgb(255,0,0)'
  • The density is 1或大于1 , the output color 'rgb(255,0,0)'

Online example: http://gisarmory.xyz/blog/index.html?demo=mapboxglStyleInterpolate1

Another rendering method corresponding to color stretching rendering is to use step expression to color- classify the data.

The implementation of color classification rendering is in the code of the above example, but it is annotated. You can download the code and try it yourself.

The effect is as follows:

2. Trajectory diagram

An example is provided on the official website of mapboxgl, which uses color to express the progress of the trajectory. The renderings are as follows:

它是用线的line-gradient属性来实现的,其中用到了插值表达式---2e709c05ea9e0566ac1e602dbdd00cc4 interpolate和线进度表达式line-progressinterpolate The role of the expression here is still to color-stretch the attribute value. The code is as follows:

 map.addLayer({
    type: 'line',
    source: 'line',
    id: 'line',
    paint: {
        'line-color': 'red',
        'line-width': 14,
        // 'line-gradient' 必须使用 'line-progress' 表达式实现
        'line-gradient': [    //
            'interpolate',
            ['linear'],
            ['line-progress'],
            0, "blue",
            0.1, "royalblue",
            0.3, "cyan",
            0.5, "lime",
            0.7, "yellow",
            1, "red"
        ]
    },
    layout: {
        'line-cap': 'round',
        'line-join': 'round'
    }
});

In actual projects, it is relatively rare to use color to express the progress of the trajectory, and more often we need to use color to express the speed of the trajectory.

Use color to indicate trajectory speed:

We have prepared a cycling track data, the track consists of multiple line segments, each line segment contains the attributes of start speed, end speed and average speed, two adjacent line segments, the end point of the previous line segment and the start of the next line segment points, they have the same latitude, longitude and speed.

 //line数据中的单个线段示例
{
    "type": "Feature",
        "properties": {
            "startSpeed": 8.301424026489258, //开始速度
            "endSpeed": 9.440339088439941, //结束速度
            "speed": 8.8708815574646 //平均速度
        },
        "geometry": {
            "coordinates": [
                [
                    116.29458653185719,
                    40.08948061960585
                ],
                [
                    116.29486002031423,
                    40.08911413450488
                ]
            ],
                "type": "LineString"
        }
}

The simplest way to do this is to assign a color to each line segment based on the average speed of the line segment.

The implementation is still using the interpolate expression, which is used to interpolate the color based on the velocity of the line segments in the trajectory.

The core code is as follows:

 //添加图层
map.addLayer({
    type: 'line',
    source: 'line',
    id: 'line',
    paint: {
        'line-color': [
            'interpolate',//表达式名称
            ["linear"],//表达式类型,此处是线性插值
            ["get", "speed"],//输入值,此处是属性值speed
            0,'red',//两两出现的判断值和输出值
            8,'yellow',
            10,'lime'
        ],
        'line-width': 6,
        'line-blur': 0.5
    },
    layout: {
        'line-cap': 'round'
    }
});

In the above code, interpolate expression means:

  • 0km/h及以下(含0km/h) output 红色
  • 0-8km/ h output 红到黄之间的颜色
  • 8km/h output 黄色
  • 8-10km/h output 黄到绿之间的颜色
  • 10km/h及以上(含10km/h) output 绿色

The effect is as follows:

Example online address: http://gisarmory.xyz/blog/index.html?demo=mapboxglStyleInterpolate2

The overall look is not bad, but when you zoom in on the map, you will find that the color is segmented and the transition is not smooth enough, as shown below:

How to make the local color smooth?

It would be nice to have a smooth transition of color between the two line segments.

Thinking of this, we remembered the previous official example of using color to indicate the progress of the trajectory. If the two methods are combined, the desired effect may be achieved.

Implementation ideas:

每条线段的属性中有开始速度 结束速度 ,根据颜色和速度的对应关系,可以插值出开始颜色 结束颜色 , the former line segment 开始颜色 and the latter line segment 结束颜色 are the same color, the color in the middle of each line segment is realized by using line-gradient 开始颜色 The gradient from 开始颜色 to 结束颜色 .

In this way, a smooth transition of color between the two line segments can be achieved.

Implementation:

According to this idea, two interpolations are required. The first interpolation is to interpolate each line segment 开始颜色 and 结束颜色 , and the second is to interpolate each pixel on each line segment. color

Originally, I wanted to implement this function by nesting multiple expressions in line-gradient , so that the code would be more concise, but I found that it would not work after many attempts line-progress There are some restrictions on the use of , so the logic of the first interpolation needs to be implemented by yourself.

The first step is to write a color interpolation function by yourself, and interpolate the 开始颜色 and 结束颜色 of each line segment. The implementation comments have been written clearly.

 //通过canvas获取开始颜色和结束颜色:
//原理是利用canvas创建一个线性渐变色对象,再通过计算颜色所在的位置去用getImageData获取颜色,最后返回这个颜色
//1.创建canvas
var canvas = document.createElement("canvas");
canvas.width = 101;
canvas.height = 1;
var ctx = canvas.getContext('2d');
//2.创建线性渐变的函数,该函数会返回一个线性渐变对象,参数0,1,101,1分别指:渐变的起始点x,y和渐变的终止点x,y
var grd = ctx.createLinearGradient(0,1,101,1) 
//3.给线性渐变对象添加颜色点的函数,参数分别是停止点、颜色
grd.addColorStop(0,'red');
grd.addColorStop(0.8,'yellow');
grd.addColorStop(1,'lime');
//4.给canvas填充渐变色
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 101, 1);
//5.返回渐变色的函数
function getInterpolateColor(r) {
    //6.这里是渐变色的精细度,我将canvas分成101份来取值,每一份都会有自己对应的颜色
    //声明的变量x就是我们需要的颜色在渐变对象上的位置
    let x =  parseInt(r * 100);
    x>100?x=100:x=x
    //7.传入插值颜色所在的位置x,通过canvas的getImageData方法获取颜色
    var colorData = ctx.getImageData(x, 0, 1, 1).data;
    //8.返回这个颜色
    return `rgba(${colorData[0]},${colorData[1]},${colorData[2]},${colorData[3]})`
}

In the second step, each line segment is set as a layer, and each layer calls the method in the first step to obtain the 开始颜色 and 结束颜色 of the line segment, and then use line-gradient The line-gradient sets the color of the middle of the line segment.

 //allFeatures是line数据中单个线段组成的集合
allFeatures.map((item,index)=>{
    //通过上面的渐变色函数获取开始颜色和结束颜色
    let startColor = getInterpolateColor(item.properties.startSpeed/10)
    let endColor = getInterpolateColor(item.properties.endSpeed/10)
    //循环添加图层
    map.addLayer({
        type: 'line',
        source: 'line',
        id: 'line'+index,
        paint: {
            'line-width': 6,
            'line-blur': 0.5,
            'line-gradient': [
                'interpolate',
                ['linear'],
                ['line-progress'],
                0, startColor,
                1, endColor
            ]
        },
        layout: {
            'line-cap': 'round',
        },
        'filter': ['==', "$id", index]
    });
})

Each line segment is set as a layer, and there may be thousands of layers in the end, which is not easy to manage.

Here is another idea, you can combine all line segments into a polyline, and then calculate the speed, color and percentage of the entire trajectory of each node on the polyline. The percentage of the entire trajectory is calculated by the length of the node from the starting point and the end point. .

The percentages and colors of all nodes are used as the judgment parameters of line-gradient , so that the same effect as multiple layers can be produced, and only one layer needs to be created.

The disadvantage of this method is that it needs to process data, and which one is suitable can be determined according to the actual situation.

The final effect is as follows:

Example online address: http://gisarmory.xyz/blog/index.html?demo=mapboxglStyleInterpolate3

2. Model mesh rendering

In this mode, the grid data mainly comes from the model output results. On the basis of the output results, you only need to use the interpolate interpolation tool to interpolate the grid color according to the grid attribute value.

The following code and renderings are examples of the output of the EFDC model. This grid is relatively larger, but the transition in the middle part is still natural.

Code:

 //图层
{
    "id": "waterTN",
    "type": "fill",
    "source": "efdc",
    "paint": {
        "fill-color": [
            "interpolate",
            ["linear"],
            ["get", "TN"],//输入值是属性TN
            0, "#36D1DC",
            15, "#6666ff",
            20, "#4444FF"
        ]
    }
}

Effect picture:

4. Interpolate graphic properties as the map zooms

The mapboxgl official website gives two relevant examples:

One is to change the color of the building according to the zoom level , which interpolates the color and transparency of the building at the same time.

Relevant code:

 //对颜色插值
map.setPaintProperty('building', 'fill-color', [
    "interpolate",
    ["exponential", 0.5],
    ["zoom"],
    15,
    "#e2714b",
    22,
    "#eee695"
]);
//对透明度插值
map.setPaintProperty('building', 'fill-opacity', [
    "interpolate",
    ["exponential", 0.5],
    ["zoom"],
    15,
    0,
    22,
    1
]);

Effect picture:

缩放改变颜色3

The other is to change the display height of the building according to the zoom level of the map , which interpolates the height of the building and the height of the building from the map.

Relevant code:

 map.addLayer({
    'id': '3d-buildings',
    'source': 'composite',
    'source-layer': 'building',
    'filter': ['==', 'extrude', 'true'],
    'type': 'fill-extrusion',
    'minzoom': 15,
    'paint': {
        'fill-extrusion-color': '#aaa',
        'fill-extrusion-height': [
            "interpolate", ["linear"],
            ["zoom"],
            15, 0,
            15.05, ["get", "height"]
        ],
        'fill-extrusion-base': [
            "interpolate", ["linear"],
            ["zoom"],
            15, 0,
            15.05, ["get", "min_height"]
        ],
        'fill-extrusion-opacity': .6
    }
}, labelLayerId);

Effect picture:

缩放改变高度

Similarly, we can also interpolate the size of the map icon, for example, the larger the zoom level, the larger the icon, and the smaller the zoom level, the smaller the icon, etc.

5. Advanced usage of interpolate

When we introduced the syntax of the interpolation tool interpolate , we did not introduce the option 插值类型 . We will talk about it in this section.

In most of the previous examples, we used the --- 插值类型 option ['linear'] this type, which means linear interpolation.

In addition to linear interpolation, 插值类型 also supports ["exponential",base] exponential interpolation and ["cubic-bezier", x1, y1, x2, y2] cubic Bezier interpolation.

Their syntax is:

  • ["linear"] Linear interpolation, no other parameters.
  • ["exponential",base] exponential interpolation, base parameter is the exponent value.
  • ["cubic-bezier",x1,y1,x2,y2]三次贝塞尔曲线插值, x1y1x2y2 4个参数用于Controls the shape of the Bezier curve.

It may sound a bit abstract, let's take an example:

The following piece of code changes the transparency of buildings based on the map zoom level:

 map.setPaintProperty('building', 'fill-opacity', [
   "interpolate", 
    ["linear"],
    ["zoom"],
    15,0,
    22,1
]);

means:

  • When the zoom level is less than 15, the transparency is 0.
  • When the zoom level is greater than or equal to 22, the transparency is 1.
  • When the zoom level is between 15 and 22, the value of transparency is automatically calculated using linear interpolation, between 0 and 1.

Linear interpolation:

If the zoom level is set to x, the transparency is set to y, and the value of x is limited between 15 and 22, the equation for linear interpolation is:

y=(x-15)/(22-15)

As can be seen intuitively from the function image below, it is a straight line, which means that when the map is zoomed in, from level 15 to level 22, the opacity of the building will increase at a constant speed until it is fully displayed.

Exponential interpolation :

The exponential interpolation equation adds an exponential value to the linear interpolation equation. This value is represented by z. The equation is:

y=((x-15)/(22-15))^z

Through the z value, we can adjust the shape of the function image, such as: take the z value of 0.1, 0.5, 1, 2, and 10, respectively, and draw the picture as follows:

For example, the purple line with an exponent of 10 in the above figure is an example. When the map is zoomed in from level 15 to level 19, the building will never be seen because the transparency of the building is always 0.

Continue to zoom in, from level 19 to level 22, the building will appear quickly until fully displayed.

This is the difference between exponential interpolation and linear interpolation, which provides us with a way to control the speed of the interpolation output .

Cubic Bezier interpolation:

The cubic Bezier curve interpolation and the exponential interpolation above are the same, both to be able to control the speed of the interpolation output more flexibly.

Or use the function image to help understand. The exponentially interpolated image can only bend in one direction. When the index is between 0 and 1, the curve is curved upward, and when the index is greater than 1, the curve is curved downward.

The cubic Bezier curve interpolation can bend the curve in both directions.

The mapboxgl official website provides an example of ocean depth , which uses cubic Bezier curve interpolation.

In the example, a cubic Bezier curve is used to interpolate the color representing the depth of the ocean, and the effect is as follows:

The relevant code is as follows:

 {
    'id': '10m-bathymetry-81bsvj',
    'type': 'fill',
    'source': '10m-bathymetry-81bsvj',
    'source-layer': '10m-bathymetry-81bsvj',
    'layout': {},
    'paint': {
    'fill-outline-color': 'hsla(337, 82%, 62%, 0)',
    'fill-color': [
        'interpolate',
        ['cubic-bezier', 0, 0.5, 1, 0.5],
        ['get', 'DEPTH'],
        200,'#78bced',
        9000,'#15659f'
        ]
    }
},

上面代码中,三次贝塞尔曲线插值的4个参数x1y1x2y2 : 0, 0.5, 1, 0.5.

Its function image is:

As can be seen from the above figure, the output speed of the function is first fast, then slow and finally fast . Combined with the example of ocean depth, when the depth is near 200 meter and 9000 meter, the color The change is fast, and when the depth is in the middle, the color change is relatively gentle. The following two figures are a comparison of linear interpolation and cubic Bezier curve interpolation:

The above picture uses ["linear"] linear interpolation, the color is output at a uniform speed, and the change of depth can be seen, but the 'blocky feeling' is obvious

The following figure uses ['cubic-bezier', 0, 0.5, 1, 0.5] cubic Bezier curve interpolation, the color output is first fast, then slow, and finally fast, which can not only see the depth change, but also achieve a smooth effect of natural transition , will make it feel softer.

The recommended article is an easy-to-understand explanation of cubic Bezier curves to understand how cubic Bezier curves are drawn. There is also a tool website where you can draw points to help you understand.

The functions represented by these three interpolation methods can be drawn in the coordinate axis. Whether it is a straight line or various curves, we do not need to worry about how the line is drawn, because we can use tools to complete this step. , what needs to be concerned about is the speed of the output of this line, which is related to the smooth transition of the meaning of our "interpolate" expression.

6. Summary

  1. interpolate is the expression used for interpolation in the mapboxgl map style, which can interpolate colors and numbers, allowing the map to achieve a smooth transition effect.
  2. There are two types of application scenarios, one is color-stretching rendering of map data, such as: heat map, trajectory map, model grid rendering, etc.
  3. The other is to interpolate graphic attributes when the map is zoomed, such as: slow changes in the height of buildings and smooth switching of graphics colors as the map is zoomed.
  4. interpolate Interpolation tool provides three interpolation methods, linear interpolation, exponential interpolation, cubic Bezier curve interpolation, the difference between them is to control the speed of the interpolation output.



Original address: http://gisarmory.xyz/blog/index.html?blog=mapboxglStyleInterpolate

Welcome to " GIS Arsenal "

This article is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Welcome to reprint, use and republish, but be sure to keep the article signature "GIS Armory" (including the link: http://gisarmory.xyz/blog/ ), not for commercial purposes, works modified based on this article must use the same Published with permission.


GIS兵器库
64 声望15 粉丝