看到一个博客 分析矢量地图切片技术,里面提到MBTiles文件。于是产生了想利用该文件格式部署离线地图想法,也是补充自己在使用mapbox地图数据来源这块短板。
使用mbtiles文件发布地图分为以下几步:
找到或制作合适的 mbtiles文件。osmlab,openmaptiles
tippecanoe将文件内的发布出去。mbview
以上的解决方案,都是网上搜集的方案。但是存在以下问题:
- osm在国内数据质量不行,如缺少建筑数据。
- 使用网上开源的发布程序,没找到自己称手的。
为了解决上面问题,打算自己写代码爬数据然后自己发布数据。
- 爬取四维图新的数据。
import sqlite3
import requests
from mbtiles.TileLnglatTransformGoogle import *
from mbtiles.TileLnglatTransformGaode import *
import time
#**创建表格接收数据**
def createDataTable(layerName):
print(layerName)
#链接sqlite数据库文件
conn = sqlite3.connect('minedata.mbtiles')
c = conn.cursor()
print("Opened database successfully");
table_name = layerName;
sql = "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='" + table_name + "';";
cursor = c.execute(sql);
dateArr = cursor.fetchall();
tableCount = dateArr[0][0];
if (tableCount > 0):
print("已存在")
else:
print("不存在")
createSql = "CREATE TABLE " + table_name + " (zoom_level integer, tile_column integer, tile_row integer, tile_data blob,primary key (zoom_level,tile_column,tile_row));"
cursor.execute(createSql)
conn.commit();
print("Operation done successfully");
cursor.close();
conn.close()
#瓦片入库
def insert_image_db(z,x,y,image,layerName):
sql_path = "minedata.mbtiles"
conn = sqlite3.connect(sql_path, timeout=10)
try:
sql="INSERT INTO "+layerName+" (zoom_level, tile_column, tile_row, tile_data) VALUES (?,?,?,?);"
conn.execute(sql,(z,x,y,image))
conn.commit()
conn.close()
except IOError:
print ("写入数据库失败")
conn.close()
#请求瓦片
def downImg(z,x,y,url,layerName):
url=url+str(z)+"/"+str(x)+"/"+str(y)+"?token=[your_key]&solu=2365";
print(url)
try:
print("url")
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
print(len(r.content))
if(len(r.content)>0):
img_blob=sqlite3.Binary(r.content);
insert_image_db(z,x,y,img_blob,layerName);
time.sleep(2);
except Exception as e:
print(e);
def downOneLayer(layerName,lnglatRange,startZ,lastZ):
url="http://datahive.minedata.cn/data/"+layerName+"/"
for zlevel in range(startZ,lastZ):
print(zlevel)
tilesRange = getTilesRange(lnglatRange, zlevel);
print(tilesRange)
minX = tilesRange[0];
minY = tilesRange[3];
maxX = tilesRange[2];
maxY = tilesRange[1];
print(tilesRange);
print(minX, maxX);
print(minY, maxY)
tiles = [];
for i in range(minX - 1, maxX + 1):
for j in range(minY - 1, maxY + 1):
tile = [i, j, zlevel];
tiles.append(tile);
for tile in tiles:
downImg(tile[2], tile[0], tile[1],url,layerName)
#根据经纬度、层级计算瓦片号
def lnglatToTilesRange(lnglat,z):
return lnglatToTile_gaode(lnglat[0],lnglat[1],z);
#获取瓦片范围
def getTilesRange(lnglatRange,z):
# [minX,minY,maxX,maxY]
return [ lnglatToTilesRange(lnglatRange[0],z)[0],lnglatToTilesRange(lnglatRange[0],z)[1],lnglatToTilesRange(lnglatRange[1],z)[0],lnglatToTilesRange(lnglatRange[1],z)[1]];
#layerName需要爬取的图层名,范围,开始层级,结束层级
def downTileLayer(layerName,minX,minY,maxX,maxY,startZ,lastZ):
createDataTable(layerName);
lnglatRange = [
[minX, minY], # [minX,minY]
[maxX, maxY], # [maxX,maxY]
];
downOneLayer(layerName,lnglatRange,startZ,lastZ);
downTileLayer("Waterface",119.9635, 30.0841,120.3971, 30.4146,12,18)
- 发布图层数据
import sqlite3
from django.http import HttpResponse
#发布图层数据
def getXYZ(request,x,y,z,layerName):
conn = sqlite3.connect('/home/yushi/PycharmProjects/superpower/simulation/view/minedata.mbtiles')
c = conn.cursor()
# z = 12;
# x = 2885;
# y = 2534;
sql="SELECT * from "+layerName+" m WHERE m.zoom_level = " + str(
z) + " AND m.tile_column = " + str(x) + " AND m.tile_row = " + str(y) + ""
cursor = c.execute(sql)
dateArr = cursor.fetchall();
print(len(dateArr))
print(sql)
if (len(dateArr) > 0):
pbfFile=dateArr[0][3];
FileName=dateArr[0][2];
cursor.close();
conn.close();
response = HttpResponse(pbfFile, content_type='application/octet-stream')
response['Content-Disposition'] = "filename="+str(FileName)
return response;
- mapbox添加图层,测试结果
//mapbox添加图层
map.on('load', function () {
map.addSource('minedata_source', {
type: 'vector',
tiles:['http://192.168.25.44:8000/simulation/getXYZ/{z}/{x}/{y}'],
maxzoom: 22
})
map.addLayer( {
"id": "83798d37c86f475985aa16353d0e269f",
"maxzoom": 17.5,
"source": "minedata_source",
"layout": {
"visibility": "visible"
},
"source-layer": "Waterface",
"paint": {
"fill-outline-color": "#e6120e",
"fill-color": "#e6120e",
"fill-antialias": true,
"fill-translate-anchor": "viewport"
},
"minzoom": 5.0,
"type": "fill",
})
结果验证,效果还是比较满意,自己爬数据自己发布数据,终于mapbox使用上补上了数据这块。