文章主要来自于该篇文章,对博客中的内容进行了注释及部分修改,帮助理解。
https://www.jianshu.com/p/24f7363c05b9
#BBOX函数,该函数要先在数据库中运行才不会报错,将文中的注释去掉
CREATE OR REPLACE FUNCTION BBox(x integer, y integer, zoom integer)
RETURNS geometry AS
$BODY$
DECLARE
max numeric := 6378137 * pi();#墨卡托投影的最大范围
res numeric := max * 2 / 2^zoom;#墨卡托投影下一个瓦片代表的实际距离
bbox geometry;
BEGIN
根据传入的行列号计算出该瓦片对应的墨卡托投影范围并转换到84坐标系下,
返回的范围是84坐标系下的范围--经纬度
return st_transform(ST_MakeEnvelope(
-max + (x * res),
max - (y * res),
-max + (x * res) + res,
max - (y * res) - res,
3857),4326);
END;
$BODY$
LANGUAGE plpgsql IMMUTABLE;
python执行代码
import psycopg2
#配置数据库字典
dbConfig={
"database":"postgis_25_sample",
"user":"postgres",
"password":"123456",
"host":"localhost",
"port":"5432"
}
#该函数的作用就是根据表名,动态生成sql语句,在已知表结构的情况下,完全不需要这个函数。
def createSql(table_name,x,y,z):
geoColumn = "(select f_geometry_column from geometry_columns where f_table_name= '" + table_name + "' )"
#由于pg_attribute中没有与表名直接对应的字段,先从pg_class中选出c表,得到其c.oid。再用a.attrelid判断,即可得到c表
#实际上第一句sql做的事情就是将空间字段列和属性字段列分开存储f_geometry_column,和name列中
condition=" pg_class as c,pg_attribute as a where c.relname = '" + table_name + "' and a.attrelid = c.oid and a.attnum>0"
sql1 = "SELECT "+geoColumn+",replace(replace(string_agg(a.attname,','),"+geoColumn+"||',',''),"+geoColumn+"||'','') as name FROM" +condition
print(sql1)
conn = psycopg2.connect(database=dbConfig["database"], user=dbConfig["user"], password=dbConfig["password"], host=dbConfig["host"], port=dbConfig["port"])
cur = conn.cursor()
cur.execute(sql1)
keyData = cur.fetchall()
geom_column=keyData[0][0]
other_fields=keyData[0][1]
cur.close()
conn.close()
#select 前面就是正常的切片,from 后面的select在选择空间属性与普通属性列,where条件则是先判断是否相交,如果相交则返回交集
sql2="SELECT ST_AsMVT(q, '"+table_name+"', 4096, 'geometry') from (SELECT "+other_fields+"ST_AsMvtGeom("+geom_column+",BBox("+str(x)+", "+str(y)+" ,"+str(z)+") , 4096,256, true) as geometry FROM "+table_name+" WHERE "+geom_column+" && BBox("+str(x)+", "+str(y)+" ,"+str(z)+") AND ST_Intersects("+geom_column+", BBox("+str(x)+", "+str(y)+" ,"+str(z)+") )) q;"
return sql2;
#运行sql,获得mvt的16进制文件
def getPbfFile(table_name,x,y,z):
sql=createSql(table_name,x,y,z)
conn = psycopg2.connect(database=dbConfig["database"], user=dbConfig["user"], password=dbConfig["password"], host=dbConfig["host"], port=dbConfig["port"])
cur = conn.cursor()
cur.execute(sql)
print(sql)
keyData = cur.fetchall()
cur.close()
conn.close()
with open(str(z)+"_"+str(x)+"_"+str(y)+'.pbf', 'wb') as f:
f.write(keyData[0][0])
return keyData[0][0]
#x,y,z对应的是矢量切片的行号,列号,级别
tile=getPbfFile("cs",54605,26956,16)
print(tile)