共计 4492 个字符,预计需要花费 12 分钟才能阅读完成。
进行图形可视化,难免会遇到地理数据的可视化需求。通常情况下,直接使用 echarts 对配置项进行处理,就可以满足大部分需求。当然,更加复杂的定制化需求,可能就需要借助 d3、Three.js 等工具。如果对详细的地图背景有要求的话,又需要将图形库与 leaflet、maptalks 等地图引擎相结合。不过也许你的需求和我一样,没有那么复杂的交互需求,但对显示效果却有一些想法。那么就可以尝试阅读本文,使用一种比较偷懒的方法,仅基于 maptalks 本身,来绘制可交互的伪 3d 地图。下面,以贵州省的伪 3d 地图为例,进行代码的编写和相应数据的简单处理。1. 基本的地图绘制 maptalks(maptalks 的 git)的官方范例写得相当亲切,我们可以从中找到所有绘制伪 3d 地图需要的元素。首先,从地图底图开始。(官方入门示例)
initMapTalk() {
let map = new maptalks.Map(‘mapDom’, {
center: [121.345, 31.2088],
zoom: 9,
baseLayer: new maptalks.TileLayer(‘base’, {
urlTemplate: ‘http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png’,
subdomains: [‘a’,’b’,’c’,’d’],
attribution: ‘© <a href=”http://osm.org”>OpenStreetMap</a> contributors, © <a href=”https://carto.com/”>CARTO</a>’
})
});
}
需要注意的是,除了 maptalks.js 以外,maptalks.css 也是必须引入的。
然后,我们需要借助 maptalks.Polygon 添加一些地图区块(Polygon 示例)。虽然地图看起来和长方体不太一样,实际上这些区块也不过是稍微复杂一些的点线集合而已。作为一切绘制的基础,我们需要找一些 GeoJson 格式的数据(中国各省市级 json,世界主要国家 json)。观察 GeoJson,其中,的 coordinates 属性,就是地图边界的集合。需要注意的是,区域 type 包含 Polygon 和 MultiPolygon 两类,和 maptalks 的多边形函数相对应,在数组的层级上稍有区别。为了减少数据选取的麻烦,这里选择使用 MultiPolygon 来进行绘制。
drawPolygons(idx, coordinates, properties) {
const polygon = new maptalks.MultiPolygon(coordinates, {
symbol: {
lineWidth: 1,
lineColor: edgeColor,
polygonFill: polygonColors[0],
polygonOpacity: 0.5
},
properties: {
id: properties.id,
index: idx,
properties: properties
}
})
.on(“mouseenter”, function(e) {
e.target.updateSymbol({
polygonFill: polygonColors[1]
});
})
.on(“mouseout”, function(e) {
e.target.updateSymbol({
polygonFill: polygonColors[0]
});
})
this.polygons.push(polygon);
},
drawRegion() {
const self = this
$.getJSON(“guizhou.json”, “”, function(mapData) {
const features = mapData.features;
features.forEach((g, i) => {
const properties = g.properties;
const coordinates = g.geometry.coordinates
self.drawPolygons(i, coordinates, properties)
});
const polygonsLayer = new maptalks.VectorLayer(
“vector-polygon”,
self.polygons,
).addTo(self.mapDom);
})
},
到现在为止,一切还只是 2d 的样子。不过,maptalks 允许我们绘制 3 维的高度面(立体的线)。只需要引入一个 altitude 属性,并在底图上引入 pitch 属性使视角稍稍偏移,我们的 2.5d 地图就画出来了。
drawLimitLines(idx, coordinates, properties) {
const outLine = new maptalks.MultiLineString(coordinates, {
symbol: {
lineColor: edgeColor,
lineWidth: 1,
textPlacement: “vertex”
},
properties: {
altitude: altitude,
index: idx,
id: properties.id,
properties: properties
}
});
this.limitLines.push(outLine);
},
drawPolygons(idx, coordinates, properties) {
const polygon = new maptalks.MultiPolygon(coordinates, {
symbol: {
lineWidth: 1,
lineColor: edgeColor,
polygonFill: polygonColors[0],
polygonOpacity: 0.5
},
properties: {
altitude: altitude,
id: properties.id,
index: idx,
properties: properties
}
})
.on(“mouseenter”, function(e) {
e.target.updateSymbol({
polygonFill: polygonColors[1]
});
})
.on(“mouseout”, function(e) {
e.target.updateSymbol({
polygonFill: polygonColors[0]
});
})
this.polygons.push(polygon);
},
drawRegion() {
const self = this
$.getJSON(“guizhou.json”, “”, function(mapData) {
const features = mapData.features;
features.forEach((g, i) => {
const properties = g.properties;
const coordinates = g.geometry.coordinates
self.drawPolygons(i, coordinates, properties)
const pathCoordinates = g.geometry.type == “MultiPolygon” ? coordinates.map(d => { return d[0] }) : coordinates
self.drawLimitLines(i, pathCoordinates, properties)
});
const polygonsLayer = new maptalks.VectorLayer(
“vector-polygon”,
self.polygons,
{
enableAltitude: true
}
).addTo(self.mapDom);
const limitLinesLayer = new maptalks.VectorLayer(
“vector-line”,
self.limitLines,
{
enableAltitude: true,
drawAltitude: {
polygonFill: edgeColor,
polygonOpacity: 0.3,
lineWidth: 0
}
}
).addTo(self.mapDom);
})
},
2. 数据和样式处理到这个时候,效果还是不太令人满意。县市间的边界太丑,有没有什么办法把他去掉呢?很简单,直接绘制地图的外沿就好。不过,网上下载的贵州省边界好像和现在带有区县划分的精度不太一样?那么,就来自己处理一下吧。根据问答如何合并区域边界,访问在线的地图数据处理网站 http://mapshaper.org/,给每个县市取一个相同的别名,一番输入输出,我们就得到了贵州省的外边界。
drawBorderLines(coordinates, properties) {
const outLine = new maptalks.MultiLineString(coordinates, {
symbol: {
lineColor: edgeColor,
lineWidth: 1,
textPlacement: “vertex”
},
properties: {
altitude: altitude,
id: properties.id,
properties: properties
}
});
this.limitLines.push(outLine);
},
drawWall() {
const self = this
$.getJSON(“guizhou-border.json”, “”, function(borderMapData) {
const borderFeatures = borderMapData.features[0]
const properties = borderFeatures.properties;
const pathCoordinates = borderFeatures.geometry.coordinates.map(d => { return d[0] })
self.drawBorderLines(pathCoordinates, properties)
const limitLinesLayer = new maptalks.VectorLayer(
“vector-line”,
self.limitLines,
{
enableAltitude: true,
drawAltitude: {
polygonFill: edgeColor,
polygonOpacity: 0.3,
lineWidth: 0
}
}
).addTo(self.mapDom);
})
}
当然,mapshaper 的功效不止于此,简直是区域数据处理的一大利器,非常值得探索。另一个令人不太满意的是地图的底图。打开 mapbox,找到 Studio 然后 Start With Basic, 一个全新的自配地图的世界等待着你。这里,就随便先把英文的区县名换成中文好了。完成配置之后,点击 share,你会得到一个链接。不过,在用他替换掉 Map 的 urlTemplate 之前,还要按照格式进行一下修整。最后,就得到了本文开头所示的地图。相关源码