写在前面:我在github
上创建了对应的项目,可点此跳转,本文的所有源码均可在项目里找到,欢迎大家访问交流
一、开发背景
在gis
领域,geoserver
是后端地图发布的开源项目。目前我们在启动服务后,可通过自带的web
应用进行地图发布操作,这个过程是简单有效的,但是,如果我们想要把地图发布的功能集成到自己的平台里,仍旧使用geoserver
的web
应用就显得格格不入,为此我们需要对geoserver
的功能进行封装,开发自己的后端服务,并配套对应的前端功能(自带的web应用只将其当作管理工具)。本篇研究了基于python
语言开发的geoserver-restconfig
库,该库封装了geoserver
的常用操作函数,考虑到自身的需求以及该库存在的问题,本篇在该库的基础上开发了新的服务类,主要用于发布shp
、普通tiff
、大型tiff
,经过测试,该服务类可正常使用,可用于后端服务的构建
二、geoserver-restconfig
基于python
语言开发的geoserver
使用库,可进行工作区的创建删除、shp
图层的创建删除、tiff
图层的创建删除等。但是在使用时存在两个问题:
1、类实例化失败(requests版本为2.30
):TypeError: Retry.__init__() got an unexpected keyword argument 'method_whitelist'
2、该库不支持切片tiff
的发布
三、GeoServerCatalog
针对上述问题,对该库的核心类Catalog
进行派生,主要做两个改动
3.1 重构create_coveragestore函数
该函数可用于栅格数据的发布,其中GeoTIFF
类型就是tiff
文件发布对应的类型,在我的另一篇文章:使用geoserver发布shp和tiff数据 中已经介绍了使用geoserver
发布切片tiff
目录的方法,对应的类型是ImagePyramid
,但此类型在该类中没有(没有是正常的,因为切片tiff
类型是通过插件方式增加的),所以我们重写该函数,在allowed_types
中增加ImagePyramid
即可,其他相同
3.2 重构setup_connection函数
经调试发现,Retry
的报错是由于对象实例化时参数不对,主要时method_whitelist
这个变量名不对,经过排查,确认是requests
库的版本不对,2.30
版本的Retry
类用的是allowed_methods
,2.28
版本用的是method_whitelist
,为了兼容不同的版本,在派生类中重写该函数,通过try except
来捕获由于版本差异导致的异常,在捕获异常时,使用另一版本的参数
3.3 源码
# GeoServerCatalog.py
import os
from geoserver.catalog import Catalog, ConflictingDataError, _name, FailedRequestError
from geoserver.store import UnsavedCoverageStore
from geoserver.support import build_url
import requests
from requests.packages.urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
class GeoServerCatalog(Catalog):
"""
geoserver.Catalog的派生类
用于扩展基类的create_coveragestore函数,使其支持"ImagePyramid"类型
"""
def __init__(self, service_url, username="admin", password="geoserver", validate_ssl_certificate=True, access_token=None, retries=3, backoff_factor=0.9):
super().__init__(service_url, username, password, validate_ssl_certificate, access_token, retries, backoff_factor)
def create_coveragestore(self, name, workspace=None, path=None, type='GeoTIFF',
create_layer=True, layer_name=None, source_name=None, upload_data=False, contet_type="image/tiff",
overwrite=False):
"""
Create a coveragestore for locally hosted rasters.
If create_layer is set to true, will create a coverage/layer.
layer_name and source_name are only used if create_layer ia enabled. If not specified, the raster name will be used for both.
"""
if path is None:
raise Exception('You must provide a full path to the raster')
if layer_name is not None and ":" in layer_name:
ws_name, layer_name = layer_name.split(':')
allowed_types = [
'ImageMosaic',
'GeoTIFF',
'Gtopo30',
'WorldImage',
'AIG',
'ArcGrid',
'DTED',
'EHdr',
'ERDASImg',
'ENVIHdr',
'GeoPackage (mosaic)',
'NITF',
'RPFTOC',
'RST',
'VRT',
'ImagePyramid'
]
if type is None:
raise Exception('Type must be declared')
elif type not in allowed_types:
raise Exception(f"Type must be one of {', '.join(allowed_types)}")
if workspace is None:
workspace = self.get_default_workspace()
workspace = _name(workspace)
if not overwrite:
stores = self.get_stores(names=name, workspaces=[workspace])
if len(stores) > 0:
msg = f"There is already a store named {name} in workspace {workspace}"
raise ConflictingDataError(msg)
if upload_data is False:
cs = UnsavedCoverageStore(self, name, workspace)
cs.type = type
cs.url = path if path.startswith("file:") else f"file:{path}"
self.save(cs)
if create_layer:
if layer_name is None:
layer_name = os.path.splitext(os.path.basename(path))[0]
if source_name is None:
source_name = os.path.splitext(os.path.basename(path))[0]
data = f"<coverage><name>{layer_name}</name><nativeName>{source_name}</nativeName></coverage>"
url = f"{self.service_url}/workspaces/{workspace}/coveragestores/{name}/coverages.xml"
headers = {"Content-type": "application/xml"}
resp = self.http_request(url, method='post', data=data, headers=headers)
if resp.status_code != 201:
raise FailedRequestError('Failed to create coverage/layer {} for : {}, {}'.format(layer_name, name,
resp.status_code, resp.text))
self._cache.clear()
return self.get_resources(names=layer_name, workspaces=[workspace])[0]
else:
data = open(path, 'rb')
params = {"configure": "first", "coverageName": name}
url = build_url(
self.service_url,
[
"workspaces",
workspace,
"coveragestores",
name,
f"file.{type.lower()}"
],
params
)
headers = {"Content-type": contet_type}
resp = self.http_request(url, method='put', data=data, headers=headers)
if hasattr(data, "close"):
data.close()
if resp.status_code != 201:
raise FailedRequestError('Failed to create coverage/layer {} for : {}, {}'.format(layer_name, name, resp.status_code, resp.text))
return self.get_stores(names=name, workspaces=[workspace])[0]
def setup_connection(self, retries=3, backoff_factor=0.9):
self.client = requests.session()
self.client.verify = self.validate_ssl_certificate
parsed_url = urlparse(self.service_url)
try:
retry = Retry(
total = retries or self.retries,
status = retries or self.retries,
read = retries or self.retries,
connect = retries or self.retries,
backoff_factor = backoff_factor or self.backoff_factor,
status_forcelist = [502, 503, 504],
allowed_methods = set(['HEAD', 'TRACE', 'GET', 'PUT', 'POST', 'OPTIONS', 'DELETE'])
) # allowed_methods : requests > 2.30.0
except TypeError:
retry = Retry(
total = retries or self.retries,
status = retries or self.retries,
read = retries or self.retries,
connect = retries or self.retries,
backoff_factor = backoff_factor or self.backoff_factor,
status_forcelist = [502, 503, 504],
method_whitelist = set(['HEAD', 'TRACE', 'GET', 'PUT', 'POST', 'OPTIONS', 'DELETE'])
) # method_whitelist : requests <= 2.30.0
self.client.mount(f"{parsed_url.scheme}://", HTTPAdapter(max_retries=retry))
四、GeoServerService
Catalog
类提供的都是一些基本功能函数,为了能快速的发布数据,我们对GeoServerCatalog
进行封装,构建GeoServerService
类,该类主要用于实现发布shp
、tiff
、金字塔tiff
的功能
4.1 发布shp图层
函数名:createShapeLayer
,参数如下。此函数可用于发布shp
文件,其中styleParas
是图层样式参数,用于控制图层的显示,主要支持点、线、多边形
workspaceName: 图层所在的工作空间的名称
layerName:图层的名称
shapePath:shape文件的路径
charset:dbf的字符集
styleParas:图层的样式参数,
point-{type:circle/rectangle/star, color:“#000000”, transparency:0.5, size:10}
polyline/line-{color:“#000000”, width:1}
polygon-{fill_color:“#AAAAAA”, outline_color:“#000000”, outline_width:1}
点的样式参数:
type:类型,支持圆、正方形、五角星
color: 颜色
transparency: 透明度
size: 大小
线的样式参数:
color: 颜色
width: 宽度
多边形的样式参数:
fill_color:填充颜色
outlin_color: 边框颜色
outline_width: 边框宽度
4.2 发布普通tiff图层
函数名:createTiffLayer
,参数如下。此函数可用于发布<2GB
的tiff
文件
workspaceName: 图层所在的工作空间的名称
layerName:图层的名称
tiffPath:tiff文件的路径
4.3 tiff切片
tiff
切片需要用到gdal
库,对应的下载地址为:windows版、linux版
下载的是whl
类型的,可通过pip
直接安装
该类中的createPyramidTiff
可将tiff
文件切片生成金字塔级别的目录,参数介绍如下
tiffPath:tiff文件的路径
tiffDir:生成的金字塔文件夹的路径
level:金字塔层级,可选,默认为4
blockWidth: 金字塔切块的宽度分辨率,可选,默认为2048
blockHeight: 金字塔切块的高度分辨率,可选,默认为2048
4.4 发布金字塔切片tiff图层
函数名:createPyramidTiff
,参数如下。此函数可用于发布>=2GB
的tiff
文件,该函数会自动调用上述的切片函数,并发布
tiffPath:tiff文件的路径
tiffDir:生成的金字塔文件夹的路径
level:金字塔层级,可选,默认为4
blockWidth: 金字塔切块的宽度分辨率,可选,默认为2048
blockHeight: 金字塔切块的高度分辨率,可选,默认为2048
4.5 其他函数
除了上述4
个比较常用的函数外,还有如创建工作空间
、获取工作空间列表
、修改样式
等函数,可自行研究,不再详述
4.6 源码
# GeoServerService.py
import subprocess
import geoserver.util
from GeoServerCatalog import GeoServerCatalog
class GeoServerService(object):
"""
基于GeoServerCatalog库封装的常用服务类
"""
def __init__(self, url, username, password) -> None:
self.__cat = GeoServerCatalog(url, username, password)
def save(self, obj):
self.__cat.save(obj)
def getWorkspace(self, workspaceName):
"""
通过名称获取工作空间
workspaceName: 工作空间的名称
return Workspace
"""
return self.__cat.get_workspace(workspaceName)
def isWorkspaceExist(self, workspaceName):
"""
判断工作空间是否存在
workspaceName: 工作空间的名称
return True-存在;False-不存在
"""
res = self.getWorkspace(workspaceName)
if res == None:
return False
else:
return True
def createWorkspace(self, workspaceName):
"""
创建工作空间
workspaceName: 工作空间的名称
return {
status: 状态,success-创建成功;fail-创建失败
info: 信息, success-""; fail-工作空间已存在;其他信息
data: None
}
"""
res = {
"status": "fail",
"info": "",
"data": None
}
try:
if self.isWorkspaceExist(workspaceName):
res["info"] = "工作空间已存在:{0}".format(workspaceName)
return res
self.__cat.create_workspace(workspaceName, "http://{0}.com".format(workspaceName))
res["status"] = "success"
return res
except Exception as e:
res["info"] = repr(e)
return res
def deleteWorkspace(self, workspaceName):
"""
删除工作空间(会删除工作空间下的所有内容,包括图层和样式)
workspaceName: 工作空间的名称
"""
workspace = self.getWorkspace(workspaceName)
if workspace != None:
self.__cat.delete(workspace, None, True)
def getStore(self, workspaceName, storeName):
"""
通过名称获取数据存储
workspaceName: 数据存储所在的工作空间的名称
storeName:数据存储的名称
return Store
"""
return self.__cat.get_store(storeName, workspaceName)
def isStoreExist(self, workspaceName, storeName):
"""
判断数据存储是否存在
workspaceName: 数据存储所在的工作空间的名称
storeName:数据存储的名称
return True-存在;False-不存在
"""
res = self.getStore(workspaceName, storeName)
if res == None:
return False
else:
return True
def deleteStore(self, workspaceName, storeName):
"""
删除数据存储
workspaceName: 数据存储所在的工作空间的名称
storeName:数据存储的名称
"""
store = self.getStore(workspaceName, storeName)
if store != None:
self.__cat.delete(store, None, True)
def getLayer(self, workspaceName, layerName):
"""
通过名称获取图层
workspaceName: 图层所在的工作空间的名称
layerName:图层的名称
return Layer
"""
layerNameReg = "{0}:{1}".format(workspaceName, layerName)
return self.__cat.get_layer(layerNameReg)
def isLayerExist(self, workspaceName, layerName):
"""
判断图层是否存在
workspaceName: 图层所在的工作空间的名称
layerName:图层的名称
return True-存在;False-不存在
"""
res = self.getLayer(workspaceName, layerName)
if res == None:
return False
else:
return True
def createShapeLayer(self, workspaceName, layerName, shapePath, charset):
"""
创建Shp图层
workspaceName: 图层所在的工作空间的名称
layerName:图层的名称
shapePath:shape文件的路径
charset:dbf的字符集
styleParas:图层的样式参数,
point-{type:circle/rectangle/star, color:"#000000", transparency:0.5, size:10}
polyline/line-{color:"#000000", width:1}
polygon-{fill_color:"#AAAAAA", outline_color:"#000000", outline_width:1}
return {
status: 状态,success-创建成功;fail-创建失败
info: 信息, success-""; fail-工作空间不存在/图层已存在/其他信息
data: Layer
}
"""
res = {
"status": "fail",
"info": "",
"data": None
}
try:
# 判断工作空间是否存在
if not self.isWorkspaceExist(workspaceName):
res["info"] = "工作空间不存在:{0}".format(workspaceName)
return res
# 判断图层是否存在
if self.isLayerExist(workspaceName, layerName):
res["info"] = "图层已存在:{0}".format(layerName)
return res
# 创建图层
workspace = self.getWorkspace(workspaceName)
try:
data = geoserver.util.shapefile_and_friends(shapePath)
self.__cat.create_featurestore(layerName, data, workspace, True, charset)
except Exception as e:
res["info"] = "文件解析错误"
return res
# 获取创建的图层
layer = self.getLayer(workspaceName, layerName)
styleType = layer.default_style.name
res["status"] = "success"
res["data"] = {
"layer": layer,
"default_style": styleType
}
return res
except Exception as e:
res["info"] = repr(e)
return res
def __createTiffLayer(self, workspaceName, layerName, tiffPath, pyramid=False):
"""
创建Tiff图层
workspaceName: 图层所在的工作空间的名称
layerName:图层的名称
tiffPath:tiff文件/夹的路径
pyramid: 是否为金字塔结构,默认false
return {
status: 状态,success-创建成功;fail-创建失败
info: 信息, success-""; fail-工作空间不存在/图层已存在/其他信息
data: {
layer:生成的图层对象,
default_style: 图层的默认样式
}
}
"""
res = {
"status": "fail",
"info": "",
"data": None
}
try:
# 判断工作空间是否存在
if not self.isWorkspaceExist(workspaceName):
res["info"] = "工作空间不存在:{0}".format(workspaceName)
return res
# 判断图层是否存在
if self.isLayerExist(workspaceName, layerName):
res["info"] = "图层已存在:{0}".format(layerName)
return res
# 创建图层
workspace = self.getWorkspace(workspaceName)
try:
tiffType = "GeoTIFF"
if pyramid:
tiffType = "ImagePyramid"
self.__cat.create_coveragestore(name=layerName, workspace=workspace, path=tiffPath, type=tiffType, layer_name=layerName)
except Exception as e:
res["info"] = "文件解析错误"
return res
# 获取创建的图层
layer = self.getLayer(workspaceName, layerName)
styleType = layer.default_style.name
res["status"] = "success"
res["data"] = {
"layer": layer,
"default_style": styleType
}
return res
except Exception as e:
res["info"] = repr(e)
return res
def createTiffLayer(self, workspaceName, layerName, tiffPath):
"""
创建Tiff图层:适用于文件大小<2GB的Tiff
workspaceName: 图层所在的工作空间的名称
layerName:图层的名称
tiffPath:tiff文件的路径
return {
status: 状态,success-创建成功;fail-创建失败
info: 信息, success-""; fail-工作空间不存在/图层已存在/其他信息
data: Layer
}
"""
return self.__createTiffLayer(workspaceName, layerName, tiffPath)
def createPyramidTiff(self, tiffPath, tiffDir, levels=4, blockWidth=2048, blockHeight=2048):
"""
对tiff进行金字塔切片
tiffPath:tiff文件的路径
tiffDir:生成的金字塔文件夹的路径
level:金字塔层级,可选,默认为4
blockWidth: 金字塔切块的宽度分辨率,可选,默认为2048
blockHeight: 金字塔切块的高度分辨率,可选,默认为2048
"""
exeStr = 'python gdal_retile.py -v -r bilinear -ot BYTE -levels {0} -ps {1} {2} -co "ALPHA=YES" -targetDir {3} {4}'.format(levels, blockWidth, blockHeight, tiffDir, tiffPath)
process = subprocess.Popen(exeStr, shell=True)
process.wait()
def createPyramidTiffLayer(self, workspaceName, layerName, tiffPath, tiffDir, levels=4, blockWidth=2048, blockHeight=2048):
"""
创建金字塔切片后的Tiff图层:适用于文件大小>=2GB的Tiff
workspaceName: 图层所在的工作空间的名称
layerName:图层的名称
tiffPath:tiff文件的路径
tiffDir:生成的金字塔文件夹的路径
level:金字塔层级,可选,默认为4
blockWidth: 金字塔切块的宽度分辨率,可选,默认为2048
blockHeight: 金字塔切块的高度分辨率,可选,默认为2048
return {
status: 状态,success-创建成功;fail-创建失败
info: 信息, success-""; fail-工作空间不存在/图层已存在/其他信息
data: {
layer:生成的图层对象,
default_style: 图层的默认样式
}
}
"""
res = {
"status": "fail",
"info": "",
"data": None
}
try:
# 先对tiff进行切片,生成金字塔结构目录
try:
self.createPyramidTiff(tiffPath, tiffDir, levels, blockWidth, blockHeight)
except Exception as e:
res["info"] = "切片错误"
return res
# 再创建金字塔数据图层
return self.__createTiffLayer(workspaceName, layerName, tiffDir, True)
except Exception as e:
res["info"] = repr(e)
return res
def deleteLayer(self, workspaceName, layerName):
"""
删除图层
workspaceName: 图层所在的工作空间的名称
layerName:图层的名称
"""
layer = self.getLayer(workspaceName, layerName)
if layer != None:
self.__cat.delete(layer, None, True)
def __getPointStyle(self, styleParas):
"""
生成点类型的样式xml
styleParas:样式参数 {type:circle/rectangle/star, color:"#000000", transparency:0.5, size:10}
return xml
"""
type = "circle"
if "type" in styleParas:
type = styleParas["type"]
color = "#000000"
if "color" in styleParas:
color = styleParas["color"]
transparency = 0
if "transparency" in styleParas:
transparency = styleParas["transparency"]
size = 1
if "size" in styleParas:
size = styleParas["size"]
style = '<?xml version="1.0" encoding="UTF-8"?>\r\n \
<StyledLayerDescriptor version="1.0.0" \r\n \
xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd" \r\n \
xmlns="http://www.opengis.net/sld" \r\n \
xmlns:ogc="http://www.opengis.net/ogc" \r\n \
xmlns:xlink="http://www.w3.org/1999/xlink" \r\n \
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r\n \
<NamedLayer>\r\n \
<Name>default_point</Name>\r\n \
<UserStyle>\r\n \
<FeatureTypeStyle>\r\n \
<Rule>\r\n \
<PointSymbolizer>\r\n \
<Graphic>\r\n \
<Mark>\r\n \
<WellKnownName>{0}</WellKnownName>\r\n \
<Fill>\r\n \
<CssParameter name="fill">{1}</CssParameter>\r\n \
<CssParameter name="fill-opacity">{2}</CssParameter>\r\n \
</Fill>\r\n \
</Mark>\r\n \
<Size>{3}</Size>\r\n \
</Graphic>\r\n \
</PointSymbolizer>\r\n \
</Rule>\r\n \
</FeatureTypeStyle>\r\n \
</UserStyle>\r\n \
</NamedLayer>\r\n\
</StyledLayerDescriptor>'.format(type, color, 1.0-transparency, size)
return style
def __getPolylineStyle(self, styleParas):
"""
生成线类型的样式xml
styleParas:样式参数 {color:"#000000", width:1}
return xml
"""
color = "#000000"
if "color" in styleParas:
color = styleParas["color"]
width = 1
if "width" in styleParas:
width = styleParas["width"]
style = '<?xml version="1.0" encoding="UTF-8"?>\r\n \
<StyledLayerDescriptor version="1.0.0" \r\n \
xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd" \r\n \
xmlns="http://www.opengis.net/sld" \r\n \
xmlns:ogc="http://www.opengis.net/ogc" \r\n \
xmlns:xlink="http://www.w3.org/1999/xlink" \r\n \
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r\n \
<NamedLayer>\r\n \
<Name>default_line</Name>\r\n \
<UserStyle>\r\n \
<FeatureTypeStyle>\r\n \
<Rule>\r\n \
<LineSymbolizer>\r\n \
<Stroke>\r\n \
<CssParameter name="stroke">{0}</CssParameter>\r\n \
<CssParameter name="stroke-width">{1}</CssParameter>\r\n \
</Stroke>\r\n \
</LineSymbolizer>\r\n \
</Rule>\r\n \
</FeatureTypeStyle>\r\n \
</UserStyle>\r\n \
</NamedLayer>\r\n\
</StyledLayerDescriptor>'.format(color, width)
return style
def __getPolygonStyle(self, styleParas):
"""
生成多边形类型的样式xml
styleParas:样式参数 {fill_color:"#AAAAAA", outline_color:"#000000", outline_width:1}
return xml
"""
fill_color = "#AAAAAA"
if "fill_color" in styleParas:
fill_color = styleParas["fill_color"]
outline_color = "#000000"
if "outline_color" in styleParas:
outline_color = styleParas["outline_color"]
outline_width = 1
if "outline_width" in styleParas:
outline_width = styleParas["outline_width"]
style = '<?xml version="1.0" encoding="UTF-8"?>\r\n \
<StyledLayerDescriptor version="1.0.0" \r\n \
xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd" \r\n \
xmlns="http://www.opengis.net/sld" \r\n \
xmlns:ogc="http://www.opengis.net/ogc" \r\n \
xmlns:xlink="http://www.w3.org/1999/xlink" \r\n \
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\r\n \
<NamedLayer>\r\n \
<Name>default_polygon</Name>\r\n \
<UserStyle>\r\n \
<FeatureTypeStyle>\r\n \
<Rule>\r\n \
<PolygonSymbolizer>\r\n \
<Fill>\r\n \
<CssParameter name="fill">{0}</CssParameter>\r\n \
</Fill>\r\n \
<Stroke>\r\n \
<CssParameter name="stroke">{1}</CssParameter>\r\n \
<CssParameter name="stroke-width">{2}</CssParameter>\r\n \
</Stroke>\r\n \
</PolygonSymbolizer>\r\n \
</Rule>\r\n \
</FeatureTypeStyle>\r\n \
</UserStyle>\r\n \
</NamedLayer>\r\n\
</StyledLayerDescriptor>'.format(fill_color, outline_color, outline_width)
return style
def getStyle(self, workspaceName, styleName):
"""
通过名称获取样式
workspaceName: 样式所在的工作空间的名称
styleName:样式的名称
return Style
"""
return self.__cat.get_style(styleName, workspaceName)
def isStyleExist(self, workspaceName, styleName):
"""
判断样式是否存在
workspaceName: 样式所在的工作空间的名称
styleName:样式的名称
return True-存在;False-不存在
"""
res = self.getStyle(workspaceName, styleName)
if res == None:
return False
else:
return True
def createStyle(self, workspaceName, styleName, styleType, styleParas):
"""
创建样式
workspaceName: 样式所在的工作空间的名称
styleName:样式的名称
styleType:样式的类型 point/polyline/line/polygon
styleParas: 样式的参数,
point-{type:circle/rectangle/star, color:"#000000", transparency:0.5, size:10}
polyline/line-{color:"#000000", width:1}
polygon-{fill_color:"#AAAAAA", outline_color:"#000000", outline_width:1}
return {
status: 状态,success-创建成功;fail-创建失败
info: 信息, success-""; fail-不支持的样式类型
data: Style
}
"""
res = {
"status": "fail",
"info": "",
"data": None
}
styleData = None
if styleType == "point":
styleData = self.__getPointStyle(styleParas)
elif styleType == "polyline" or styleType == "line":
styleData = self.__getPolylineStyle(styleParas)
elif styleType == "polygon":
styleData = self.__getPolygonStyle(styleParas)
else:
res["info"] = "不支持的样式类型"
return res
style = self.__cat.create_style(styleName, styleData, overwrite=True, workspace=workspaceName)
res["status"] = "success"
res["data"] = style
return res
def deleteStyle(self, workspaceName, styleName):
"""
删除样式
workspaceName: 样式所在的工作空间的名称
styleName:样式的名称
"""
style = self.getStyle(workspaceName, styleName)
if style != None:
self.__cat.delete(style, None, True)
def updateStyle(self, workspaceName, styleName, styleType, styleParas):
"""
更新样式
workspaceName: 样式所在的工作空间的名称
styleName:样式的名称
styleType:样式的类型 point/polyline/line/polygon
styleParas: 样式的参数,
point-{type:circle/rectangle/star, color:"#000000", transparency:0.5, size:10}
polyline/line-{color:"#000000", width:1}
polygon-{fill_color:"#AAAAAA", outline_color:"#000000", outline_width:1}
return {
status: 状态,success-创建成功;fail-创建失败
info: 信息, success-""; fail-样式不存在/不支持的样式类型
data: Style
}
"""
res = {
"status": "fail",
"info": "",
"data": None
}
if not self.isStyleExist(workspaceName, styleName):
res["info"] = "样式不存在:{0}".format(styleName)
return res
# 获取样式
style = self.getStyle(workspaceName, styleName)
if styleType == "point":
data = self.__getPointStyle(styleParas)
elif styleType == "polyline" or styleType == "line":
data = self.__getPolylineStyle(styleParas)
elif styleType == "polygon":
data = self.__getPolygonStyle(styleParas)
else:
res["info"] = "不支持的样式类型"
return res
style.update_body(data)
res["status"] = "success"
return res
def getWorkSpaces(self):
return self.__cat.get_workspaces()
五、案例
在此提供3
个demo
程序,分别是发布shp
、发布普通tiff
、发布大型tiff
,创建成功后可通过geoserver
的web
应用程序进行验证
5.1 发布shp
# demo_publish_shp.py 演示案例:使用GeoServerService发布SHP服务
from GeoServerService import GeoServerService
# 初始化服务
url = "http://localhost:8080/geoserver/rest" # geoserver url
username = "admin" # geoserver username
password = "geoserver" # geoserver password
service = GeoServerService(url, username, password)
# 准备参数
workspaceName = "test" # geoserver 工作空间名称
layerName = "shp_layer" # geoserver 图层名称
shapePath = "" # 用于发布的shp文件路径,精确到文件,不带后缀
charset = "utf-8" # dbf文件的编码格式,不影响发布,主要影响发布后的属性拾取,如果编码格式不对,拾取到的中文属性会乱码
# 检查工作空间是否存在,如果不存在,则创建
if not service.isWorkspaceExist(workspaceName):
service.createWorkspace(workspaceName)
# 检查图层是否已存在,如果存在,则删除
if service.isStoreExist(workspaceName, layerName):
service.deleteStore(workspaceName, layerName)
# 发布图层
res = service.createShapeLayer(workspaceName, layerName, shapePath, charset)
print(res)
5.2 发布普通tiff
# demo_publish_tiff.py 演示案例:使用GeoServerService发布普通TIFF(<2GB)服务
from GeoServerService import GeoServerService
# 初始化服务
url = "http://localhost:8080/geoserver/rest" # geoserver url
username = "admin" # geoserver username
password = "geoserver" # geoserver password
service = GeoServerService(url, username, password)
# 准备参数
workspaceName = "test" # geoserver 工作空间名称
layerName = "tiff_layer" # geoserver 图层名称
tiffPath = "" # 用于发布的TIFF文件路径
# 检查工作空间是否存在,如果不存在,则创建
if not service.isWorkspaceExist(workspaceName):
service.createWorkspace(workspaceName)
# 检查图层是否已存在,如果存在,则删除
if service.isStoreExist(workspaceName, layerName):
service.deleteStore(workspaceName, layerName)
# 发布图层
res = service.createTiffLayer(workspaceName, layerName, tiffPath)
print(res)
5.3 发布大型tiff
# demo_publish_pyramid_tiff.py 演示案例:使用GeoServerService发布金字塔切片的TIFF(>=2GB)服务
import os
import shutil
from GeoServerService import GeoServerService
# 初始化服务
url = "http://localhost:8080/geoserver/rest" # geoserver url
username = "admin" # geoserver username
password = "geoserver" # geoserver password
service = GeoServerService(url, username, password)
# 准备参数
workspaceName = "test" # geoserver 工作空间名称
layerName = "pyramid_tiff_layer" # geoserver 图层名称
tiffPath = "" # 用于发布的TIFF文件路径
tiffDir = "" # 切片后生成的TIFF金字塔文件夹路径
levels = 4 # 金字塔层级
blockWidth = 2048 # 金字塔每个块的宽度
blockHeight = 2048 # 金字塔每个块的高度
# 检查待生成的文件夹是否存在,如果存在则清空,否则创建
if os.path.isdir(tiffDir):
shutil.rmtree(tiffDir)
os.mkdir(tiffDir)
# 检查工作空间是否存在,如果不存在,则创建
if not service.isWorkspaceExist(workspaceName):
service.createWorkspace(workspaceName)
# 检查图层是否已存在,如果存在,则删除
if service.isStoreExist(workspaceName, layerName):
service.deleteStore(workspaceName, layerName)
# 发布图层
res = service.createPyramidTiffLayer(workspaceName, layerName, tiffPath, tiffDir, levels, blockWidth, blockHeight)
print(res)