前面学习GeoServer发布数据的时候,我就一直在想,GeoServer可不可以自动、批量发布地图服务,毕竟实际工作中的数据可能是大量的,或着是实时生成的,如果不能自动、批量发布数据,那么GeoServer恐怕无法满足我的实际需求。事实证明我想多了,GeoServer能在地图服务领域获得主流地位这么简单的问题肯定是提供了解决方法的,那就是rest接口。
rest接口实质上对应的是一个http地址,我们可对通过不同的方式向这个地址发送请求以达到我们读取、增加、修改、删除geoserver数据的目的,每个接口有get、post、put、delete四种请求方法对应到geoserver模块的读、写、改、删功能,当然并不是每个接口的这四个方法都是有效的,因为并非每个模块都有读、写、改、删功能,个别接口还可以进行head请求。理论上任务服务器语言都可以实现通过Rest操作geoserver,python操作http的包比较多,比如http,urllib,urllib2,requests等。
下面以datastores接口为例,说明通过rest操作geoserver的过程,datastores对应的地址http://localhost:8080/geoserver/rest/workspaces/{workspace_name}/datastores
,比如我们要获得tiger工作空间下的datastore实现过程如下:
http://localhost:8080/geoserver/rest/workspaces/tiger/datastores
headers={
'Accept':'application/xml'}
requests.get('http://localhost:8080/geoserver/rest/workspaces/tiger/datastores',auth=('admin','geoserver'),headers=headers)
headers
定义了接收数据的方式是xml,如果要获得json格式的数据就是application/json
,如果要定义发送数据的格式用在headers中用content-type
,比如{'content-type':'application/json'}
auth
中是geoserver的用户名和密码<dataStores>
<dataStore>
<name>nycname>
<atom:link xmlns:atom="http://www.w3.org/2005/Atom" rel="alternate" href="http://localhost:8080/geoserver/rest/workspaces/tiger/datastores/nyc.xml" type="application/atom+xml"/>
dataStore>
dataStores>
结果说明tiger工作空间下只有一个datastore,其名称是nyc
如果要添加一个datastore就应该向该地址发送post请求:
headers={
'content-type':'application/xml'}
requests.post('http://localhost:8080/geoserver/rest/workspaces/tiger/datastores',data=open('ds.xml').read(),auth=('admin','geoserver'),headers=headers)
ds.xml
的内容如下:
<dataStore>
<name>shpname>
<type>Shapefiletype>
<description>Shapefile created from RESTdescription>
<connectionParameters>
<entry key="charset">GBKentry>
<entry key="filetype">shapefileentry>
<entry key="url">*.shpentry>
<entry key="cache and reuse memory maps">trueentry>
<entry key="create spatial index">trueentry>
<entry key="memory mapped buffer">falseentry>
<entry key="timezone">Pacific Standard Timeentry>
connectionParameters>
dataStore>
这个地址并没有delete方法和put方法,因为它一个datastore的集合,而删除和修改要对应到一个具体的datastore,它的url应该是http://localhost:8080/geoserver/rest/workspaces/{workspace_name}/datastores/{datastore_name}
如下要删除tiger下的nyc存储应该是
requests.delete(url,auto=(‘admin’,‘geoserver’))
其它的模块的操作我就不展开说了,如果想学习可以参考这篇文章:geoserver REST使用
最后给大家安利一下我自己花了半个月时间用python写的geoserver rest库。下载地址请猛戳这里,水平、时间有限,目前只实现了一些常用的接口,并没有完全实现,后面如果有需要会持续更新,如果能帮到大家请点赞,如果写的太烂请轻喷。下面写一些简单的用法.
其结构与GeoServer相对应,最顶层是Catlog目录类,Catlog下有workspace(s),datastore(s),coveragestore(s),layer(s),style(s),layer下还有featuretype和coverage,需要说明的是catlog下的每个类都有两种,比如workspace和workspaces,前者指的是一个具体的worksapce,后者指是所有workspace的集合,其它同理。
下载地址
解压后执行python setup.py install安装,此外还需要安装两个库geopandas和rasterio,通过conda可以快速安装
conda install -c conda-forge geopandas
conda install rasterio
from rest.catlog import catlog
cat=Catlog(password='xtfge')
print(cat.workspaces.items)
[out]~:['cite', 'tiger', 'nurc', 'sde', 'it.geosolutions', 'topp', 'sf', 'resttest', 'test']
Catlog
类有6个参数,分别是service_url,user,password,workspace,datastore,coveragestore,其中后3个参数在初始化时可以不指定,service_url默认值是http://localhost:8080/geoserver/rest,即本地geoserver服务,且路径配置没有修改,默认用户名和密码分别是admin和geoserver,即如果你使用的是本地服务,用户名和密码也是默认的用户名密码,那么有定义Catlog时可以直接用Catlog()。cat.workspace='tiger'#设置cat的工作空间为tiger
print(cat.workspace.isolated)
#查看工作空间tiger下的datastore,下面两种方法返回的结果是相同的,因为当设置workspace之后对catlog的操作就限制到这个命名空间之下了
print("cat.workspace.datastores.items:",cat.workspace.datastores.items)
print("cat.datastores.items",cat.datastores.items)
[out]~:cat.workspace.datastores.items: ['nyc']
cat.datastores.items ['nyc']
#查看tiger下的图层,当设置命名寒意之后,下面两个方法是等价的,如果没有设置命名空间,后都将返回服务器上所有的layer
print("cat.workspace.layers.items",cat.workspace.layers.items)
print("cat.layers.items",cat.layers.items)
#如是在定义命名空间之后想查看所有layer或其它命名空间下的layer,可以使用get_layers()方法
print("cat.get_layers().items",cat.get_layers().items)#不设置命名空间返回所有layer
print("cat.get_layers('tiger').items",cat.get_layers('tiger').items)#返回tiger上所有layer
##添加、删除workspace
print('添加前的命名空间:',cat.workspaces.items)
#添加命名空间需要提供两个参数,name和uri,如果uri未指定将默认http://{name}
#添加命名空间也可以用cat.add_workspace('test_workspace')
cat.workspaces.add('test_workspace')
print('添加后的命名空间:',cat.workspaces.items)
#删除命名空间也可以用cat.workspaces['test_workspace'].drop()
#或着cat.drop_workspace(name),其本质都是调用datastore.drop(),推荐使用cat.drop_workspace(name)
cat.workspaces.drop('test_workspace')
print('删除了新添加的命名空间',cat.workspaces.items)
[out]~:添加前的命名空间: ['cite', 'tiger', 'nurc', 'sde', 'it.geosolutions', 'topp', 'sf', 'resttest', 'test']
Workspace 'test_workspace' create successfully
添加后的命名空间: ['test_workspace', 'cite', 'tiger', 'nurc', 'sde', 'it.geosolutions', 'topp', 'sf', 'resttest', 'test']
Successfully drop workspace "test_workspace"
删除了新添加的命名空间 ['cite', 'tiger', 'nurc', 'sde', 'it.geosolutions', 'topp', 'sf', 'resttest', 'test']
cat.datastore='ffaf'
print(cat.datastore.name)
print(cat.datastore.type)
print(cat.datastore.enabled)
#添加datastore
cat.add_datastore('test_store',storetype=Datastoretype.directory,url='/home/zhang/Desktop/')
print(cat.datastores['test_store'].name)
print(cat.datastores['test_store'].type)
[out]~:successful add a datastore.
test_store
Directory of spatial files (shapefiles)
add_datastore
的参数:
#如果要用postgis添加store
cat.add_datastore('test_postgis1',Datastoretype.postgis,database='test',host='localhost',user='postgres',passwd='postgres',schema='public',port=5432)
#如果你的数据库密码的端口是默认的,则可以不写,如果你的数据库在本地host也可以不定义,比如下面的代码可以写成
cat.add_datastore('test_postgis1',Datastoretype.postgis,database='test')
##删除datastore
cat.drop_datastore('test_store',True)
#如果datastore不为空,必须指定recurse参数为True才能删除
print(cat.coveragestores.items)
cat.coveragestore='newcoverage0'
# print(cat.coveragestore.type)
print(cat.coveragestore.enabled)
print(cat.coveragestore.name)
cat.add_coveragestore('test_cstore',Coveragestoretype.geotiff,url='/home/zhang/Desktop/China_dem.tif')
#删除多个datastore时也可以这么写
cat.drop_coveragestore(['newcoverage', 'newcoverage0', 'newcoverage10', 'newcoverage19990','test_cstore'])
ps:重头戏来了,为了说清楚,这里从头开始说前面已经说过了
#发布一个shp文件,比如我有一个china_province.shp的文件
#首先创建Catlog
cat=Catlog(password='xtfge')
##确定要发布图层的命名空间,如果不存在则创建,比如我们要在一个新的命名空间rest_workspace上发布数据,
ws=cat.create_workspace('rest_workspace')
cat.workspace=ws #or cat.workspace='rest_workspace'
##创建datastore
cat.add_datastore('shpstore',url='/home/zhang/Desktop/China_province.shp')
##发布服务
cat.publish_layer(layername='China_province',store=ws.datastores['shpstore'])
#从一个文件夹发布数据
cat.add_datastore('dir_store',Datastoretype.directory,url='/home/zhang/Desktop/')
cat.publish_layer('sjy','三江源',store='dir_store',storetype=Datastoretype.directory)
#从postgis发布数据
cat.add_datastore('China',Datastoretype.postgis,database='test')
###注意下面两个图层写法的区别,store可以是store的名称,也可以是一个Datastore对象,如果store指定的Datastore,可以不用定义storetype
#发布数据库图层必须提供数据库密码
cat.publish_layer('china_province',password='postgres',store='China',storetype=Datastoretype.postgis)
cat.publish_layer('china_weather_station',layername='weather_station',password='postgres',store=cat.datastores['China'])
#看一下刚才添加的图层,layername指发布图层的名称,如果不指定将以数据的名称作为图层名称
print(cat.layers.items)
[out]~:['china_province', 'weather_station']
#那么如果我们要对图层的属性进行设置应该怎么作的,方法比较多,推荐使用layer_setter_function,比如我有一个世界河流数据
def layer_setter(featureinfo):
CHINA_EPSG3857_BOUNDARY=[8181867.023674167,427100.28419312113,15037859.133433886,7086980.581736245]
FeatureInfo.keywords=['河流数据']
featureinfo.name='中国河流'
featureinfo.title='中国河流矢量数据'
featureinfo.boundary=CHINA_EPSG3857_BOUNDARY#这是世界渡河数据,但我只想显示中国部分,所以设置边界为中国的范围
return featureinfo
cat.publish_layer('world_river',store=cat.datastores['China'],password='postgres',layer_setter_function=layer_setter)
#发布一个tif数据
cat.add_store('tif_store',Coveragestoretype.geotiff,url='/home/zhang/Desktop/China_dem.tif')
cat.publish_layer(layername='中国高程数据',store=cat.coveragestores['tif_store'])
那么到底发布成功了没有,我们用leaflet来看一下
##删除layer
cat.drop_layer('三江源',True)
#如果该图层正在使用,必须设置recurse参数为True才能删除
#获取当前命名空间下的style列表
print(cat.styles.items)
#获得任意命名空间下的style列表,若workspace未指定,则返回所有style
print(cat.get_styles())
#添加一个style
#比如我们要给刚才添加的图层定义个样式,毕竟默认的样式太丑了
mypoint="""
* {
mark:symbol(circle);
:mark{
size: 8px;
fill: #51ce17;
stroke: red;
stroke-width: 2;
}
}
"""
mypolygon='''/* @title area>100 */
[shape_area>100]{
fill:#f1f507;
fill-opacity: 0.7;
}
/* @title 北京市 */
[name='北京市']{
fill:#ff0000;
fill-opacity: 0.7;
}
/* @title 省界 */
* {
stroke: #000000;
stroke-width: 0.5;
fill: #0099cc;
label: [name];
label-anchor: 0.5 0.5;
font-family: "宋体";
font-fill: #000000;
font-size: 14;
font-style: normal;
}
'''
from rest.style import StyleType
myline='/home/zhang/Desktop/point.sld'
cat.add_style(stylebody=open(myline).read())
cat.add_style('point_style',StyleType.css,mypoint)
cat.add_style('polygon_style',StyleType.css,mypolygon.encode('utf-8'))
#style定义好了接下来就是应用到图层了
cat.layers['china_province'].set_style(cat.styles['polygon_style'])
cat.layers['weather_station'].style=cat.styles['point_style']
cat.layers['中国河流'].set_style(cat.styles[-1])