暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

开源GIS:基于PostGIS实现社区中小学步行15分钟覆盖率模型

GIS学习交流 2021-11-11
169

模型需求描述

模型主要是计算中小学缓冲区与居住用地的面积比率;模型的主要逻辑如下:在POI数据中筛选出中小学,利用中小学点数据创建1000米缓冲区;接着在土地利用现状图斑中筛选出居民用地数据,然后利用居民用地数据裁剪学校缓冲区;利用行政区对分别居民用地和裁剪后的学校缓冲区数据进行标识,标识完成后,使用行政区名称字段对标识结果进行融合,最后计算学校缓冲区面积和居民用地面积的比例。

数据说明

使用的数据为某市的测试数据,图层数据的简单说明如下:数据坐标系:国家2000坐标系 POI数据 行政区划面数据 土地利用现状图斑数据

数据入库使用PostGIS 2.0 Shapefile and DBF Loader Exporter工具把shapefile数据入库到PostgreSql中;入库的步骤如下;打开PostGIS 2.0 Shapefile and DBF Loader Exporter工具,点击【View connection details】按钮,在弹出的数据库连接信息窗口中填写连接信息;填写完毕点击【ok】按钮开始连接;如连接成功,可以在【log Window】中看到连接成功的信息;如下图1,2所示。



图1-数据库连接信息填写




图2-连接成功信息提示


点击【Add File】按钮,选择需要入库的Shapefile数据,选择完毕后点击【Open】按钮,把数据加载到Import页面中;如下图3所示;



图3-选择入库数据


在Import页面中填写数据的空间参考标识符信息(SRID),例如这里入库的数据空间参考为CGCS2000_3_Degree_GK_Zone_38,SRID值为4526;填写完毕后点击【Import】按钮开始导入数据;如导入完成后可以在【Log Window】中查看数据入库的信息;如下图4所示。


图4-数据入库


测试环境

两台配置一样的CentOS7虚拟机上分别安装PostgreSQL9.6(Postgis2.4)和阿里云POLARDB数据库。单台虚拟机的配置如下:内存:2 GB 磁盘:40 GB CPU:2核 使用pgAdmin4客户端连接管理PostgreSQL9.6和阿里云POLARDB数据库;通过Python3.6脚本,利用Python第三方库psycopg2连接操作PostgreSQL9.6和阿里云POLARDB数据库。使用PostGIS的处理逻辑如下

  1. 在POI中筛选出中小学构建1000米的缓冲区得到图层schoolbuffer;

create table IF NOT EXISTS SchoolBuffer as select ST_Buffer(geom,1000)::geometry(Polygon,4526)as geom from (select ST_Transform(geom,4526)as geom from public.poi2 where CLASID = '110501' OR CLASID = '110502') as geotran

2.在土地利用现状数据中筛选出居住用地("DLBM" = '0701' OR "DLBM" = '0702');

create table IF NOT EXISTS juminarea as select ST_Transform(geom,4526)as geom
from public.sdtdlydltb2 where DLBM = '0701' OR DLBM = '0702'

3.使用居住用地图层裁剪schoolbuffer图层,获得schoolbuffer_clip图层;

先把居住用地数据的所有面融合为一个几何图形

--居民用地融合为一个图形Create table cliptable as (SELECT st_Union(ST_Accum(ST_SnapToGrid(geom,0.0001))) as geom from juminarea where 1=1)--居民用地裁剪学校缓冲区

create table schoolbuffer_clip as (select st_intersection(geom,(select geom from cliptable)
::
geometry(MultiPolygon,4526)) as geom from SchoolBuffer where 1=1)

4.用行政区划数据分别对居住用地和schoolbuffer_clip图层进行标识;

GIS中的标识功能的描述如下图5所示。



图5-标识功能描述


在PostGIS中找不到标识函数,所以根据标识功能,使用PostGIS的函数st_intersection()获得几何图形,再结合Update获得相交图形部分的属性信息。

5.对标识后的居住用地和schoolbuffer_clip图层分别按照行政区名称字段进行融合;

--学校融合create table schoolbuffer_Union as select xzqmc,st_union(ST_Accum(ST_SnapToGrid(geom,0.0001)))
as geom from schoolbuffer_identify where 1=1 group by xzqmc

--居民用地融合create table juminarea_Union as select xzqmc,st_union(ST_Accum(ST_SnapToGrid(geom,0.0001)))
as geom from juminarea_identify where 1=1 group by xzqmc

6.计算学校缓冲区面积,居民用地面积,计算覆盖率(学校缓冲区面积/居住用地面积);

--计算学校面积update schoolbuffer_Union set schoolArea = st_area(geom)--计算居民用地面积update schoolbuffer_Union set jumin_area=(select st_area(geom)
from juminarea_Union where juminarea_Union.xzqmc=schoolbuffer_Union.xzqmc)--计算覆盖率update schoolbuffer_Union set fugailv=schoolArea/jumin_area

Python脚本操作数据库代码

根据模型的处理逻辑,结合PostGIS的SQL语句,在Python脚本中操作数据库; PostgresqlExexute.py文件主要用于实现数据库增删查改;MakeModel.py文件用于实现模型的逻辑,主要代码如下:PostgresqlExexute.py文件

import psycopg2

class DBExcute():
def __init__(self):
#虚拟机上的polardb
self
.Conn = psycopg2.connect(database='edb', user='postgres',
password
="postgres", host='192.168.207.111',
port
="5444")
self
.Cur = self.Conn.cursor()

def ExecuteSql(self,sql):
'''
执行SQL语句并提交;一般为更新,修改,创建,删除等操作
:param sql: SQL语句
:return: 执行SQL是否成功
'''

try:
self
.Cur.execute(sql)
self
.Conn.commit()
return True
except Exception as err:
print("执行SQL语句发生错误{0}".format(err ))
return False

def Check_Exist(self, sql):
self
.Cur.execute(sql)
count
= None
for cu in self.Cur:
count
= cu[0]
if count == 0:
return False
else:
return True

def GetRowstExecute(self,sql):
'''
执行查询sql,返回查询结果的行
:param sql: SQL语句
:return: 返回rows
'''

self
.Cur.execute(sql)
rows
=self.Cur.fetchall()
return rows

def GetExecuteSql(self,sql):
'''
执行SQL查询,返回几何图形的text
:param sql: SQL语句
:return: 只返回第一个图形的text
'''

self
.Cur.execute(sql)
rows
=self.Cur.fetchall()
for row in rows:
return row[0]


def dbClose(self):
self
.Conn.close()#postgresql数据库操作类实例化
dbexecute
=DBExcute()

MakeModel.py文件

from PostgresqlExexute import dbexecute
import time

def Union2(tableName,saveTableName):
'''
把表tableName的所有图形融合为一个图形,保存到表saveTableName中
:param tableName: 需要融合的表名称
:param saveTableName: 融合后的表名称
:return: 融合后的表名称
'''

# 判断表是否存在,存在则删除
deltablesql
= "drop table if exists {0}".format(saveTableName)
dbexecute
.ExecuteSql(deltablesql)
sql
="Create table {0} as (SELECT ST_Union(geom) as geom from {1} where 1=1)"
.format(saveTableName,tableName)
successful
=dbexecute.ExecuteSql(sql)
if(successful):
print("裁剪图层融合完成!")
return saveTableName
else:
return Nonedef clip(tableName,Cliptable,saveTableName):
'''
用ClipGeoText裁剪图层tableName,并新建一个裁剪后图层
:param tableName: 图层名称
:param ClipGeoText: 裁剪图形的几何text
:return: 返回裁剪后的图层名称
'''

#判断表是否存在,存在则删除
deltablesql
="drop table if exists {0}".format(saveTableName)
dbexecute
.ExecuteSql(deltablesql)
#判断图形是否可用
makevalid
= "update {0} set geom=st_makevalid(geom) where not st_isvalid(geom)".format(Cliptable)
print("正在修复图形……")
dbexecute
.ExecuteSql(makevalid)
print("裁剪图层修复完毕!")

getGeo
="select st_astext(geom) from {0}".format(Cliptable)
geotext
=dbexecute.GetExecuteSql(getGeo)

ClipAndCreatesql
= "create table {0} as (select st_intersection(geom,(select geom from {1})
::geometry(MultiPolygon,4526)) as geom from {2} where 1=1)".format(saveTableName,Cliptable,tableName)
print("正在进行裁剪操作……")
successful
=dbexecute.ExecuteSql(ClipAndCreatesql)
if successful:
return saveTableName
else:
return Nonedef identify(savetableName,intable,selGeotext,xzqmc):
'''
实现标识功能;利用相交获得与标识图形相交部分的几何图形,
然后把行政区名称,相交几何图形保存到表savetableName中
:param savetableName: 保存结果的表
:param intable: 需要标识的表
:param selGeotext: 标识几何图形的text
:param xzqmc: 标识字段
:return:
'''

#通过Geotext获得相交的部分图形,带入行政区名称,保存到savetableName中
selsql
="select st_astext(st_intersection(geom,(st_geomfromtext('{0}',4526))))
from {1} where 1=1".format(selGeotext,intable)
rows
=dbexecute.GetRowstExecute(selsql)
for row in rows:
geoinfo
=row[0]
insertSql
="insert into {0} (geom,xzqmc)values(ST_GeomFromText('{1}',4526),'{2}')"
.format(savetableName,geoinfo,xzqmc)
dbexecute
.ExecuteSql(insertSql)def UnionByField(intable,newTable,FieldName):
'''
按照字段FieldName把字段值相同的图形融合为一个
:param intable: 需要融合的表
:param newTable: 融合后的表
:param FieldName: 融合字段名称
:return: 融合后的表名称
'''

delsql
="drop table if exists {0}".format(newTable)
dbexecute
.ExecuteSql(delsql)
# 判断图形是否可用
makevalid
= "update {0} set geom=st_makevalid(geom) where not st_isvalid(geom)"
.format(intable)
dbexecute
.ExecuteSql(makevalid)#正在修复图形
union
= "create table {0} as select xzqmc,st_union(geom) as geom from {1} where 1=1 group by {2}"
.format(newTable, intable, FieldName)
successful
=dbexecute.ExecuteSql(union)
if successful:
return newTable
else:
return Nonedef mainFunction(POItable,sdtdlyxztable,xzqtable):
'''
社区中小学步行15分钟覆盖率模型的逻辑函数
:param POItable: POI数据表
:param sdtdlyxztable: 土地利用现状图斑表
:param xzqtable: 行政区划表
:return:
'''

#从POI中选择学校点并进行投影,缓冲
schoolbuffer
="schoolbuffer" #学校poi点缓冲区
delsql
="drop table if exists {0}".format(schoolbuffer)
dbexecute
.ExecuteSql(delsql)
bufferSql
="create table IF NOT EXISTS {0} as select ST_Buffer(geom,1000)::geometry(Polygon,4526) " \
"as geom from (select ST_Transform(geom,4526)as geom from {1} where CLASID = '110501' OR" \
" CLASID = '110502') as geotran".format(schoolbuffer,POItable)
successful
=dbexecute.ExecuteSql(bufferSql)
if successful==False:
print("选择学校并进行缓冲失败!")
return

#从土地利用现状图斑中选择居民用地并进行投影
juminarea
="juminarea"
delsql
="drop table if exists {0}".format(juminarea)
dbexecute
.ExecuteSql(delsql)
selSQL
="create table IF NOT EXISTS {0} as select ST_Transform(geom,4526) " \
"as geom from {1} where DLBM = '0701' OR DLBM = '0702'".format(juminarea,sdtdlyxztable)
successful
=dbexecute.ExecuteSql(selSQL)
if successful==False:
print("选择居民地并进行投影失败!")
return

# 居民用地融合,用于裁剪学校缓冲区
UniontableName
= "uniontb"
UniontableName
=Union2(juminarea,UniontableName)

#用居民用地裁剪学校缓冲区
ClipTableName
= "cliptb"
if UniontableName!=None:
clip
(schoolbuffer,UniontableName,ClipTableName)
else:
print("获得的裁剪图层名称为空!")
return

# 进行标识
scidentifyTb
="scidentifytb";jmidentifytb="jmidentifytb"
# 创建保存标识数据的表
delsql
= "drop table if exists {0}".format(scidentifyTb)
dbexecute
.ExecuteSql(delsql)
createSql
= "create table {0} (xzqmc text,geom geometry)".format(scidentifyTb)
dbexecute
.ExecuteSql(createSql)

delsql
= "drop table if exists {0} ".format(jmidentifytb)
dbexecute
.ExecuteSql(delsql)
createSql
= "create table {0} (xzqmc text,geom geometry)".format(jmidentifytb)
dbexecute
.ExecuteSql(createSql)

selsql
= "select st_astext(st_transform(geom,4526)),xzqmc from {0}".format(xzqtable)
xzqrows
= dbexecute.GetRowstExecute(selsql)
for row in xzqrows:
identify
(scidentifyTb, ClipTableName, row[0], row[1]) # 学校缓冲区
identify
(jmidentifytb, juminarea, row[0], row[1]) #居民居住区标识

# 按行政区进行融合
schoolbufferUnion
="schoolbufferUnion"
schoolbufferUnion
= UnionByField(scidentifyTb, schoolbufferUnion, "xzqmc")
if schoolbufferUnion == None:
print("学校缓冲图层融合失败!")
return

juminareaUnion
= "juminareaUnio"
juminareaUnion
= UnionByField(jmidentifytb, juminareaUnion, "xzqmc")
if juminareaUnion == None:
print("居民用地图层融合失败!")
return

# 分别添加学校面积,居民居住用地面积,覆盖率字段
schoolarea
= "schoolarea";juminareaField="juminarea"; fglfield="xxfgl"
addsql
= "alter table {0} add COLUMN {1} decimal,add COLUMN {2} decimal,add COLUMN {3} decimal".format(
schoolbufferUnion
, schoolarea,juminareaField,fglfield)
dbexecute
.ExecuteSql(addsql)

# 计算学校面积,居民用地面积
update
= "update {0} set {1} = st_area(geom)".format(schoolbufferUnion, schoolarea)
dbexecute
.ExecuteSql(update)

#通过计算表juminareaUnion几何图形面积,并保存到表schoolbufferUnion的juminarea字段中
update
="update {0} set {1}=(select st_area(geom) from {2} where {3}.xzqmc={4}.xzqmc)".format(
schoolbufferUnion
,juminareaField,juminareaUnion,juminareaUnion,schoolbufferUnion
)
dbexecute
.ExecuteSql(update)

#计算覆盖率xxfgl字段值;schoolarea/juminareaField
update
="update {0} set {1}={2}.{3}/{4}.{5}".format(schoolbufferUnion,fglfield,schoolbufferUnion,
schoolarea
,schoolbufferUnion,juminareaField)
dbexecute
.ExecuteSql(update)

dbexecute
.dbClose()
print("All Execute Successfully!")if __name__=="__main__":
start
=time.clock()
poi
="poi"
tuban
="sdtdlydltb"
xzq
="xzq"
mainFunction
(poi,tuban,xzq) #执行模型
end
=time.clock()print("Running time:%s Seconds"%(end-start)) #获得模型运行时间

运行效率对比

运行效率对比 使用同一份数据和相同的SQL语句在虚拟机环境相同的Postgresql和POLARDB数据库上运行,两者的运行时间对比如下表1所示。

Postgresql和POLARDB数据库运行时间截图如下图6,7所示。


图6 -Postgresql运行时间



图7-Polardb运行时间


运行结果对比

使用同一份数据,通过ArcMap的模型构建器实现社区中小学步行15分钟覆盖率模型,模型的结构图如下图8,9所示(图8,图9为同一个模型);使用ArcMap模型构建器运行的结果作为标准,对比验证Postgresql和Polardb数据库执行模型的准确度。


图8



图9


在ArcMap,polardb和postgresql数据库中运行模型的结果如下图所示。


图10-ArcMap运行结果



图11-polardb运行结果



图12-postgresql运行结果


Polardb、postgresql和ArcMap的15分钟覆盖率字段值对比如下表2所示;通过对比三者的运行结果发现,polardb与postgresql的15分钟覆盖率字段值几乎完全一样;polardb(或postgresql)数据库与ArcMap的15分钟覆盖率相差0.001左右。

欢迎关注微信公众号【GIS学习交流】进行学习交流


文章转载自GIS学习交流,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论