栅格瓦片转mongodb离线部署
栅格瓦片转mongodb离线部署
前提
上一篇博客中讲到了如何将
TMS
标准的瓦片转换成mbtiles格式发布服务,但是在现实环境中,使用的切图工具或者切图的人并没有按照标准切图。有的人切片为ArcServer切片或者GeoWebCache切片。这样的话上篇博客的方法就不能适用了。那么本篇博客就讲述上篇解决不了的切片。
实现思路
使用NoSQL(这里指
Mongodb
)对瓦片的png
图片进行入库存储,通过x,y,z来关联对应的图片,然后通过Nodejs查询得到图片的二进制写到页面中。完成展示瓦片的功能
准备工作
工具安装
这里的
Python
,NodeJS
,Mongodb
可以按照网上的教程安装,这里我就不再赘述了
步骤
-
下载栅格瓦片地图
-
编写脚本,将瓦片存入
Mongodb
库中,这里基于瓦片的图都比较小,就以二进制存到库中,不使用GridFS下面的脚本只是我自己要用的,不能满足所有情况,如果自己有特殊需求,可以对代码进行修改。大概思路就是:
- 循环遍历切片文件夹中的所有切片
- 将读取到的切片转成二进制并存储到库中
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')
执行完毕后库中的数据
- 给数据创建索引
png.layer.createIndex({x:1,y:1,z:1})
--创建完成后可以用explain看看有没有使用索引
-
编写node脚本,发布服务
这里只是简单的写了一下,node我也是边用边学的,如果已经是对node写服务端很熟悉的大佬这段就可以过滤掉了。如果跟我一样也是刚接触的话可以看一下。具体思路:
- 连接mongo,查询数据
- 写一个接口用于前端调用得到瓦片图
这里node需要安装的组件有:
mongoose
,express
新建一个node项目如下:
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)
})
-
使用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);
}
最后的效果:
总结
- 如果切片比较多,导入数据库的速度会受影响,可以考虑换成多线程进行处理
- 上面的效果是在我自己的开发机器上展现的。单机情况下,这个速度还是可以的。感觉做集群部署将请求进行分发,速度应该还能提高。
最后
以上的代码和脚本均已上传到github库中。