(1)主要过程:结合Kriging.js 实现 Mapboxgl 上的插值图
(2)效果:
(3)代码
HTML 、CSS:
<head>
<meta charset="UTF-8" />
<title>Mapboxgl(canvas) + kriging.js</title>
<style>
html,
body,
#map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<link rel="stylesheet" href="../lib/js/geoglobe/mapbox-gl.css" type="text/css" />
<script type="text/javascript" src="../lib/js/geoglobe/mapbox-gl.js"></script>
<script type="text/javascript" src="../lib/js/turf/turf_new.min.js"></script>
<script type="text/javascript" src="../lib/js/interpolation/kriging_noModule.js"></script>
<!-- 行政区划数据 (为了偷懒使用了js的形式) -->
<script src="./data/wuhanShi.js"></script>
<body>
<!-- 用于绘制插值图的canvas -->
<canvas id="canvasMap" style="display: none; opacity: 0.2"></canvas>
<div id="map"></div>
<script src="./js/mapboxKriging.js"></script>
</body>
</head>
完整 JS:
// 颜色范围 (自定义)
const colors = [
'rgba(0,0,0,0)',
'rgb( 82 , 184 , 70 )',
'rgb(90,187,68)',
'rgb(98,189,66)',
'rgb(106,192,63)',
'rgb(114,194,61)',
'rgb(122,197,59)',
'rgb(130,199,57)',
'rgb(138,202,55)',
'rgb(146,204,52)',
'rgb(154,207,50)',
'rgb(162,209,48)',
'rgb(171,212,46)',
'rgb(179,215,44)',
'rgb(187,217,41)',
'rgb(195,220,39)',
'rgb(203,222,37)',
'rgb(211,225,35)',
'rgb(219,227,33)',
'rgb(227,230,30)',
'rgb(235,232,28)',
'rgb( 243 , 235 , 26 )',
'rgb(243,230,26)',
'rgb(243,224,27)',
'rgb(243,219,27)',
'rgb(244,213,28)',
'rgb(244,208,28)',
'rgb(244,203,29)',
'rgb(244,197,29)',
'rgb(244,192,30)',
'rgb(244,186,30)',
'rgb(244,181,30)',
'rgb(245,176,31)',
'rgb(245,170,31)',
'rgb(245,165,32)',
'rgb(245,159,32)',
'rgb(245,154,33)',
'rgb(245,149,33)',
'rgb(246,143,34)',
'rgb(246,138,34)',
'rgb(246,132,35)',
];
let canvasImg, // 用于插值的画布
map, // 地图实例
points, // 插值点 通过随机数产生;
range; // 插值范围
// 插值范围 外边界
// 可以使用 turf.js 库,获取行政区划的bbox,即为外边界
let xlim = [113.70228083999996, 115.08257304000006];
let ylim = [29.96907695999994, 31.36125996000004];
// 用于将canvas导出的图片放置在地图上合适位置
let coordinates = [
[xlim[0], ylim[1]],
[xlim[0], ylim[0]],
[xlim[1], ylim[0]],
[xlim[1], ylim[1]],
];
// 地图初始化
function init() {
map = new mapboxgl.Map({
container: 'map',
style: {
version: 8,
sources: {
cartodb: {
tiles: ['http://b.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png'],
tileSize: 256,
type: 'raster',
},
},
layers: [
{
id: 'cartodb',
type: 'raster',
source: 'cartodb',
},
],
},
center: [114.44515001943142, 30.649477958235423],
zoom: 8.3,
pitch: 0,
});
// 地图加载
map.on('load', () => {
createPoints();
addStateLayer();
cavasLayer();
});
}
// 添加边界图层
function addStateLayer() {
let wuhanFeatures = [];
wuhanShi.features.forEach((element) => {
wuhanFeatures.push({
type: 'Feature',
properties: element.attributes,
geometry: {
type: 'Polygon',
coordinates: element.geometry.rings,
},
});
});
map.addSource('wuhan', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: wuhanFeatures,
},
});
map.addLayer({
id: 'wuhan',
source: 'wuhan',
type: 'line',
paint: {
'line-color': 'black',
'line-width': 2,
},
});
// 插值范围
range = wuhanFeatures[0].geometry.coordinates;
}
// 创建用于插值的随机数据点
function createPoints() {
var randomPoints = turf.randomPoint(25, {
bbox: [113.70228083999996, 29.96907695999994, 115.08257304000006, 31.36125996000004],
});
randomPoints.features.forEach((item, index) => {
item.properties = {
z: Math.round(Math.random() * 100),
id: index,
};
});
// console.log(randomPoints); // 打印randomPoints查看插值点数据
// 插值点图层
map.addSource('point', {
type: 'geojson',
data: randomPoints,
});
map.addLayer({
id: 'point',
source: 'point',
type: 'circle',
paint: {
'circle-color': 'black',
},
});
// 插值点数据
points = randomPoints.features;
}
//进行克里金插值
function loadkriging() {
var canvas = document.getElementById('canvasMap');
canvas.width = 1000;
canvas.height = 1000;
var n = points.length;
var t = []; // 数值
var x = []; // 经度
var y = []; // 纬度
for (var i = 0; i < n; i++) {
t.push(points[i].properties.z);
x.push(points[i].geometry.coordinates[0]);
y.push(points[i].geometry.coordinates[1]);
}
//对数据集进行训练
var variogram = kriging.train(t, x, y, 'exponential', 0, 100);
//使用variogram对象使polygons描述的地理位置内的格网元素具备不一样的预测值,最后一个参数,是插值格点精度大小
var grid = kriging.grid(range, variogram, (ylim[1] - ylim[0]) / 250);
//将得到的格网grid渲染至canvas上
kriging.plot(canvas, grid, [xlim[0], xlim[1]], [ylim[0], ylim[1]], colors);
// 将插值结果进行处理,转换为图片格式以便作为图层接入mapboxgl地图中
return drawImage(0, 0, 0.5);
}
// canvas图片处理
function drawImage(x, y, alpha) {
// 获取canvas元素对应的DOM对象
var canvas = document.getElementById('canvasMap');
// 获取在canvas上绘图的CanvasRenderingContext2D对象
var ctx = canvas.getContext('2d');
// 绘制图片
// ctx.drawImage(image, x, y);
// 获取从x、y开始,宽为image.width、高为image.height的图片数据
// 也就是获取绘制的图片数据
var imgData = ctx.getImageData(x, y, canvas.width, canvas.height);
for (var i = 0, len = imgData.data.length; i < len; i += 4) {
// 改变每个像素的透明度
imgData.data[i + 3] = imgData.data[i + 3] * alpha;
}
// 将获取的图片数据放回去。
ctx.putImageData(imgData, x, y);
return canvas.toDataURL('image/png');
}
// 添加一个raster图层到地图中,使用的数据源是canvas转换为的图片形式(插值结果)
function cavasLayer() {
map.addSource('kriging', {
type: 'image',
url: loadkriging(),
coordinates: [coordinates[0], coordinates[3], coordinates[2], coordinates[1]],
});
map.addLayer({
id: 'kriging',
type: 'raster',
source: 'kriging',
paint: {
'raster-opacity': 1,
},
});
}
init();
我觉得应该不能再详细了...
(4)还存在的问题:目前够用。如有问题请联系我改正!