提交 78763c77c9a924f1661922fcee3f4a277e3520d6

作者 nheweijun
1 个父辈 d40d7c2a

2022.02.22 增加简单发布服务接口

@@ -6,10 +6,11 @@ @@ -6,10 +6,11 @@
6 from flasgger import swag_from 6 from flasgger import swag_from
7 from flask import Blueprint 7 from flask import Blueprint
8 from app.util import BlueprintApi 8 from app.util import BlueprintApi
9 -from . import map_service_register, map_service_edit 9 +from . import map_service_register, map_service_edit, map_service_sample_register
10 from app.decorators.token_decorator import token_decorator 10 from app.decorators.token_decorator import token_decorator
11 from ..util.ServiceType import ServiceType 11 from ..util.ServiceType import ServiceType
12 12
  13 +
13 class DataManager(BlueprintApi): 14 class DataManager(BlueprintApi):
14 15
15 bp = Blueprint("MapService", __name__, 16 bp = Blueprint("MapService", __name__,
@@ -27,6 +28,15 @@ class DataManager(BlueprintApi): @@ -27,6 +28,15 @@ class DataManager(BlueprintApi):
27 return map_service_register.Api().result 28 return map_service_register.Api().result
28 29
29 @staticmethod 30 @staticmethod
  31 + @bp.route('/SampleRegister', methods=['POST'])
  32 + @swag_from(map_service_sample_register.Api.api_doc)
  33 + def api_wms_sample_register():
  34 + """
  35 + 注册简单MapService
  36 + """
  37 + return map_service_sample_register.Api().result
  38 +
  39 + @staticmethod
30 @bp.route('/Edit', methods=['POST']) 40 @bp.route('/Edit', methods=['POST'])
31 @swag_from(map_service_edit.Api.api_doc) 41 @swag_from(map_service_edit.Api.api_doc)
32 @token_decorator("profile") 42 @token_decorator("profile")
@@ -47,7 +47,8 @@ class Api(ApiTemplate): @@ -47,7 +47,8 @@ class Api(ApiTemplate):
47 else: 47 else:
48 raise Exception("调用矢量服务的注册服务接口失败!") 48 raise Exception("调用矢量服务的注册服务接口失败!")
49 # 并获得服务缩略图 49 # 并获得服务缩略图
50 - 50 + if Service.query.filter_by(name = self.para.get("name")).one_or_none():
  51 + raise Exception("同名服务已存在!")
51 service = Service( 52 service = Service(
52 guid = service_guid, 53 guid = service_guid,
53 name = self.para.get("name"), 54 name = self.para.get("name"),
  1 +# coding=utf-8
  2 +#author: 4N
  3 +#createtime: 2021/9/17
  4 +#email: nheweijun@sina.com
  5 +
  6 +
  7 +from app.util.component.ApiTemplate import ApiTemplate
  8 +from app.util.component.PGUtil import PGUtil
  9 +from app.util.component.GeometryAdapter import GeometryAdapter
  10 +import uuid
  11 +from ..models import Service,db,MapService,ServiceFunction
  12 +from app.modules.data.models import Database,Table,DES,Columns
  13 +import datetime
  14 +import configure
  15 +import requests
  16 +import json
  17 +from osgeo.ogr import DataSource,Layer,FeatureDefn,FieldDefn
  18 +import base64
  19 +
  20 +
  21 +class Api(ApiTemplate):
  22 +
  23 + api_name = "注册MapService服务"
  24 +
  25 + def para_check(self):
  26 + if not self.para.get("name"):
  27 + raise Exception("缺乏服务名!")
  28 + if not self.para.get("title"):
  29 + raise Exception("缺乏标题!")
  30 + if not self.para.get("database_guid"):
  31 + raise Exception("缺乏数据库id!")
  32 + if not self.para.get("table_name"):
  33 + raise Exception("缺乏表名!")
  34 +
  35 + def process(self):
  36 + # 返回结果
  37 + res = {}
  38 +
  39 + try:
  40 +
  41 + this_time = datetime.datetime.now()
  42 + service_guid = uuid.uuid1().__str__()
  43 + map_service_guid = uuid.uuid1().__str__()
  44 +
  45 + # 逻辑是,先调用引擎接口,引擎说可以,才做持久化
  46 + project_file = self.create_projectfile(self.para)
  47 + para = {"name":self.para.get("name"),
  48 + "title":self.para.get("title"),
  49 + "type":"mapserver",
  50 + "capabilities":2,
  51 + "project":project_file}
  52 +
  53 +
  54 + map_service_register_url = "{}/dmap/api/manager/regservice".format(configure.dmap_engine)
  55 + resp: requests.Response = requests.post(map_service_register_url,data=json.dumps(para),
  56 + headers={'Content-Type':'application/json'},timeout=3
  57 + )
  58 +
  59 + if resp.status_code == 200:
  60 + resp.encoding="utf-8"
  61 + resp_json = resp.json()
  62 + if not resp_json["status"]=="1":
  63 + raise Exception("调用矢量服务的注册服务接口失败!")
  64 + else:
  65 + raise Exception("调用矢量服务的注册服务接口失败!")
  66 + # 并获得服务缩略图
  67 +
  68 + if Service.query.filter_by(name = self.para.get("name")).one_or_none():
  69 + raise Exception("同名服务已存在!")
  70 +
  71 + service = Service(
  72 + guid = service_guid,
  73 + name = self.para.get("name"),
  74 + title = self.para.get("title"),
  75 + creator=self.para.get("creator"),
  76 + state = 1,
  77 + create_time = this_time,
  78 + update_time = this_time,
  79 + description = self.para.get("description"),
  80 + node = 1 ,
  81 + overview = resp_json["url"],
  82 + type = "矢量地图",
  83 + catalog_guid = self.para.get("catalog_guid")
  84 + )
  85 +
  86 + map_service = MapService(
  87 + guid = map_service_guid,
  88 + name = self.para.get("name"),
  89 + title = self.para.get("title"),
  90 + type = "mapserver",
  91 + capabilities = int(self.para.get("capabilities",2)),
  92 + project = project_file,
  93 + service_guid = service_guid
  94 + )
  95 +
  96 + service_function_wms = ServiceFunction(guid=uuid.uuid1().__str__(),type="WMS",service_guid=service_guid)
  97 + service_function_wfs = ServiceFunction(guid=uuid.uuid1().__str__(),type="WFS",service_guid=service_guid)
  98 +
  99 + db.session.add(service)
  100 + db.session.add(map_service)
  101 + db.session.add(service_function_wms)
  102 + db.session.add(service_function_wfs)
  103 + db.session.commit()
  104 +
  105 + res["data"] = service_guid
  106 + res["result"] = True
  107 + except Exception as e:
  108 + db.session.rollback()
  109 + raise e
  110 + return res
  111 +
  112 +
  113 + def create_projectfile(self,para):
  114 +
  115 + database = Database.query.filter_by(guid=para.get("database_guid")).one_or_none()
  116 + if not database:
  117 + raise Exception("数据库不存在,请先注册数据库!")
  118 + pg_ds: DataSource = PGUtil.open_pg_data_source(0, DES.decode(database.sqlalchemy_uri))
  119 + table = Table.query.filter_by(database_guid=database.guid,name=self.para.get("table_name")).first()
  120 + layer: Layer = pg_ds.GetLayerByName(self.para.get("table_name"))
  121 + #注册数据表
  122 + if not table:
  123 + if not layer:
  124 + raise Exception("图层不存在!")
  125 + table = self.register_table(layer,para)
  126 +
  127 + gemetry_type = ["MULTIPOINT","MULTILINESTRING","MULTIPOLYGON"][table.table_type-1]
  128 +
  129 + geom_column = layer.GetGeometryColumn()
  130 + srid = PGUtil.get_srid(pg_ds,self.para.get("table_name"))
  131 + if not table.table_type in [1,2,3]:
  132 + raise Exception("数据表是非空间表!")
  133 +
  134 + xml_format='''
  135 +<dmap projectname="{name}" version="">
  136 + <projectCrs>
  137 + <spatialrefsys>
  138 + <wkt/>
  139 + <proj4/>
  140 + <srid>0</srid>
  141 + </spatialrefsys>
  142 + </projectCrs>
  143 + <projectlayers>
  144 + <maplayer alias="" alwaysShow="false" featurecount="{feature_count}" geometry="{gemetry_type}" geomfield="{geom_column}" id="{table_guid}" name="{table_name}" schema="public" srid="{srid}" tag="" type="1" visible="true" workspace="">
  145 + <extent>
  146 + <xmin>{xmin}</xmin>
  147 + <ymin>{ymin}</ymin>
  148 + <xmax>{xmax}</xmax>
  149 + <ymax>{ymax}</ymax>
  150 + </extent>
  151 + <datasource>{database_guid}</datasource>
  152 + {renderer}
  153 + </maplayer>
  154 + </projectlayers>
  155 +</dmap>
  156 + '''
  157 + point_renderer='''
  158 + <renderer>
  159 + <GROUPRENDERER>
  160 + <SIMPLERENDERER alwaysShow="false" maxscale="10000000000" minscale="0">
  161 + <SIMPLEMARKERSYMBOL png="" type="circle" color="255,255,255" transparency="1" boundary="true" outline="0,0,0" outlinetransparency="1" width="1.5" size="10.00" />
  162 + </SIMPLERENDERER>
  163 + </GROUPRENDERER>
  164 + </renderer>
  165 + '''
  166 + line_renderer = '''
  167 + <renderer>
  168 + <GROUPRENDERER>
  169 + <SIMPLERENDERER alwaysShow="true" maxscale="10000000000" minscale="0">
  170 + <SIMPLELINESYMBOL type="solid" png="" color="0,0,0" transparency="1.00" width="2.00" captype="cap_butt" jiontype="jion_miter" />
  171 + </SIMPLERENDERER>
  172 + </GROUPRENDERER>
  173 + </renderer>
  174 + '''
  175 +
  176 + polygon_renderer='''
  177 + <renderer>
  178 + <GROUPRENDERER>
  179 + <SIMPLERENDERER alwaysShow="true" maxscale="10000000000" minscale="0">
  180 + <SIMPLEPOLYGONSYMBOL png="" boundary="true" linetype="solid" width="0.5" boundarycolor="0,0,0" boundarytransparency="1.00" needfill="false" filltype="solid" filltransparency="1.00" fillinterval="1" font="" fontname="" fillsize="0" fillcolor="0,0,0" icon="" needbackground="true" diagalignment="true" backgroundcolor="255,255,255" backgroundtransparency="1.00" />
  181 + </SIMPLERENDERER>
  182 + </GROUPRENDERER>
  183 + </renderer>
  184 + '''
  185 +
  186 + renderer = point_renderer if table.table_type==1 else line_renderer
  187 + renderer = renderer if table.table_type == 2 else polygon_renderer
  188 + extent = [float(x) for x in table.extent.split(",")]
  189 +
  190 + xml = xml_format.format(name = para.get("name"),
  191 + xmin = extent[0],
  192 + xmax = extent[1],
  193 + ymin = extent[2],
  194 + ymax = extent[3],
  195 + feature_count = table.feature_count,
  196 + database_guid = table.database_guid,
  197 + gemetry_type = gemetry_type,
  198 + geom_column = geom_column,
  199 + table_guid = table.guid,
  200 + table_name = table.name,
  201 + srid = srid,
  202 + renderer= renderer
  203 + )
  204 +
  205 +
  206 + if pg_ds:
  207 + pg_ds.Destroy()
  208 +
  209 + project_xml = xml.strip()
  210 + return str(base64.b64encode(project_xml.encode('utf-8')), encoding="utf8")
  211 +
  212 + def register_table(self, layer: Layer,para):
  213 + '''
  214 + 注册表
  215 + :param layer: 图层
  216 + :param new_layer_name: 图层名
  217 + :return: 表名
  218 + '''
  219 +
  220 + table_guid = uuid.uuid1().__str__()
  221 + this_time = datetime.datetime.now()
  222 + ext = layer.GetExtent()
  223 + if ext[0] < 360:
  224 + ext = [round(e, 6) for e in ext]
  225 + else:
  226 + ext = [round(e, 2) for e in ext]
  227 +
  228 + geom_type = GeometryAdapter.get_geometry_type(layer)
  229 +
  230 + extent = "{},{},{},{}".format(ext[0], ext[1], ext[2], ext[3])
  231 +
  232 + table = Table(guid=table_guid,
  233 + database_guid=para.get("database_guid"),
  234 + name=layer.GetName(),
  235 + create_time=this_time, update_time=this_time,
  236 + table_type=GeometryAdapter.get_table_type(geom_type),
  237 + extent=extent,
  238 + feature_count=layer.GetFeatureCount()
  239 + )
  240 + # 删除遗留业务数据
  241 + history_table = Table.query.filter_by(name=layer.GetName(),database_guid=para.get("database_guid")).all()
  242 +
  243 + if history_table:
  244 + for ht in history_table:
  245 + db.session.delete(ht)
  246 + db.session.add(table)
  247 +
  248 + feature_defn: FeatureDefn = layer.GetLayerDefn()
  249 +
  250 + for i in range(feature_defn.GetFieldCount()):
  251 +
  252 + field_defn: FieldDefn = feature_defn.GetFieldDefn(i)
  253 + field_name = field_defn.GetName()
  254 +
  255 + field_alias = field_name if field_defn.GetAlternativeName() is None or field_defn.GetAlternativeName().__eq__(
  256 + "") else field_defn.GetAlternativeName()
  257 +
  258 + column = Columns(guid=uuid.uuid1().__str__(), table_guid=table_guid,
  259 + name=field_name, alias=field_alias, create_time=this_time, update_time=this_time)
  260 + db.session.add(column)
  261 + db.session.commit()
  262 +
  263 + return table
  264 +
  265 +
  266 +
  267 +
  268 + api_doc = {
  269 + "tags": ["矢量地图接口"],
  270 + "parameters": [
  271 +
  272 + {"name": "name",
  273 + "in": "formData",
  274 + "type": "string",
  275 + "required": "true"},
  276 +
  277 + {"name": "title",
  278 + "in": "formData",
  279 + "type": "string",
  280 + "required": "true"},
  281 +
  282 + {"name": "database_guid",
  283 + "in": "formData",
  284 + "type": "string",
  285 + "description": "DMapServer中数据库的guid",
  286 + "required": "true"},
  287 +
  288 + {"name": "table_name",
  289 + "in": "formData",
  290 + "type": "string",
  291 + "description": "表名",
  292 + "required": "true"},
  293 + ],
  294 + "responses": {
  295 + 200: {
  296 + "schema": {
  297 + "properties": {
  298 + }
  299 + }
  300 + }
  301 + }
  302 + }
@@ -49,6 +49,9 @@ class Api(ApiTemplate): @@ -49,6 +49,9 @@ class Api(ApiTemplate):
49 else: 49 else:
50 raise Exception("调用电子地图的注册服务接口失败!") 50 raise Exception("调用电子地图的注册服务接口失败!")
51 51
  52 + if Service.query.filter_by(name = self.para.get("name")).one_or_none():
  53 + raise Exception("同名服务已存在!")
  54 +
52 service = Service( 55 service = Service(
53 guid = service_guid, 56 guid = service_guid,
54 name = self.para.get("name"), 57 name = self.para.get("name"),
@@ -184,6 +184,7 @@ class GeometryAdapter: @@ -184,6 +184,7 @@ class GeometryAdapter:
184 layer.ResetReading() 184 layer.ResetReading()
185 return geom_type 185 return geom_type
186 186
  187 +
187 @classmethod 188 @classmethod
188 def convert(cls, geometry: Geometry, bbox, xy_res): 189 def convert(cls, geometry: Geometry, bbox, xy_res):
189 ''' 190 '''
@@ -122,6 +122,21 @@ class PGUtil: @@ -122,6 +122,21 @@ class PGUtil:
122 122
123 123
124 @classmethod 124 @classmethod
  125 + def get_srid(cls,pg_ds,table_name):
  126 + layer = pg_ds.GetLayerByName(table_name)
  127 + if not layer:
  128 + return None
  129 + srid_sql = '''select st_srid({}) from public."{}" limit 1'''.format(layer.GetGeometryColumn(), layer.GetName())
  130 + srid_layer = pg_ds.ExecuteSQL(srid_sql)
  131 + srid_feature = srid_layer.GetNextFeature()
  132 + if srid_feature:
  133 + if srid_feature.GetField(0):
  134 + return int(srid_feature.GetField(0))
  135 + else:
  136 + return None
  137 +
  138 +
  139 + @classmethod
125 def check_database_privilege(cls,table_name,pri_type,user,session): 140 def check_database_privilege(cls,table_name,pri_type,user,session):
126 pass 141 pass
127 142
@@ -42,7 +42,6 @@ class SQLUtil: @@ -42,7 +42,6 @@ class SQLUtil:
42 return False 42 return False
43 43
44 44
45 -  
46 @classmethod 45 @classmethod
47 def check_database_privilege(cls,table_name,pri_type,user,session): 46 def check_database_privilege(cls,table_name,pri_type,user,session):
48 pass 47 pass
注册登录 后发表评论