高质量编码-克里金插值地图可视化(后台缓存优化)

2021-08-20 18:14:06 浏览数 (1)

前面在使用kriging.js进行克里金插值可视化时,计算分为三个步骤:

  • 模型训练(kriging.train)
  • 网格生成(kriging.grid)
  • 绘制结果(kriging.plot)

实际在web页面中根据真实数据即时运行上面三个步骤的计算,因为模型训练涉及到的数学计算量很大,可能需要很长时间才能得到结果,前端javascript耗时统计如下:

上面三个步骤的耗时大小取决于模型选择,原始数据,以及网格分辨率等参数,查看源代码可以看到各个步骤使用的参数。

  • 模型训练(kriging.train)

  • 网格生成(kriging.grid)

  • 绘制结果(kriging.plot)

现实需求中需要使用真实数据生成一个或多个热力图canvas,既然每次从计算到生成图片耗时很久,可不可以按照需求将每组数据计算生成的图片缓存到后台,下次使用直接从后台获取,这样页面上便能更快的看到热力图?答案是肯定的。

这里将某个时间对应的数据第一次克里金插值计算绘制生成的多个canvas的base64编码以及各自对应的经纬度范围信息保存到后台,下次再次需要绘制这个时间对应数据的热力图,直接从后台获取叠加到地图上。怎么判断是否是第一次呢,JavaScript代码逻辑首先从后台询问某个日期对应的热力图是否已经存在,如果存在就直接获取,如果不存在前端JavaScript开始计算生成,同时生成后保存到后台方便下次使用。

将热力图保存到后台将热力图保存到后台
从后台获取热力图数据从后台获取热力图数据

我们看一下前端JavaScript实现:

再来看一下后台实现:

前端代码如下:

代码语言:html复制
<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <style type="text/css">
        html,
        body,
        #allmap {
            width: 100%;
            height: 100%;
            padding: 0;
            margin: 0;
            overflow: hidden;
        }

        #allmap img {
            height: 100%;
        }

    </style>

    <script type="text/javascript" src="https://underscorejs.net/js/jquery-1.11.0.min.js"></script>
    <script type="text/javascript" src="https://underscorejs.net/js/underscore.js"></script>
    <script type="text/javascript" src="static/lib/kriging.js"></script>

    <script type="text/javascript" src="static/js/china.js"></script>
    <script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=wWy2A8K94nhntYTYUHS19RXW"></script>
    <title>空气质量空间插值可视化</title>

</head>

<body>
    <div id="allmap"></div>

    <script type="text/javascript">
        // 百度地图API功能
        var map = new BMap.Map("allmap");
        map.centerAndZoom(new BMap.Point(116.403765, 39.914850), 5);
        map.enableScrollWheelZoom();

        function loadMarkers(points) {

            map.clearOverlays();
            $.each(points, (i, x) => {
                //console.log(x);
                var position = new BMap.Point(x['经度'], x['纬度']);
                var marker1 = new BMap.Marker(position);
                dictMarker[x['编号']] = marker1;
                map.addOverlay(marker1);
                marker1.addEventListener("click", function() {

                    var attrs = _.omit(x, ['编号', '经度', '纬度','value2']);
                    var content = _.map(attrs, (x, i) => `${i}:${x}`).join('<br>');
                    var infoWindow = new BMap.InfoWindow(content);
                    marker1.openInfoWindow(infoWindow);

                });

            })

        }
        dictMarker = {};
        $.get('api/stations', function(data) {

            data = JSON.parse(data['result']);
            window.points = data;

            dictPoints = _.indexBy(points, '编号');
            //loadMarkers(data);
            refreshCanvas();
        })

        function refreshCanvas() {
            var date = location.search.match(/date=([^&]*)/i);
            var hour = location.search.match(/hour=([^&]*)/i);
            if (!!date) {
                date = date[1];
            } else {
                date = "20210610";
            }
            if (!!hour) {
                hour = hour[1];
            } else {
                hour = "0";
            }
            var type = "PM2.5";
			window.params={date: date,hour: hour, type: type};
			
			$.post('krigingMap',params,function(data){console.log(data);
			if(data['result']==0){
			$.post('api/stations', params, function(data) {

                data = JSON.parse(data['result']);
                var airData = _.find(data, {
                    hour: parseInt(hour),
                    type: type
                });

                airData = _.omit(airData, ['date', 'hour', 'type']);

                var trainData = _.compact(_.map(airData, function(item, index) {
                    if (!item) {
                        return null;
                    }
                    result = _.result(dictPoints, index, null);
                    if (!!result) {
                        result['value']=item;
                        result['value2'] = _.sortedIndex(standards, item);
                    }
                    return result;
                }));
                window.data1 = trainData;
                window.canvas = getCanvas(trainData);
            })
			
			}
			else{
			var imgs=$.parseJSON(data['result']);
			imgs.forEach(function(item){
	 
			addOverlay([item['minX'],item['minY']],[item['maxX'],item['maxY']],item['data']);
			})
			}
			 
			});
			
            
        }
        var [width, height] = [10, 5];
        var [offsetX, offsetY] = [10, 5];
        let canvas = null; //画布
        let colors = ["green", "#82D827", "yellow", "#FFDF00", "orange", "#FF5E00",
            "red", "#B71850", "purple", "#952E81","brown"];
        var standards = [25, 50, 75, 100, 125, 150, 175, 200, 250, 300];

        function getCanvas(data) {
            var values = _.pluck(data, 'value2');
            var lngs = _.pluck(data, '经度');
            var lats = _.pluck(data, '纬度');
            var indexes = _.unique(values);
            var colors0 = colors.slice(_.min(indexes), _.max(indexes)   1);
       
            var [minX, minY, maxX, maxY] = [_.min(lngs), _.min(lats), _.max(lngs), _.max(lats)];
            var bounds = [
                [
                    [minX, minY],
                    [minX, maxY],
                    [maxX, maxY],
                    [maxX, minY]
                ]
            ];
            console.log(bounds);
            bounds = _.flatten(china.geometry.coordinates,true);
            console.time();
            window.variogram = kriging.train(values, lngs, lats, 'exponential', 0, 100);
            console.timeEnd();
            console.time();
            window.grid = kriging.grid(bounds, variogram, (maxY - minY) / 500);
            console.timeEnd();
            console.time();

            var lngs = _.range(minX - offsetX, maxX   offsetX, width);
            var lats = _.range(minY - offsetY, maxY   offsetY, height);
			window.backupImgs=[];
            _.map(lngs, function(m) {
                canvas = document.createElement('canvas');
                canvas.width = width * 5;
                canvas.height = height * 5;
                canvas.style.display = 'block';
                canvas.getContext('2d').globalAlpha = 0.75;

                [minX, maxX] = [m, m   width]
				
                _.map(lats, function(n) {
                    [minY, maxY] = [n, n   height]

                    kriging.plot(canvas, grid, [minX, maxX], [minY, maxY], colors0);
					canvasImg=	canvas.toDataURL();
					backupImgs.push({'minX':minX,'minY':minY,'maxX':maxX,'maxY':maxY,'data':canvasImg});
                    addOverlay([minX, minY], [maxX, maxY], canvasImg);
					
                })
            })
			console.timeEnd();
			
			$.post('backupImgs',$.extend({'imgs':JSON.stringify(backupImgs)},params),function(data){
			if(data['result']==1){
			console.info('success');
			}
			});
            

        }

        function addOverlay(west_south, east_north, canvasImg) {
            var west_south = new BMap.Point(west_south[0], west_south[1]);
            var east_north = new BMap.Point(east_north[0], east_north[1]);
            var bounds = new BMap.Bounds(west_south, east_north);
            var canvasOverlay = new BMap.GroundOverlay(bounds, {

                imageURL: canvasImg,
                opacity: 0.6
            });
            map.addOverlay(canvasOverlay);
        }

    </script>
</body>


</html>

后台代码(使用Python Web框架Tornado实现)如下:

代码语言:python代码运行次数:0复制
class backupImgsHandler(tornado.web.RequestHandler):
    def post(self):
        imgs=self.get_argument('imgs')
        date=self.get_argument('date')
        hour=self.get_argument('hour')
        _type=self.get_argument('type')
        imgsFile=os.path.join('static/backupImgs',f'{date}-{hour}-{_type}')
        with open(imgsFile,'w') as f:
            f.write(imgs)
            
        print(imgs)
        self.write({'result':1})

class krigingMapHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('krigingMap.html')

    def post(self):
        date=self.get_argument('date')
        hour=self.get_argument('hour')
        _type=self.get_argument('type')
        imgsFile=os.path.join('static/backupImgs',f'{date}-{hour}-{_type}')
        if os.path.exists(imgsFile):
            with open(imgsFile) as f:
                result=f.read()
            self.write({'result':result})
        else:
            self.write({'result':0})

0 人点赞