基于思极地图应用的优化


我们在项目中使用了思极地图,需要在地图上绘制大量的线路和图标。最初的做法是每个线路和图标都用独立的图层绘制,当数据量增加时,地图表现开始出现卡顿。为了优化这个问题,我们需要将所有图标归为一个图层。

通过对卡顿原因的分析,我们确定了图层数量过多是导致卡顿的主要原因。而解决方法就是将所有图标和所有线路归为一个图层。在思极地图基于mapbox开发的背景下,我们可以查阅mapbox文档,得到可以通过GeoJson的方式,对地图进行调优,可以有效地解决地图卡顿问题。

我们通过一个简单的例子来实现优化

图标都是用的图片,我这里直接用circle表示,区别无非就是loadImage,addImage,url

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
51
52
53
54
55
56
57
const colors = {
supermarket: "#1E90FF",
cafe: "#90EE90",
basketballCourt: "#FFA500",
}

const mockData = [
{ id: "1", iconType: "supermarket", lngLat: [116.38918825391062,39.90396280207008] },
{ id: "2", iconType: "cafe", lngLat: [116.41974397900901,39.904489540086104] },
{ id: "3", iconType: "supermarket", lngLat: [116.33288332227039,39.918709935748126] },
{ id: "4", iconType: "cafe", lngLat: [116.32395693066911,39.87709379386882] },
{ id: "5", iconType: "basketballCourt", lngLat: [116.34249635938016,39.854694909073345] },
]
const initMap = useCallback(() => {
if (!mapRef.current && isLogin) {
mapRef.current = new SGMap.Map({
container: "map",
style: "aegis://styles/aegis/StreetsDark",
zoom: 11,
center: [116.397428, 39.90923],
localIdeographFontFamily: "Microsoft YoHei",
});
mapRef.current.on("load", async() => {
initIcons();
})
}
}, [initIcons, isLogin])

const initIcons = useCallback(() => {
mockData.forEach((i,index) => {
mapRef.current.addLayer({
id: i.id,
type: "circle",
source: {
type: "geojson",
data: {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: i.lngLat,
},
},
],
},
},
paint: {
"circle-radius": 10,
"circle-color": colors[i.iconType],
"circle-stroke-color": colors[i.iconType],
"circle-stroke-width": 5,
},
})
})
}, [])

上面有个问题很明显,5个图标对应了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
36
37
38
const initIcons = useCallback(() => {
const circleArray = [];
mockData.forEach((i) => {
circleArray.push({
id: i.id,
type: "circle",
properties: { // 可以自定义属性
iconId: i.id,
iconType: i.iconType,
circleRadius: 10,
circleColor: colors[i.iconType],
circleStrokeColor: colors[i.iconType],
circleStrokeWidth: 5,
},
geometry: {
type: "Point",
coordinates: i.lngLat,
},
})
})
mapRef.current.addSource("icons", {
type: "geojson",
data: {
type: 'FeatureCollection',
features: circleArray
}
})
mapRef.current.addLayer({
id: "iconsLayerId",
type: "circle",
source: "icons",
paint: {
"circle-radius": ["get", "circleRadius"],
"circle-color": ["get", "circleColor"],
"circle-stroke-color": ["get", "circleStrokeColor"],
"circle-stroke-width": ["get", "circleStrokeWidth"],
},
});

properties:我们可以在这里自定义属性
geometry: 图形类型:点、多点、线段、多线段、面、多面
addSource:定义了资源,第一个参数为资源ID,第二个参数为Geojson数据
addLayer:source对应的就是资源ID,paint样式中我们可以看到,用了表达式取出了properties中的value

清除图层

现在只需要简单的一行mapRef.current.removeLayer("iconsLayerId");就可以把图标图层全部清除。

过滤条件 setFilter

可以将任何布局特性、绘制特性或过滤器的值定义为表达式
setFilter第一个参数为设置过滤条件的图层,第二个参数为表达式mapRef.current.setFilter("iconsLayerId", ["==", "iconType", "cafe"]);就可以只展示类型为cafe的图标

点击高亮

我们先为图标图层添加点击事件mapRef.current.on('click', 'iconsLayerId', handleIconClick);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 点击事件
const handleIconClick = useCallback((e) => {
const clickedFeatureId = e.features[0].id;
mapRef.current.setPaintProperty('iconsLayerId', 'circle-color', [
'case', // 选择语句
['==', ['id'], clickedFeatureId], // 匹配id为点击图标的id
'yellow', // 修改为黄色
['get', 'circleColor'], // 否则,颜色保持原来的
]);
mapRef.current.setPaintProperty('iconsLayerId', 'circle-stroke-color', [
'case',
['==', ['id'], clickedFeatureId],
'yellow',
['get', 'circleStrokeColor'],
]);
}, [])

这样我们高亮也不需要添加新的图层了。
表达式很强大,可以写检索表达式,逻辑表达式,具体看文档就行

请求优化

因为我们需要将坐标转国网坐标,每次坐标转换又需要调用接口,每条线路点位的经纬度又不固定,还好转坐标接口支持一次性同时转200个,那么我们要做的就是,需要后端提供一个全量线路坐标的接口,并且顺序必须和线路保持一致,这样的话 total / 200,计算出需要调用几次接口,然后分别插入到顺序线路的坐标中即可。

最后简单理解下GeoJson

顾名思义,GeoJson就是用json对地理数据结构进行编码的一种格式,GeoJson对象表示了以下7种几何形状类型(type)

  • Point: 表示点,coordinates:[10,10]

  • MultiPoint: 表示多点,coordinates:[[10,10],[20,20]]

  • LineString:表示线段,线段的起点和重点,coordinates:[[10,10],[20,20]]

  • MultiLineString:表示多条线段, coordinates:[[[10,10],[20,20]],[[30,30],[40,40]]]

  • Polygon:表示多边形,坐标成员必须是线性环坐标数组的数组,也就是说,首和尾的坐标要一致,coordinates:[[10,10],[20,20],[10,10]]

  • MultiPolygon: 表示多个多边形,coordinates成员是Polygon坐标数组的数组

  • GeometryCollection:表示复合类型,上面6种类型的组合geometries代替coordinates,geometries的每个元素都是一个Geometry对象,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    "geometries": [
    {
    "type": "Point",
    "coordinates": [0, 0]
    }, {
    "type": "Polygon",
    "coordinates": [[[45, 45], [45, -45], [-45, -45], [-45, 45], [45,45]]]
    }
    ]

Feature

Feature类型的对象定义实体的几何形状和属性。
Feature 对象有一个名为“type”且值为“Feature”的成员。它还有一个名为“geometry”的成员,其值为我们上面讨论的任何几何形状或空值。此外,它有一个名为“properties”的成员,其值是一个 JSON 对象(或空值),定义了该对象的属性。

1
2
3
4
5
6
7
8
9
10
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [116.40360780957536,39.92265952170874],
},
"properties": {
"name":"myName"
}
},

FeatureCollection

FeatureCollection对象就是Feature对象的组合

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
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-80.83775386582222, 35.24980190252168]
},
"properties": {
"name": "DOUBLE OAKS CENTER",
"address": "1326 WOODWARD AV"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-80.83827000459532, 35.25674709224663]
},
"properties": {
"name": "DOUBLE OAKS NEIGHBORHOOD PARK",
"address": "2605 DOUBLE OAKS RD"
}
}
]
}

这篇文章完全可以入门GeoJson: http://www.bimant.com/blog/geojson-intro/
入门GeoJson

我的微信公众号: 梨的前端小屋


文章作者: 梨啊梨
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 梨啊梨 !
  目录