栅格瓦片转mongodb离线部署

栅格瓦片转mongodb离线部署

前提

​ 上一篇博客中讲到了如何将TMS标准的瓦片转换成mbtiles格式发布服务,但是在现实环境中,使用的切图工具或者切图的人并没有按照标准切图。有的人切片为ArcServer切片或者GeoWebCache切片。这样的话上篇博客的方法就不能适用了。那么本篇博客就讲述上篇解决不了的切片。

实现思路

​ 使用NoSQL(这里指Mongodb)对瓦片的png图片进行入库存储,通过x,y,z来关联对应的图片,然后通过Nodejs查询得到图片的二进制写到页面中。完成展示瓦片的功能

准备工作

  1. 切好的瓦片(我这里是ArcServer切片)
  2. Python(我使用的版本:3.6.2)
  3. NodeJS(我使用的版本:8.11.3)
  4. Mongodb(我使用的版本:4.0.4)

工具安装

这里的Python,NodeJS,Mongodb 可以按照网上的教程安装,这里我就不再赘述了

步骤

  1. 下载栅格瓦片地图
    栅格瓦片转mongodb离线部署

    栅格瓦片转mongodb离线部署

  2. 编写脚本,将瓦片存入Mongodb库中,这里基于瓦片的图都比较小,就以二进制存到库中,不使用GridFS

    下面的脚本只是我自己要用的,不能满足所有情况,如果自己有特殊需求,可以对代码进行修改。大概思路就是:

    1. 循环遍历切片文件夹中的所有切片
    2. 将读取到的切片转成二进制并存储到库中
from pymongo import MongoClient
import os
from bson import binary


class file2db(object):
    def __init__(self, database, setname, level):
        mongodb_url = 'localhost'
        mongodb_port = 27017
        conn = MongoClient(mongodb_url, mongodb_port)
        db = conn[database]
        self.my_set = db[setname]
        self.max_level = level

    def read_png_file(self, root_path):
        # 遍历文件形成dict
        # D:\Tomcat 8.5\webapps\Layers\_alllayers
        _files = []
        if os.path.exists(root_path):
            list = os.listdir(root_path)
            for i in range(0, len(list)):
                path = os.path.join(root_path, list[i])
                if os.path.isdir(path):
                    _files.extend(self.read_png_file(path))
                if os.path.isfile(path):
                    _files.extend(path)
                    # get column
                    file_name = os.path.basename(path).split('.')[0]
                    file_type = os.path.basename(path).split('.')[1]
                    dir_path = os.path.dirname(path).split('\\')
                    # get row
                    row_name = dir_path[len(dir_path) - 1]
                    # get zoom level
                    zoom_name = dir_path[len(dir_path) - 2]
                    # insert mongodb
                    if file_type == 'png':
                        self.insert_file(zoom_name, row_name, file_name, path)
            return _files
        else:
            return None

    def insert_file(self, zoom, row, column, path):
        # 将data 存到mongo中
        level = zoom[1:]
        if int(level) <= self.max_level:
            png_list = self.my_set.find({"x": row, "y": column, "z": zoom})
            if png_list.count() == 0:
                print('zoom:%s row:%s fileName:%s path:%s ' % (zoom, row, column, path))
                file = open(path, mode='rb')
                content = binary.Binary(file.read())
                self.my_set.insert({"x": row, "y": column, "z": zoom, "img": content})
                file.close()
            else:
                print('this file is exist')
        else:
            print('this zoom greater than max level')


if __name__ == '__main__':
    file2db = file2db('png', 'layer', 13)
    file2db.read_png_file('D:\Tomcat 8.5\webapps\Layers\_alllayers')

执行完毕后库中的数据

栅格瓦片转mongodb离线部署

  1. 给数据创建索引
png.layer.createIndex({x:1,y:1,z:1})
--创建完成后可以用explain看看有没有使用索引
  1. 编写node脚本,发布服务

    这里只是简单的写了一下,node我也是边用边学的,如果已经是对node写服务端很熟悉的大佬这段就可以过滤掉了。如果跟我一样也是刚接触的话可以看一下。具体思路:

    1. 连接mongo,查询数据
    2. 写一个接口用于前端调用得到瓦片图

    这里node需要安装的组件有:mongoose,express

新建一个node项目如下:

栅格瓦片转mongodb离线部署

db.js内容:

'use strict';
var mongoose = require('mongoose'),
    DB_URL = 'mongodb://localhost:27017/png';

/**
* 连接
*/
mongoose.connect(DB_URL);

/**
* 连接成功
*/
mongoose.connection.on('connected', function () {    
    console.log('Mongoose connection open to ' + DB_URL);  
});    

/**
* 连接异常
*/
mongoose.connection.on('error',function (err) {    
    console.log('Mongoose connection error: ' + err);  
});    

/**
* 连接断开
*/
mongoose.connection.on('disconnected', function () {    
    console.log('Mongoose connection disconnected');  
});    

module.exports = mongoose;

layer.js内容:

'use strict';
var mongoose = require('./db'),
    Schema = mongoose.Schema;

var GGSchema = new Schema({          
    x : { type: String },                    //行
    y: {type: String},                        //列
    z: {type: String},                        //层级
    img : { type: Buffer},                       //图片二进制
});

module.exports = mongoose.model('layer',GGSchema,'layer');

map-server.js内容:

'use strict';
var express = require('express');
var mylayer = require("./layer");
var app = express();

// 获取图片
app.get('/getimage/',function(req, res) {
    var x = req.query.x
    var y = req.query.y
    var z = req.query.z
    console.log(req.originalUrl)
    var whereStr = {"x":x,"y":y,"z":z};
    mylayer.findOne(whereStr,function(err, doc){
        if(err){
            console.log("Error: "+err)
        }else{
            let img = null 
            if(doc){
                if(doc.img){
                    img = doc.img
                }
            }
            if(img){
                res.writeHead('200', {'Content-Type': 'image/png'});    //写http头部信息
                res.end(img,'binary'); 
            }else{
                res.status(404);
                res.end()
            }

        }
    })

})
var server = app.listen(8081, function () {

    var host = server.address().address
    var port = server.address().port

    console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

  1. 使用OL预览效果

    这里我的切片是ArcgisServer切片,如果是其他切片(geowebcache等)只要更改加载算法就行

//初始化XYZsource
var source  = new ol.source.XYZ({
    tileUrlFunction : function (xyz, obj1, obj2) {
        var z = xyz[0];
        var x = Math.abs(xyz[1]);
        var y = Math.abs(xyz[2]) - 1;
        var x = 'C' + padLeft(8, x.toString(16)).toUpperCase();
        var y = 'R' + padLeft(8, y.toString(16)).toUpperCase();
        var z = 'L' + padLeft(2, z);
        var url = 'http://localhost:8081/getimage?x=' + y + '&y=' + x + '&z=' + z ;
        return url;
    }
});
      
//ArcgisServer切片加载算法
function padLeft(num, val) {
    return (new Array(num).join('0') + val).slice(-num);
}

最后的效果:

栅格瓦片转mongodb离线部署

总结

  1. 如果切片比较多,导入数据库的速度会受影响,可以考虑换成多线程进行处理
  2. 上面的效果是在我自己的开发机器上展现的。单机情况下,这个速度还是可以的。感觉做集群部署将请求进行分发,速度应该还能提高。

最后

以上的代码和脚本均已上传到github库中。