# 数据源与图层

# 主要特点:

  • 无需区分绘制、布局和其他属性。
  • 所有属性都可以表示为camelCase 而不是kebab-case。
  • 层操作可以作用于多个层(由数组、正则表达式或过滤器函数给出),而不仅仅是一层。
  • 源类型、图层类型和属性名称合并到函数名称中:addGeoJSON(), addCircleLayer(), setCircleRadius(), getTextFont()...
  • 添加层和源是幂等的:调用addLineLayer()多次创建,然后更新层。
  • 其他一些便利功能:show(), hide(), onLoad(), setData(),fontsInUse()
  • 更好的点击和悬停功能:hoverPointer(), hoverFeatureState(), hoverPopup(),clickLayer()
  • 某些功能表现更好:(removeLayer()如果图层不存在则不会出错),removeSource()(自动删除附加图层),setFilter()(一次在多个图层上工作),setData()如果没有提供 GeoJSON ,则清除数据。

# 使用图层

props添加图层时传递的对象可以自由混合绘制、布局等属性。可以在camelCase 或kebab-case 中指定属性键:

map.addCircleLayer('trees-circle', 'trees', {
    circleColor: 'green', // paint property
    circleRadius: ['interpolate', ['zoom'], 12, 3, 15, 5], // paint property
    circleSortKey: ['get', 'tree-sort-key'], // layout property
    filter: ['!=', 'type', 'stump'], // other property
});
1
2
3
4
5
6

几乎所有适用于现有层(例如show())的方法都可以适用于多个层。有四种方法可以指定要修改的图层:

  • 字符串: map.show('trees-label'); map.show('trees-circle');
  • 字符串数组:map.show(['trees-label', 'trees-circle']);
  • 正则表达式:map.show(/^trees-/);
  • 接受一个层并返回真实值的函数: map.show(layer => layer.source === 'trees');

# 添加源

添加源的方法返回一个对象,可以链接该对象以允许向其中添加层:

map.addGeoJSONSource( 'properties' )
.addCircleLayer( 'properties-line' , { lineWidth : 3 })
.addSymbolLayer( 'properties-fill' , { fillColor : 'hsla(30,30%,60%,0.5)' })
1
2
3

添加和删除图层

// 方便地添加线要素,混合绘制,布局等属性。
// 请注意,您可以对所有属性名称使用驼峰命名法。
map.addLineLayer( 'mylines' , 'mysource' , {
     lineWidth : 3 ,
     lineCap : 'round' ,
     minzoom : 11
});

// 还有 addFillLayer, addFillExtrusionLayer, addRasterLayer, addVideoLayer, addSymbolLayer, addHillshadeLayer, addHeatmapLayer 
map.addCircleLayer( 'mycircles' , 'mysource' , { circleStrokeColor : 'red' });
// 如果图层已经存在,调用 add*Layer 只会更新任何属性
map.addCircleLayer( 'mycircles' , 'mysource' , { circleStrokeColor : 'red' , circleRadius : 4 , filter: ['==', 'type', 'active'});


// 当然,如果需要,在另一个图层“之前”添加图层: 
map.addLineLayer( 'mylayer' , 'mysource' , { lineColor : 'red' }, 'toplayer' );

// 如果图层不存在,removeLayer() 不会抛出错误
map.removeLayer([ 'towns' , 'town-labels' ]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 添加和删除源

// 创建 GeoJSON 源的更简单方法: 
map.addGeoJSON( 'mysource' , geojson);

// 或者使用初始空白数据创建一个 GeoJSON 源。如果您单独加载, 数据并且稍后将调用`setData()`这将非常方便。
map.addGeoJSON( 'mysource' );

// 创建矢量切片源的更简单方法: 
map.addVector( 'mysource' , 'xxx' );
map.addVector( 'mysource' , 'https://example.com/tiles/{z}/{x}/{y}.pbf' );

// 其他属性仍然有效
map.addVector( 'mysource' , 'https://example.com/tiles/{z}/{x}/{y}.pbf' , { maxzoom : 13 });

// 还有 addRaster(), addRasterDem(), addImage(), addVideo() 
// 调用任何 add* 函数只会更新源定义(如果源定义已经存在)。

// 使用这些源自动删除任何层。如果源不存在,则不是错误。
map.removeSource(['buildings', 'roads']);

// 您也可以使用返回的对象方便地添加图层: 
map.addGeoJSON( 'buildings' , 'data/buildings.geojson' )
    .addFillExtrusion( 'buildings-3d' , {
         fillExtrusionHeight : 100 ,
         fillExtrusionColor : 'grey' 
    }).addLineLayer( 'buildings-footprint' , {
         lineColor : ' lightblue '
    });

// 替换现有图层上的源。(实际上删除并重新添加它。) 
map.setLayerSource( 'buildings' , 'newsource' );
map.setLayerSource([ 'buildings-3d' , 'buildings-outline]' , 'newsource' , 'newsourcelayer' );

// 要更改源图层,请传递第三个参数或 null 以清除它(如果从矢量切片切换到 geojson) 
map.setLayerSource( 'buildings' , 'mylocalbuildings' , null );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 设置属性和更新数据

// 每个属性都有一个 setXxx() 形式: 
map.setTextSize( 'mylayer' , 12 );

// 它们都同时作用于多个层: 
map.setLineWidth([ 'mylayer' , 'mylayer-highlight' ], 4 );
map.setLineOpacity( /^ border- / , 0 );
map.setFillColor( layer => layer.source === 'farms' , 'green' );

// 还有一个更熟悉的 setProperty() 形式。
map.setProperty( 'mylayer' , 'line-width' , 3 );
// 现有属性不会被触及
map.setProperty( 'mylayer' , {
     textSize : 12 ,
     textColor : 'red'
});

// 每个函数都有一个 `get...` 版本。
map.getFillColor( 'water' )

// 更新源数据的更简单方法: 
map.setData( 'mysource' , data);

// 您可以省略 data 参数以清除 GeoJSON 源: 
map.setData( 'mysource' );

// 更容易记住打开和关闭图层的方法: 
map.show( 'mylayer' );
map.hide( 'mylayer' );
map.toggle([ 'mylayer' , 'myotherlayer' ], isVisible);

// 为避免名称冲突,例如与 'raster' 的名称冲突,您可以使用更长的形式
// 以 ...Layer() 或 ...Source()结尾

map.addRasterSource( 'myrastersource' , { type : 'raster' , url : 'xxx' , tileSize : 256 });
map.addRasterLayer( 'myrasterlayer' , 'myrastersource' , { rasterSaturation : 0.5 });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 悬停和点击

// 悬停在此图层上时使用鼠标“手指”光标。
map.hoverPointer( 'mylayer' );

// 如果您传递多个图层,它会正确处理从一个图层移动到另一个图层
// 悬停在该图层上时使用鼠标“手指”光标。
map.hoverPointer([ 'regions-border' , 'regions-fill' ]);

// 当鼠标移动到该层的特征上时,将“悬停”特征状态设置为真或假。
// 要求特性有一个 `id`。
map.hoverFeatureState( 'mylayer' );

// 想要将悬停功能状态应用于不同的源?
// 例如,您将鼠标悬停在标签上,但想要突出显示周围的边界。
map.hoverFeatureState( 'town-labels' , 'boundaries' , 'town-boundaries' );

// 您还可以添加其他事件处理程序: 
map.hoverFeatureState( 'mylayer' , 'mysource' , 'mysourcelayer' ,
    e => console.log(`Entered ${e.features[0].id}`),
    e => console.log(`Left ${e.oldFeatureid}`);

// 当一个功能悬停或点击时显示一个弹出窗口。
// 第三个参数是一个选项对象,传递给 Popup 构造函数。
// 回调被调用为: (feature, popup) => htmlString 
map.hoverPopup( 'mylayer' , f =>  `<h3> ${f.properties.Name} </h3> ${f.properties.Description} ` , { anchor : 'left' });
map.clickPopup( 'mylayer' , f =>  `<h3> ${f.properties.Name} </h3> ${f.properties.Description} ` , { maxWidth : 500 });

// clickLayer() 类似于 .on('click)',但可以接受一个数组并向事件中添加一个 'features' 成员用于点击的内容。
map.clickLayer([ 'towns' , 'town-labels' ], e => panel.selectedId = e.features[ 0 ].id);

// clickOneLayer 按顺序测试多个层,在第一个命中的 上触发回调。回调被传递 { 功能,功能,层,事件 }。
map.clickOneLayer([ 'town-labels' , 'state-boundaries' ], e => {
    if (e.layer === 'town-labels') {
        setView('town');
        panel.selectedId = e.features[0].id;
    } else if (e.layer === 'state-boundaries') {
        setView('state');
        panel.selectedId = e.features[0].id;
    }
});

// 可选地传入一个额外的回调,该回调为错过所有图层的点击而触发: 
map.clickOneLayer(['town-labels', 'state-boundaries'], e => {...}, e => {
    console.log('Missed everything');
});


// 所有这些函数都返回一个“撤消”函数,删除添加的处理程序:
const remove = map.hoverPopup( 'mylayer' , showPopupFunc);
//...
remove(); // 不再有悬停弹出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# 其他功能

// 与 on('load') 类似,但在地图加载后的任何时候都会立即(并且可靠地)触发。
map.onLoad(回调);
// 如果没有回调,则返回一个承诺:
await map.onLoad();

// 获取图层定义。
const layer = map.getLayerStyle( 'mylayer' );

// 首先将所有其他属性重置为默认值。忽略非绘制、非布局属性。
map.setLayerStyle( 'mylayer' , {
     lineWidth : 3
});

// properties() 将对象转换为 `kebab-case`写法的图层对象
map.addLayer(map.properties({
    id : 'mylayer' ,
     source : 'mysource' ,
     type : 'line' ,
     lineWidth : 3 ,
     lineCap : 'round' ,
     minzoom : 11 ,
     filter : [ '==' , 'status' , 'confirmed' ]
}));

// layerStyle() 很灵活,可以根据需要传递尽可能多的  id, source, 和 type
map.layerStyle( 'mylayer' , 'mysource' , 'line' , { ... })
map.layerStyle( 'mylayer' , 'mysource' , { ... })
map.layerStyle( 'mylayer' , { ... })
map.layerStyle({ ... })


// 隐藏/显示/切换附加到此源地图的所有图层
map.hideSource('buildings');
map.showSource('buildings');
map.toggleSource('buildings', true);

// 一次更新多个过滤器。
map.setFilter([ 'buildings-fill' , 'buildings-outline' , 'buildings-label' ], [...]);

// 一步方便地将图像加载到地图中
map.loadImage( 'marker' , '/assets/marker-pin.png' );
map.loadImage( 'marker' , '/assets/marker-pin@2x.png' , { pixelRatio : 2 }).then( /* ... */) ;


// 更新地图样式的根“transition”属性
map.setTransition({ delay : 1000 , delay : 0 });

// 使用 fontsUsed() 获取符号层中使用的字体列表。用于快速显示一些文本。
const fonts = map.fontsInUse();
map.addSymbolLayer( 'labels' , 'mysource' , { textFont : fonts[ 0 ], textField : '{label}' });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# 例子

map.onload( () => {
    map.addGeoJSON('towns');
    map.addCircleLayer('small-towns', 'towns', { circleColor: 'green', filter: ['==', 'size', 'small']});
    map.addCircleLayer('large-towns', 'towns', {
        circleColor: 'red',
        filter: ['==', 'size', ['large']],
        circleStrokeWidth: ['case', ['to-boolean', ['feature-state', 'hover']], 5, 1]
    );
    map.setCircleRadius(['small-towns', 'large-towns'], 12);
    map.hoverPointer(['small-towns', 'large-towns']);
    map.hoverFeatureState('large-towns');
    // 更新数据
    d3.json('http://example.com/towns.json', data => map.setData('towns', data));
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14