前沿资讯/我的第一个开源项目|结合 openlayers、ol-ext、kriging.js 绘制全国气温插值分析图
我的第一个开源项目|结合 openlayers、ol-ext、kriging.js 绘制全国气温插值分析图
我的第一个开源项目|结合 openlayers、ol-ext、kriging.js 绘制全国气温插值分析图

2025-10-24发布

前言

程序生成的插值分析图需要人工去制作,结合后端接口后可使用网页自动绘制插值分析图,网上搜索发现大多用 kriging.js 绘制,本文也使用 kriging.js 绘制。

一、kriging.js

kriging.js 是一个用于地统计插值的 JavaScript 库,它提供了多种插值方法,并且可以在 Web 浏览器中运行。

二、程序简介

1. 定义测试数据

代码如下(示例数据,随机生成的):

let testDataJson = [
	{"city":"江苏","latitude":"118.8062","longitude":"31.9208","value":"22"},
	{"city":"黑龙江","latitude":"127.9688","longitude":"45.368","value":"25"},
	{"city":"内蒙古","latitude":"110.3467","longitude":"41.4899","value":"26"},
	{"city":"吉林","latitude":"125.8154","longitude":"44.2584","value":"17"},
	{"city":"北京市","latitude":"116.4551","longitude":"40.2539","value":"18"},
	{"city":"辽宁","latitude":"123.1238","longitude":"42.1216","value":"23"},
	{"city":"河北","latitude":"114.4995","longitude":"38.1006","value":"24"},
	{"city":"天津","latitude":"117.4219","longitude":"39.4189","value":"28"},
	{"city":"山西","latitude":"112.3352","longitude":"37.9413","value":"32"},
	{"city":"陕西","latitude":"109.1162","longitude":"34.2004","value":"29"},
	{"city":"甘肃","latitude":"103.5901","longitude":"36.3043","value":"33"},
	{"city":"宁夏","latitude":"106.3586","longitude":"38.1775","value":"31"},
	{"city":"青海","latitude":"101.4038","longitude":"36.8207","value":"20"},
	{"city":"新疆","latitude":"87.9236","longitude":"43.5883","value":"22"},
	{"city":"四川","latitude":"103.9526","longitude":"30.7617","value":"22"},
	{"city":"重庆","latitude":"108.384366","longitude":"30.439702","value":"25"},
	{"city":"山东","latitude":"117.1582","longitude":"36.8701","value":"26"},
	{"city":"河南","latitude":"113.4668","longitude":"34.6234","value":"17"},
	{"city":"安徽","latitude":"117.29","longitude":"32.0581","value":"18"},
	{"city":"湖北","latitude":"114.3896","longitude":"30.6628","value":"23"},
	{"city":"浙江","latitude":"119.5313","longitude":"29.8773","value":"24"},
	{"city":"福建","latitude":"119.4543","longitude":"25.9222","value":"28"},
	{"city":"江西","latitude":"116.0046","longitude":"28.6633","value":"32"},
	{"city":"湖南","latitude":"113.0823","longitude":"28.2568","value":"29"},
	{"city":"贵州","latitude":"106.6992","longitude":"26.7682","value":"33"},
	{"city":"云南","latitude":"102.9199","longitude":"25.4663","value":"31"},
	{"city":"广东","latitude":"113.12244","longitude":"23.009505","value":"28"},
	{"city":"广西","latitude":"108.479","longitude":"23.1152","value":"32"},
	{"city":"海南","latitude":"110.3893","longitude":"19.8516","value":"29"},
	{"city":"上海","latitude":"121.4648","longitude":"31.2891","value":"33"},
	{"city":"西藏","latitude":"91.11","longitude":"29.97","value":"31"},
];

2. 定义 kriging 默认值

代码如下(示例数据,随机生成的):

let params = {
	mapCenter: [103.5901, 36.3043], // 地图中心点
// maxValue: 100,
	krigingModel: 'exponential', // 'exponential':指数模型  'gaussian':高斯模型,'spherical':球形模型
	krigingSigma2: 0,	// 方差参数,通常可设为0
	krigingAlpha: 100,	// 变差函数模型的先验,通常可设为100
	canvasAlpha: 1, // canvas插值图层透明度
	colors:[ // 颜色区域集合(插值分析会按照当前颜色集进行渲染)
"rgb(0,0,255)",
"rgb(54,97,255)",
"rgb(56,172,255)",
"rgb(0,255,255)",
"rgb(145,255,180)",
"rgb(210,255,105)",
"rgb(255,255,0)",
"rgb(255,183,0)",
"rgb(255,111,0)",
"rgb(255,0,0)"
	]
};

3. 绘制地图

代码如下(示例),当前使用天地图底图,需要单独申请key,或者可参考前一篇文章 openlayers显示指定区域,其它区域隐藏 使用高德底图影像:

let vec_c = getTdtLayer(ol, 'vec_w') // 默认
let img_c = getTdtLayer(ol, 'img_w') // 卫星影像
let cva_c = getTdtLayer(ol, 'cva_w') // 路线
let map = new ol.Map({
target: 'map',
layers: [img_c, getChina()],
view: new ol.View({
center: params.mapCenter,
projection: 'EPSG:4326',
zoom: 5
	})
});
// 添加各省市点位信息
let obj = { "type": "FeatureCollection", "features": [] };
for (let i = 0; i < testDataJson.length; i++) {
// 地图站点
	obj.features.push({
"type": "Feature",
"properties": {
"name": testDataJson[i].city,
"value": testDataJson[i].value
		},
"geometry": { "type": "Point", "coordinates": [testDataJson[i].longitude, testDataJson[i].latitude] }
	})
}
let myfeatures = (new ol.format.GeoJSON({
featureProjection: 'EPSG:4326',
})).readFeatures(obj);
let mySource = new ol.layer.Vector({  //(矢量图层)
	source: new ol.source.Vector({
features: myfeatures
	}),
	style: function (feature, resolution) {
return new ol.style.Style({
text: new ol.style.Text({
font: 'bold 12px "Open Sans", "Arial Unicode MS", "sans-serif"',
offsetX: 20,
offsetY: 20,
text: feature.get('name') + '\n' + feature.get('value') + '℃',
textAlign: 'left',
fill: new ol.style.Fill({
color: '
#fff
'
				})
			})
		})
	}
})
mySource.setZIndex(1);
map.addLayer(mySource);
function getTdtLayer(ol, lyr) {
	let key = 'key';
	let url = 'http://t{0-7}.tianditu.com/DataServer?T=' + lyr + '&X={x}&Y={y}&L={z}&tk=' + key
	let layer = new ol.layer.Tile({
source: new ol.source.XYZ({
url: url
		})
	})
return layer
}
function getChina() {
return new ol.layer.Vector({
source: new ol.source.Vector({
url: "https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json",
format: new ol.format.GeoJSON(),
		}),
style: function (feature) {
return new ol.style.Style({
zIndex: 0,
stroke: new ol.style.Stroke({
color: "
#6FE9FF
",
width: 2
				}),
text: new ol.style.Text({
font: 'normal 12px "Open Sans", "Arial Unicode MS", "sans-serif"',
text: feature.get('name'),
textAlign: 'left',
fill: new ol.style.Fill({
color: '
#6FE9FF
'
					})
				}),
			})
		},
	})
}

4. 加载 kriging 绘制区域及剪切底图

$.getJSON('https://geo.datav.aliyun.com/areas_v3/bound/100000.json', function(data){
		let coord = data.features[0].geometry.coordinates[0];
drawKriging(coord);
// 屏蔽除了加载的中国地图外的其他区域(默认裁切大的地图,其它区域可以使用concat拼接,或者修改上述底图链接,可生成省级、市级数据示例)
var coords = data['features'][0]['geometry']['coordinates'];
var f = new ol.Feature(new ol.geom.MultiPolygon(coords));
var crop = new ol.filter.Crop({
feature: f,
wrapX: true,
inner: false
		});
		img_c.addFilter(crop);
var mask = new ol.filter.Mask({
feature: f,
wrapX: true,
inner: false,
fill: new ol.style.Fill({
color: [8, 20, 36, 0.8]
			})
		});
		img_c.addFilter(mask);
	})

5. 绘制插值分析

const drawKriging = (coord) => {
let kriging = test.kriging;
let values = [],
		lngs = [],
		lats = [];
for (let i = 0; i < testDataJson.length; i++) {
		values.push(testDataJson[i].value)
		lngs.push(testDataJson[i].longitude)
		lats.push(testDataJson[i].latitude)
	}
// 使用kriging绘制插值分析起码得三个点位数据
if (values.length >= 3){
let variogram = kriging.train(
			values,	// 样本值数组(如每个站点的降雨量)
			lngs,	// 样本点的经度坐标数组
			lats,	// 样本点的纬度坐标数组
params.krigingModel,	// 变差函数模型,可选 'gaussian'(高斯模型)、'exponential'(指数模型)或 'spherical'(球形模型)
params.krigingSigma2,	// 高斯过程的方差参数,通常可设为 0
params.krigingAlpha	//变差函数模型的先验,通常可设为 100
		);
let grid = kriging.grid(
			coord,	// 定义插值区域的边界多边形坐标数组
			variogram,	// 训练好的变差函数模型
0.1// 每个栅格单元的宽度(度),值越小,生成的分辨率越高;数值越小,轮廓越细腻,但是占浏览器内存越大(很容易崩溃)
		);
//移除已有图层
if (canvasLayer !== null){
			map.removeLayer(canvasLayer);
		}
//创建新图层
		canvasLayer=new ol.layer.Image({
			source: new ol.source.ImageCanvas({
				canvasFunction:(extent, resolution, pixelRatio, size, projection) =>{
let canvas = document.createElement('canvas');
					canvas.width = size[0];
					canvas.height = size[1];
					canvas.style.display = 'block';
//设置canvas透明度
					canvas.getContext('2d').globalAlpha = params.canvasAlpha;                          
//使用分层设色渲染
					kriging.plot(
						canvas,
						grid,
						[extent[0],extent[2]],
						[extent[1],extent[3]],
params.colors
					);
return canvas;
				},
				projection: 'EPSG:4326'
			})
		})
//向map添加图层
		map.addLayer(canvasLayer);
	}else {
		alert("有效样点个数不足,无法插值");
	}
}

三、完整程序

完整网页代码如下,可直接运行:
<!DOCTYPE html>
<html>
<head>
    <title>结合openlayers、ol-ext、kriging.js绘制全国气温插值分析图</title>
	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v10.6.0/ol.css">
	<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js"></script>
	<script src="https://cdn.jsdelivr.net/npm/ol@v10.6.0/dist/ol.js"></script>
	<script src="https://cdn.jsdelivr.net/npm/ol-ext@4.0.35/dist/ol-ext.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/kriging@0.1.12/dist/kriging.min.js"></script>
	<style>
		* {
			margin: 0;
			padding: 0;
		}


#map
 {
			width: 100vw;
			height: 100vh;
		}
	</style>
</head>
<body>
    <div id="map" class="map"></div>
    <script>
		$(function() {
let params = {
				mapCenter: [103.5901, 36.3043],
				// maxValue: 100,
				krigingModel: 'exponential', // 'exponential':指数  'gaussian':高斯,'spherical':球体
				krigingSigma2: 0,
				krigingAlpha: 100,
				canvasAlpha: 1, //canvas图层透明度
				colors:[
"rgb(0,0,255)",
"rgb(54,97,255)",
"rgb(56,172,255)",
"rgb(0,255,255)",
"rgb(145,255,180)",
"rgb(210,255,105)",
"rgb(255,255,0)",
"rgb(255,183,0)",
"rgb(255,111,0)",
"rgb(255,0,0)"
				]
			};
let testDataJson = [
				{"city":"江苏","longitude":"118.8062","latitude":"31.9208","value":"22"},
				{"city":"黑龙江","longitude":"127.9688","latitude":"45.368","value":"25"},
				{"city":"内蒙古","longitude":"110.3467","latitude":"41.4899","value":"26"},
				{"city":"吉林","longitude":"125.8154","latitude":"44.2584","value":"17"},
				{"city":"北京市","longitude":"116.4551","latitude":"40.2539","value":"18"},
				{"city":"辽宁","longitude":"123.1238","latitude":"42.1216","value":"23"},
				{"city":"河北","longitude":"114.4995","latitude":"38.1006","value":"24"},
				{"city":"天津","longitude":"117.4219","latitude":"39.4189","value":"28"},
				{"city":"山西","longitude":"112.3352","latitude":"37.9413","value":"32"},
				{"city":"陕西","longitude":"109.1162","latitude":"34.2004","value":"29"},
				{"city":"甘肃","longitude":"103.5901","latitude":"36.3043","value":"33"},
				{"city":"宁夏","longitude":"106.3586","latitude":"38.1775","value":"31"},
				{"city":"青海","longitude":"101.4038","latitude":"36.8207","value":"20"},
				{"city":"新疆","longitude":"87.9236","latitude":"43.5883","value":"22"},
				{"city":"四川","longitude":"103.9526","latitude":"30.7617","value":"22"},
				{"city":"重庆","longitude":"108.384366","latitude":"30.439702","value":"25"},
				{"city":"山东","longitude":"117.1582","latitude":"36.8701","value":"26"},
				{"city":"河南","longitude":"113.4668","latitude":"34.6234","value":"17"},
				{"city":"安徽","longitude":"117.29","latitude":"32.0581","value":"18"},
				{"city":"湖北","longitude":"114.3896","latitude":"30.6628","value":"23"},
				{"city":"浙江","longitude":"119.5313","latitude":"29.8773","value":"24"},
				{"city":"福建","longitude":"119.4543","latitude":"25.9222","value":"28"},
				{"city":"江西","longitude":"116.0046","latitude":"28.6633","value":"32"},
				{"city":"湖南","longitude":"113.0823","latitude":"28.2568","value":"29"},
				{"city":"贵州","longitude":"106.6992","latitude":"26.7682","value":"33"},
				{"city":"云南","longitude":"102.9199","latitude":"25.4663","value":"31"},
				{"city":"广东","longitude":"113.12244","latitude":"23.009505","value":"28"},
				{"city":"广西","longitude":"108.479","latitude":"23.1152","value":"32"},
				{"city":"海南","longitude":"110.3893","latitude":"19.8516","value":"29"},
				{"city":"上海","longitude":"121.4648","latitude":"31.2891","value":"33"},
				{"city":"西藏","longitude":"91.11","latitude":"29.97","value":"31"},
			];
let vec_c = getTdtLayer(ol, 'vec_w') // 默认
let img_c = getTdtLayer(ol, 'img_w') // 卫星影像
let cva_c = getTdtLayer(ol, 'cva_w') // 路线
			// 绘制地图
let map = new ol.Map({
				target: 'map',
				layers: [img_c, getChina()],
				view: new ol.View({
					center: params.mapCenter,
					projection: 'EPSG:4326',
					zoom: 5
				})
			});
			// 添加各省市点位信息
let obj = { "type": "FeatureCollection", "features": [] };
for (let i = 0; i < testDataJson.length; i++) {
				// 地图站点
				obj.features.push({
"type": "Feature",
"properties": {
"name": testDataJson[i].city,
"value": testDataJson[i].value
					},
"geometry": { "type": "Point", "coordinates": [testDataJson[i].longitude, testDataJson[i].latitude] }
				})
			}
let myfeatures = (new ol.format.GeoJSON({
				featureProjection: 'EPSG:4326',
			})).readFeatures(obj);
let mySource = new ol.layer.Vector({  //(矢量图层)
source: new ol.source.Vector({
					features: myfeatures
				}),
				style: function (feature, resolution) {
return new ol.style.Style({
						text: new ol.style.Text({
							font: 'bold 12px "Open Sans", "Arial Unicode MS", "sans-serif"',
							offsetX: 20,
							offsetY: 20,
							text: feature.get('name') + '\n' + feature.get('value') + '℃',
							textAlign: 'left',
							fill: new ol.style.Fill({
								color: '
#fff
'
							})
						}),
						// image: new ol.style.Icon(({
						// 	anchor: [0.5, 0],
						// 	anchorXUnits: 'fraction',
						// 	anchorYUnits: 'pixels',
						// 	src: './yun.png',
						// 	scale: .5
						// }))
					})
				}
			})
			mySource.setZIndex(1);
			map.addLayer(mySource);
			// 绘制kriging插值图
let canvasLayer = null;
			const drawKriging = (coord) => {
let kriging = test.kriging;
				// testDataJson
let values = [],
					lngs = [],
					lats = [];
for (let i = 0; i < testDataJson.length; i++) {
					values.push(testDataJson[i].value)
					lngs.push(testDataJson[i].longitude)
					lats.push(testDataJson[i].latitude)
				}
				// 使用kriging绘制插值分析起码得三个点位数据
if (values.length >= 3){
let variogram = kriging.train(
						values,	// 样本值数组(如每个站点的降雨量)
						lngs,	// 样本点的经度坐标数组
						lats,	// 样本点的纬度坐标数组
						params.krigingModel,	// 变差函数模型,可选 'gaussian'(高斯模型)、'exponential'(指数模型)或 'spherical'(球形模型)
						params.krigingSigma2,	// 高斯过程的方差参数,通常可设为 0
						params.krigingAlpha	//变差函数模型的先验,通常可设为 100
					);
let grid = kriging.grid(
						coord,	// 定义插值区域的边界多边形坐标数组
						variogram,	// 训练好的变差函数模型
						0.1	// 每个栅格单元的宽度(度),值越小,生成的分辨率越高;数值越小,轮廓越细腻,但是占浏览器内存越大(很容易崩溃)
					);
					//移除已有图层
if (canvasLayer !== null){
						map.removeLayer(canvasLayer);
					}
					//创建新图层
					canvasLayer=new ol.layer.Image({
source: new ol.source.ImageCanvas({
							canvasFunction:(extent, resolution, pixelRatio, size, projection) =>{
let canvas = document.createElement('canvas');
								canvas.width = size[0];
								canvas.height = size[1];
								canvas.style.display = 'block';
								//设置canvas透明度
								canvas.getContext('2d').globalAlpha = params.canvasAlpha;                          
								//使用分层设色渲染
								kriging.plot(
									canvas,
									grid,
									[extent[0],extent[2]],
									[extent[1],extent[3]],
									params.colors
								);
return canvas;
							},
							projection: 'EPSG:4326'
						})
					})
					//向map添加图层
					map.addLayer(canvasLayer);
				}else {
					alert("有效样点个数不足,无法插值");
				}
			}
			// 绘制插值图、添加遮罩
			$.getJSON('https://geo.datav.aliyun.com/areas_v3/bound/100000.json', function(data){
let coord = data.features[0].geometry.coordinates[0];
				drawKriging(coord);
				// 屏蔽除了加载的中国地图外的其他区域
				var coords = data['features'][0]['geometry']['coordinates'];
				var f = new ol.Feature(new ol.geom.MultiPolygon(coords));
				var crop = new ol.filter.Crop({
					feature: f,
					wrapX: true,
					inner: false
				});
				img_c.addFilter(crop);
				var mask = new ol.filter.Mask({
					feature: f,
					wrapX: true,
					inner: false,
					fill: new ol.style.Fill({
						color: [8, 20, 36, 0.8]
					})
				});
				img_c.addFilter(mask);
			})
		})
function getTdtLayer(ol, lyr) {
let key = 'key';
let url = 'http://t{0-7}.tianditu.com/DataServer?T=' + lyr + '&X={x}&Y={y}&L={z}&tk=' + key
let layer = new ol.layer.Tile({
source: new ol.source.XYZ({
					url: url
				})
			})
return layer
		}
function getChina() {
return new ol.layer.Vector({
source: new ol.source.Vector({
					url: "https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json",
					format: new ol.format.GeoJSON(),
				}),
				style: function (feature) {
return new ol.style.Style({
						zIndex: 0,
						stroke: new ol.style.Stroke({
							color: "
#6FE9FF
",
							width: 2
						}),
						text: new ol.style.Text({
							font: 'normal 12px "Open Sans", "Arial Unicode MS", "sans-serif"',
							text: feature.get('name'),
							textAlign: 'left',
							fill: new ol.style.Fill({
								color: '
#6FE9FF
'
							})
						}),
					})
				},
			})
		}
	</script>
</body>
</html>

四、绘制效果

image.png

?问题

跪求大神指导🙋

  1. 使用 kriging.js 绘制时,点位数据不能多余 500 个,数据量太多会报错且绘制不出来效果,怎么实现类似 arcmap 程序生成的多数据插值分析?
  2. 使用 kriging.js 绘制时,如何实现类似 arcmap 程序按照图例数据级颜色级别进行渲染?
  3. 使用 kriging.js 绘制时,如何保障绘制准确性,用上述相同数据及 10 层颜色使用 arcmap 进行克里金及反距离权重法生成的图结果完全不对等?

image.png

image.png


📫 项目地址:https://gitcode.com/weixin_43928238/GIS