正在显示
26 个修改的文件
包含
1427 行增加
和
234 行删除
| ... | ... | @@ -3,7 +3,7 @@ import base64 |
| 3 | 3 | import os |
| 4 | 4 | from flask_sqlalchemy import SQLAlchemy,Model |
| 5 | 5 | import importlib |
| 6 | -from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, Time,Float | |
| 6 | +from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, Time,Float,Binary | |
| 7 | 7 | from sqlalchemy.orm import relationship |
| 8 | 8 | import base64 |
| 9 | 9 | from pyDes import * |
| ... | ... | @@ -198,3 +198,22 @@ class InsertingLayerName(db.Model): |
| 198 | 198 | task_guid = Column(String(256)) |
| 199 | 199 | name = Column(String(256)) |
| 200 | 200 | |
| 201 | + | |
| 202 | +# class Service(db.Model): | |
| 203 | +# ''' | |
| 204 | +# ''' | |
| 205 | +# __tablename__ = 'dmdms_service' | |
| 206 | +# guid = Column(String(256), primary_key=True) | |
| 207 | +# name=Column(String(256)) | |
| 208 | +# alias = Column(String(256)) | |
| 209 | +# #服务状态 | |
| 210 | +# state= Column(Integer) | |
| 211 | +# create_time = Column(DateTime) | |
| 212 | +# update_time = Column(DateTime) | |
| 213 | +# #服务描述 | |
| 214 | +# description = Column(Text) | |
| 215 | +# #服务节点 | |
| 216 | +# node = Column(Integer) | |
| 217 | +# #服务缩略图 | |
| 218 | +# overview = Column(Binary) | |
| 219 | + | ... | ... |
| ... | ... | @@ -146,34 +146,35 @@ class Api(ApiTemplate): |
| 146 | 146 | table_guid = uuid.uuid1().__str__() |
| 147 | 147 | |
| 148 | 148 | geom_type = GeometryAdapter.get_geometry_type(layer) |
| 149 | + | |
| 150 | + | |
| 151 | + table = Table(guid=table_guid, | |
| 152 | + database_guid=database.guid, | |
| 153 | + # alias=layer.GetName(), | |
| 154 | + name=layer.GetName(), create_time=this_time, update_time=this_time, | |
| 155 | + table_type=GeometryAdapter.get_table_type(geom_type), | |
| 156 | + extent=extent, | |
| 157 | + feature_count=feature_count | |
| 158 | + ) | |
| 159 | + | |
| 160 | + db.session.add(table) | |
| 161 | + tables.append(table) | |
| 162 | + | |
| 163 | + feature_defn: FeatureDefn = layer.GetLayerDefn() | |
| 164 | + | |
| 165 | + for i in range(feature_defn.GetFieldCount()): | |
| 166 | + field_defn: FieldDefn = feature_defn.GetFieldDefn(i) | |
| 167 | + field_name = field_defn.GetName().lower() | |
| 168 | + field_alias = field_name if field_defn.GetAlternativeName() is None or field_defn.GetAlternativeName().__eq__( | |
| 169 | + "") else field_defn.GetAlternativeName() | |
| 170 | + column = Columns(guid=uuid.uuid1().__str__(), table_guid=table_guid, | |
| 171 | + name=field_name, alias=field_alias, create_time=this_time, update_time=this_time) | |
| 172 | + db.session.add(column) | |
| 173 | + | |
| 174 | + spatial_table_name.append(layer.GetName()) | |
| 149 | 175 | except: |
| 150 | 176 | StructurePrint.print("表{}注册失败!".format(l_name), "warn") |
| 151 | 177 | continue |
| 152 | - | |
| 153 | - table = Table(guid=table_guid, | |
| 154 | - database_guid=database.guid, | |
| 155 | - # alias=layer.GetName(), | |
| 156 | - name=layer.GetName(), create_time=this_time, update_time=this_time, | |
| 157 | - table_type=GeometryAdapter.get_table_type(geom_type), | |
| 158 | - extent=extent, | |
| 159 | - feature_count=feature_count | |
| 160 | - ) | |
| 161 | - | |
| 162 | - db.session.add(table) | |
| 163 | - tables.append(table) | |
| 164 | - | |
| 165 | - feature_defn: FeatureDefn = layer.GetLayerDefn() | |
| 166 | - | |
| 167 | - for i in range(feature_defn.GetFieldCount()): | |
| 168 | - field_defn: FieldDefn = feature_defn.GetFieldDefn(i) | |
| 169 | - field_name = field_defn.GetName().lower() | |
| 170 | - field_alias = field_name if field_defn.GetAlternativeName() is None or field_defn.GetAlternativeName().__eq__( | |
| 171 | - "") else field_defn.GetAlternativeName() | |
| 172 | - column = Columns(guid=uuid.uuid1().__str__(), table_guid=table_guid, | |
| 173 | - name=field_name, alias=field_alias, create_time=this_time, update_time=this_time) | |
| 174 | - db.session.add(column) | |
| 175 | - | |
| 176 | - spatial_table_name.append(layer.GetName()) | |
| 177 | 178 | return spatial_table_name,tables |
| 178 | 179 | |
| 179 | 180 | ... | ... |
| ... | ... | @@ -24,7 +24,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == '--help': |
| 24 | 24 | print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] [-novalidate] [-ca_certs certs] [-keyfile keyfile] [-certfile certfile] function [arg1 [arg2...]]') |
| 25 | 25 | print('') |
| 26 | 26 | print('Functions:') |
| 27 | - print(' string getData(string path, queryRange, originRange, bands)') | |
| 27 | + print(' string getData(string path, queryRange, originRange, bands, i32 width, i32 height)') | |
| 28 | 28 | print(' string getInfo(string path)') |
| 29 | 29 | print(' bool buildOverview(string path)') |
| 30 | 30 | print(' string getImageList(string path)') |
| ... | ... | @@ -108,10 +108,10 @@ client = ImageDataService.Client(protocol) |
| 108 | 108 | transport.open() |
| 109 | 109 | |
| 110 | 110 | if cmd == 'getData': |
| 111 | - if len(args) != 4: | |
| 112 | - print('getData requires 4 args') | |
| 111 | + if len(args) != 6: | |
| 112 | + print('getData requires 6 args') | |
| 113 | 113 | sys.exit(1) |
| 114 | - pp.pprint(client.getData(args[0], eval(args[1]), eval(args[2]), eval(args[3]),)) | |
| 114 | + pp.pprint(client.getData(args[0], eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]),)) | |
| 115 | 115 | |
| 116 | 116 | elif cmd == 'getInfo': |
| 117 | 117 | if len(args) != 1: | ... | ... |
| ... | ... | @@ -19,13 +19,15 @@ all_structs = [] |
| 19 | 19 | |
| 20 | 20 | |
| 21 | 21 | class Iface(object): |
| 22 | - def getData(self, path, queryRange, originRange, bands): | |
| 22 | + def getData(self, path, queryRange, originRange, bands, width, height): | |
| 23 | 23 | """ |
| 24 | 24 | Parameters: |
| 25 | 25 | - path |
| 26 | 26 | - queryRange |
| 27 | 27 | - originRange |
| 28 | 28 | - bands |
| 29 | + - width | |
| 30 | + - height | |
| 29 | 31 | |
| 30 | 32 | """ |
| 31 | 33 | pass |
| ... | ... | @@ -62,25 +64,29 @@ class Client(Iface): |
| 62 | 64 | self._oprot = oprot |
| 63 | 65 | self._seqid = 0 |
| 64 | 66 | |
| 65 | - def getData(self, path, queryRange, originRange, bands): | |
| 67 | + def getData(self, path, queryRange, originRange, bands, width, height): | |
| 66 | 68 | """ |
| 67 | 69 | Parameters: |
| 68 | 70 | - path |
| 69 | 71 | - queryRange |
| 70 | 72 | - originRange |
| 71 | 73 | - bands |
| 74 | + - width | |
| 75 | + - height | |
| 72 | 76 | |
| 73 | 77 | """ |
| 74 | - self.send_getData(path, queryRange, originRange, bands) | |
| 78 | + self.send_getData(path, queryRange, originRange, bands, width, height) | |
| 75 | 79 | return self.recv_getData() |
| 76 | 80 | |
| 77 | - def send_getData(self, path, queryRange, originRange, bands): | |
| 81 | + def send_getData(self, path, queryRange, originRange, bands, width, height): | |
| 78 | 82 | self._oprot.writeMessageBegin('getData', TMessageType.CALL, self._seqid) |
| 79 | 83 | args = getData_args() |
| 80 | 84 | args.path = path |
| 81 | 85 | args.queryRange = queryRange |
| 82 | 86 | args.originRange = originRange |
| 83 | 87 | args.bands = bands |
| 88 | + args.width = width | |
| 89 | + args.height = height | |
| 84 | 90 | args.write(self._oprot) |
| 85 | 91 | self._oprot.writeMessageEnd() |
| 86 | 92 | self._oprot.trans.flush() |
| ... | ... | @@ -233,7 +239,7 @@ class Processor(Iface, TProcessor): |
| 233 | 239 | iprot.readMessageEnd() |
| 234 | 240 | result = getData_result() |
| 235 | 241 | try: |
| 236 | - result.success = self._handler.getData(args.path, args.queryRange, args.originRange, args.bands) | |
| 242 | + result.success = self._handler.getData(args.path, args.queryRange, args.originRange, args.bands, args.width, args.height) | |
| 237 | 243 | msg_type = TMessageType.REPLY |
| 238 | 244 | except TTransport.TTransportException: |
| 239 | 245 | raise |
| ... | ... | @@ -329,15 +335,19 @@ class getData_args(object): |
| 329 | 335 | - queryRange |
| 330 | 336 | - originRange |
| 331 | 337 | - bands |
| 338 | + - width | |
| 339 | + - height | |
| 332 | 340 | |
| 333 | 341 | """ |
| 334 | 342 | |
| 335 | 343 | |
| 336 | - def __init__(self, path=None, queryRange=None, originRange=None, bands=None,): | |
| 344 | + def __init__(self, path=None, queryRange=None, originRange=None, bands=None, width=None, height=None,): | |
| 337 | 345 | self.path = path |
| 338 | 346 | self.queryRange = queryRange |
| 339 | 347 | self.originRange = originRange |
| 340 | 348 | self.bands = bands |
| 349 | + self.width = width | |
| 350 | + self.height = height | |
| 341 | 351 | |
| 342 | 352 | def read(self, iprot): |
| 343 | 353 | if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: |
| ... | ... | @@ -383,6 +393,16 @@ class getData_args(object): |
| 383 | 393 | iprot.readListEnd() |
| 384 | 394 | else: |
| 385 | 395 | iprot.skip(ftype) |
| 396 | + elif fid == 5: | |
| 397 | + if ftype == TType.I32: | |
| 398 | + self.width = iprot.readI32() | |
| 399 | + else: | |
| 400 | + iprot.skip(ftype) | |
| 401 | + elif fid == 6: | |
| 402 | + if ftype == TType.I32: | |
| 403 | + self.height = iprot.readI32() | |
| 404 | + else: | |
| 405 | + iprot.skip(ftype) | |
| 386 | 406 | else: |
| 387 | 407 | iprot.skip(ftype) |
| 388 | 408 | iprot.readFieldEnd() |
| ... | ... | @@ -418,6 +438,14 @@ class getData_args(object): |
| 418 | 438 | oprot.writeI32(iter20) |
| 419 | 439 | oprot.writeListEnd() |
| 420 | 440 | oprot.writeFieldEnd() |
| 441 | + if self.width is not None: | |
| 442 | + oprot.writeFieldBegin('width', TType.I32, 5) | |
| 443 | + oprot.writeI32(self.width) | |
| 444 | + oprot.writeFieldEnd() | |
| 445 | + if self.height is not None: | |
| 446 | + oprot.writeFieldBegin('height', TType.I32, 6) | |
| 447 | + oprot.writeI32(self.height) | |
| 448 | + oprot.writeFieldEnd() | |
| 421 | 449 | oprot.writeFieldStop() |
| 422 | 450 | oprot.writeStructEnd() |
| 423 | 451 | |
| ... | ... | @@ -441,6 +469,8 @@ getData_args.thrift_spec = ( |
| 441 | 469 | (2, TType.LIST, 'queryRange', (TType.DOUBLE, None, False), None, ), # 2 |
| 442 | 470 | (3, TType.LIST, 'originRange', (TType.DOUBLE, None, False), None, ), # 3 |
| 443 | 471 | (4, TType.LIST, 'bands', (TType.I32, None, False), None, ), # 4 |
| 472 | + (5, TType.I32, 'width', None, None, ), # 5 | |
| 473 | + (6, TType.I32, 'height', None, None, ), # 6 | |
| 444 | 474 | ) |
| 445 | 475 | |
| 446 | 476 | ... | ... |
| ... | ... | @@ -15,7 +15,7 @@ from . import data_list |
| 15 | 15 | from . import capabilities |
| 16 | 16 | from . import image_list |
| 17 | 17 | from . import image_tile |
| 18 | - | |
| 18 | +from . import image_wms | |
| 19 | 19 | |
| 20 | 20 | class DataManager(BlueprintApi): |
| 21 | 21 | |
| ... | ... | @@ -101,4 +101,15 @@ class DataManager(BlueprintApi): |
| 101 | 101 | """ |
| 102 | 102 | 切片服务 |
| 103 | 103 | """ |
| 104 | - return image_tile.Api("1",1,1,1).result | |
| \ No newline at end of file | ||
| 104 | + return image_tile.Api("1",1,1,1).result | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + @staticmethod | |
| 109 | + @bp.route('/WMS', methods=['GET','POST']) | |
| 110 | + @swag_from(image_wms.Api.api_doc) | |
| 111 | + def image_wms(): | |
| 112 | + """ | |
| 113 | + WMS服务 | |
| 114 | + """ | |
| 115 | + return image_wms.Api().result | |
| \ No newline at end of file | ... | ... |
| ... | ... | @@ -12,7 +12,7 @@ import json |
| 12 | 12 | from app.modules.image.ImageDataService import ImageDataService |
| 13 | 13 | from app.util.component.FileProcess import FileProcess |
| 14 | 14 | import datetime |
| 15 | - | |
| 15 | +from app.util.component.ThriftConnect import ThriftConnect | |
| 16 | 16 | import os |
| 17 | 17 | class Api(ApiTemplate): |
| 18 | 18 | |
| ... | ... | @@ -56,20 +56,14 @@ class Api(ApiTemplate): |
| 56 | 56 | elif os.path.isdir(file_path): |
| 57 | 57 | file_info["type"] = "dir" |
| 58 | 58 | data_list.append(file_info) |
| 59 | + | |
| 59 | 60 | data_list_sorted = sorted(data_list, key=lambda x: x["name"]) |
| 60 | 61 | res["data"] = data_list_sorted |
| 61 | 62 | |
| 62 | 63 | else: |
| 63 | - host = data_server.split(":")[0] | |
| 64 | - port = int(data_server.split(":")[1]) | |
| 65 | - | |
| 66 | - transport = TSocket.TSocket(host, port) | |
| 67 | - transport = TTransport.TBufferedTransport(transport) | |
| 68 | - protocol = TBinaryProtocol.TBinaryProtocol(transport) | |
| 69 | - client = ImageDataService.Client(protocol) | |
| 70 | - transport.open() | |
| 71 | - info= json.loads(client.getImageList(path)) | |
| 72 | - transport.close() | |
| 64 | + thrift_connect = ThriftConnect(data_server) | |
| 65 | + info= json.loads(thrift_connect.client.getImageList(path)) | |
| 66 | + thrift_connect.close() | |
| 73 | 67 | res["data"] = info |
| 74 | 68 | |
| 75 | 69 | res["result"] = True | ... | ... |
app/modules/image/image_delete.py
0 → 100644
| 1 | +# coding=utf-8 | |
| 2 | +#author: 4N | |
| 3 | +#createtime: 2021/7/19 | |
| 4 | +#email: nheweijun@sina.com | |
| 5 | + | |
| 6 | +from app.util.component.ApiTemplate import ApiTemplate | |
| 7 | +from app.models import db | |
| 8 | +from .models import Image | |
| 9 | +class Api(ApiTemplate): | |
| 10 | + | |
| 11 | + api_name = "删除影像数据" | |
| 12 | + | |
| 13 | + def process(self): | |
| 14 | + | |
| 15 | + res = {} | |
| 16 | + | |
| 17 | + try: | |
| 18 | + guid = self.para.get("guid") | |
| 19 | + image = Image.query.filter_by(guid=guid).one_or_none() | |
| 20 | + db.session.commit() | |
| 21 | + # res["data"] = guid | |
| 22 | + res["result"] = True | |
| 23 | + | |
| 24 | + except Exception as e: | |
| 25 | + raise e | |
| 26 | + | |
| 27 | + return res | |
| 28 | + | |
| 29 | + api_doc = { | |
| 30 | + "tags": ["影像接口"], | |
| 31 | + "parameters": [ | |
| 32 | + {"name": "guid", | |
| 33 | + "in": "formData", | |
| 34 | + "type": "string", | |
| 35 | + "description": "data_server"} | |
| 36 | + ], | |
| 37 | + "responses": { | |
| 38 | + 200: { | |
| 39 | + "schema": { | |
| 40 | + "properties": { | |
| 41 | + } | |
| 42 | + } | |
| 43 | + } | |
| 44 | + } | |
| 45 | + } | |
| 46 | + | |
| 47 | + | ... | ... |
app/modules/image/image_edit.py
0 → 100644
| ... | ... | @@ -8,7 +8,7 @@ from app.util.component.ApiTemplate import ApiTemplate |
| 8 | 8 | from app.util.component.ModelVisitor import ModelVisitor |
| 9 | 9 | |
| 10 | 10 | from app.modules.image.models import Image |
| 11 | - | |
| 11 | +from sqlalchemy import or_,and_ | |
| 12 | 12 | class Api(ApiTemplate): |
| 13 | 13 | |
| 14 | 14 | api_name = "影像数据List" |
| ... | ... | @@ -23,9 +23,19 @@ class Api(ApiTemplate): |
| 23 | 23 | |
| 24 | 24 | alias = self.para.get("alias") |
| 25 | 25 | name = self.para.get("name") |
| 26 | + images = Image.query | |
| 27 | + | |
| 28 | + # 并集 | |
| 29 | + if alias and name: | |
| 30 | + images = images.filter(or_(Image.alias.like("%" + alias + "%") , Image.name.like("%" + name + "%"))) | |
| 31 | + else: | |
| 32 | + if alias: | |
| 33 | + images = images.filter(Image.alias.like("%" + alias + "%")) | |
| 34 | + if name: | |
| 35 | + images = images.filter(Image.name.like("%" + name + "%")) | |
| 26 | 36 | |
| 37 | + images = images.limit(page_size).offset(page_index).all() | |
| 27 | 38 | |
| 28 | - images = Image.query.all() | |
| 29 | 39 | res["data"] = ModelVisitor.objects_to_jsonarray(images) |
| 30 | 40 | res["result"] = True |
| 31 | 41 | except Exception as e: | ... | ... |
| ... | ... | @@ -3,106 +3,138 @@ |
| 3 | 3 | #createtime: 2021/7/19 |
| 4 | 4 | #email: nheweijun@sina.com |
| 5 | 5 | |
| 6 | -from osgeo.ogr import * | |
| 6 | + | |
| 7 | 7 | from osgeo import gdal,ogr,osr |
| 8 | 8 | from osgeo.gdal import Dataset,Band |
| 9 | 9 | from app.util.component.ApiTemplate import ApiTemplate |
| 10 | +from app.util.component.ThriftConnect import ThriftConnect | |
| 10 | 11 | |
| 11 | - | |
| 12 | -from thrift.transport import TSocket | |
| 13 | -from thrift.transport import TTransport | |
| 14 | -from thrift.protocol import TBinaryProtocol | |
| 15 | -import numpy | |
| 16 | -import cv2 | |
| 17 | 12 | import json |
| 18 | 13 | from .models import Image |
| 19 | -from app.modules.image.ImageDataService import ImageDataService | |
| 14 | +import datetime | |
| 20 | 15 | from app.models import db |
| 21 | 16 | import uuid |
| 22 | 17 | import os |
| 18 | +import math | |
| 23 | 19 | class Api(ApiTemplate): |
| 24 | 20 | |
| 25 | 21 | api_name = "注册影像数据" |
| 26 | 22 | |
| 27 | 23 | def process(self): |
| 28 | 24 | |
| 29 | - | |
| 30 | - # 返回结果 | |
| 25 | + #可以注册一个目录 | |
| 26 | + #返回结果 | |
| 31 | 27 | res = {} |
| 32 | 28 | |
| 33 | 29 | try: |
| 34 | 30 | data_server = self.para.get("data_server") |
| 31 | + paths = json.loads(self.para.get("paths")) | |
| 35 | 32 | |
| 36 | - path = self.para.get("path") | |
| 33 | + #注册某影像 | |
| 34 | + infos = [] | |
| 35 | + if data_server.__eq__("本地服务器"): | |
| 37 | 36 | |
| 37 | + image_paths = list(self.recur_paths_local(paths)) | |
| 38 | 38 | |
| 39 | - if data_server.__eq__("本地服务器"): | |
| 40 | - host = data_server | |
| 41 | - port = 0 | |
| 39 | + for image_info in image_paths: | |
| 42 | 40 | |
| 43 | - image: Dataset = gdal.Open(path, 0) | |
| 44 | - geo = image.GetGeoTransform() | |
| 41 | + image: Dataset = gdal.Open(image_info["path"], 0) | |
| 42 | + geo = image.GetGeoTransform() | |
| 45 | 43 | |
| 46 | - origin = osr.SpatialReference() | |
| 47 | - origin.ImportFromWkt(image.GetProjection()) | |
| 48 | - band_count = image.RasterCount | |
| 49 | - band: Band = image.GetRasterBand(1) | |
| 50 | - count = band.GetOverviewCount() | |
| 51 | - nodatavalue = band.GetNoDataValue() | |
| 52 | - left_top = (geo[0], geo[3]) | |
| 44 | + origin = osr.SpatialReference() | |
| 45 | + origin.ImportFromWkt(image.GetProjection()) | |
| 53 | 46 | |
| 54 | - right_buttom = (geo[0] + geo[1] * image.RasterXSize, geo[3] + geo[5] * image.RasterYSize) | |
| 55 | - origin_extent = [left_top[0], right_buttom[0], right_buttom[1], left_top[1]] | |
| 47 | + authority_code = origin.GetAuthorityCode() | |
| 48 | + band_count = image.RasterCount | |
| 49 | + band: Band = image.GetRasterBand(1) | |
| 50 | + count = band.GetOverviewCount() | |
| 51 | + nodatavalue = band.GetNoDataValue() | |
| 52 | + left_top = (geo[0], geo[3]) | |
| 56 | 53 | |
| 57 | - info = {"band_count": band_count, "overview_count": count, | |
| 58 | - "xy_size": [image.RasterXSize, image.RasterYSize] | |
| 59 | - , "origin_extent": origin_extent, "null_value": nodatavalue} | |
| 54 | + right_buttom = (geo[0] + geo[1] * image.RasterXSize, geo[3] + geo[5] * image.RasterYSize) | |
| 55 | + origin_extent = [left_top[0], right_buttom[0], right_buttom[1], left_top[1]] | |
| 60 | 56 | |
| 61 | - del image | |
| 57 | + target = origin.CloneGeogCS() | |
| 58 | + tran = osr.CoordinateTransformation(origin, target) | |
| 59 | + | |
| 60 | + geo_left_top = tran.TransformPoint(geo[0], geo[3]) | |
| 61 | + geo_right_buttom = tran.TransformPoint(geo[0] + geo[1] * image.RasterXSize, | |
| 62 | + geo[3] + geo[5] * image.RasterYSize) | |
| 63 | + | |
| 64 | + geo_origin_extent = [geo_left_top[1], geo_right_buttom[0], geo_right_buttom[1], geo_left_top[0]] | |
| 65 | + | |
| 66 | + info = {"band_count": band_count, | |
| 67 | + "overview_count": count, | |
| 68 | + "path":image_info["path"], | |
| 69 | + "xy_size": [image.RasterXSize, image.RasterYSize], | |
| 70 | + "origin_extent": origin_extent, | |
| 71 | + "geo_origin_extent": geo_origin_extent, | |
| 72 | + "null_value": nodatavalue, | |
| 73 | + "size":os.path.getsize(image_info["path"]), | |
| 74 | + "sr_wkt": image.GetProjection(), | |
| 75 | + "epsg": authority_code, | |
| 76 | + "sr_proj4": origin.ExportToProj4(), | |
| 77 | + "cell_x_size": geo[1], | |
| 78 | + "cell_y_size": geo[5] | |
| 79 | + } | |
| 80 | + | |
| 81 | + infos.append(info) | |
| 82 | + del image | |
| 62 | 83 | |
| 63 | 84 | else: |
| 64 | - host = data_server.split(":")[0] | |
| 65 | - port = int(data_server.split(":")[1]) | |
| 66 | - | |
| 67 | - transport = TSocket.TSocket(host, port) | |
| 68 | - transport = TTransport.TBufferedTransport(transport) | |
| 69 | - protocol = TBinaryProtocol.TBinaryProtocol(transport) | |
| 70 | - client = ImageDataService.Client(protocol) | |
| 71 | - transport.open() | |
| 72 | - info= json.loads(client.getInfo(path)) | |
| 73 | - transport.close() | |
| 74 | - | |
| 75 | - # 影像空间范围 | |
| 76 | - if not info["origin_extent"]: | |
| 77 | - if not self.para.get("extent"): | |
| 78 | - res["result"] = False | |
| 79 | - res["msg"] = "数据解析范围失败,请手动输入范围" | |
| 80 | - return res | |
| 81 | - else : | |
| 82 | - origin_extent=json.loads(self.para.get("extent")) | |
| 83 | - else: | |
| 84 | - origin_extent = info["origin_extent"] | |
| 85 | - | |
| 86 | - | |
| 87 | - guid = uuid.uuid1().__str__() | |
| 88 | - | |
| 89 | - image = Image(guid= guid, | |
| 90 | - overview=info["overview_count"], | |
| 91 | - raster_x_size=info["xy_size"][0], | |
| 92 | - raster_y_size=info["xy_size"][1], | |
| 93 | - name=os.path.basename(path), | |
| 94 | - alias=self.para.get("alias"), | |
| 95 | - extent=json.dumps(origin_extent), | |
| 96 | - null_value=info["null_value"], | |
| 97 | - host=host, | |
| 98 | - port=port, | |
| 99 | - # path=os.path.normpath(path) | |
| 100 | - path = path | |
| 101 | - ) | |
| 102 | - | |
| 103 | - db.session.add(image) | |
| 85 | + thrift_connect = ThriftConnect(data_server) | |
| 86 | + image_paths = list(self.recur_paths_remote(paths,thrift_connect.client)) | |
| 87 | + for image_info in image_paths: | |
| 88 | + infos.append(json.loads(thrift_connect.client.getInfo(image_info["path"]))) | |
| 89 | + thrift_connect.close() | |
| 90 | + | |
| 91 | + this_time = datetime.datetime.now() | |
| 92 | + for info in infos: | |
| 93 | + | |
| 94 | + # 影像空间范围 | |
| 95 | + # if not info["origin_extent"]: | |
| 96 | + # if not self.para.get("extent"): | |
| 97 | + # res["result"] = False | |
| 98 | + # res["msg"] = "数据解析范围失败,请手动输入范围" | |
| 99 | + # return res | |
| 100 | + # else : | |
| 101 | + # origin_extent=json.loads(self.para.get("extent")) | |
| 102 | + # else: | |
| 103 | + # origin_extent = info["origin_extent"] | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + exist_image = Image.query.filter_by(path=os.path.normpath(info.get("path")), | |
| 109 | + size=info.get("size")).one_or_none() | |
| 110 | + if exist_image: | |
| 111 | + if exist_image.server.__contains__(data_server): | |
| 112 | + pass | |
| 113 | + else: | |
| 114 | + exist_image.update({"server":"{},".format(data_server)}) | |
| 115 | + else: | |
| 116 | + image = Image(guid= uuid.uuid1().__str__(), | |
| 117 | + overview_count=info.get("overview_count"), | |
| 118 | + raster_x_size=info["xy_size"][0], | |
| 119 | + raster_y_size=info["xy_size"][1], | |
| 120 | + cell_x_size = info.get("cell_x_size"), | |
| 121 | + cell_y_size = abs(info.get("cell_y_size")), | |
| 122 | + name=os.path.basename(info.get("path")), | |
| 123 | + origin_extent=json.dumps(info["origin_extent"]), | |
| 124 | + geo_origin_extent = json.dumps(info["geo_origin_extent"]), | |
| 125 | + null_value=info.get("null_value"), | |
| 126 | + server=data_server, | |
| 127 | + path = os.path.normpath(info.get("path")), | |
| 128 | + size=info.get("size"), | |
| 129 | + epsg= info.get("epsg"), | |
| 130 | + sr_wkt = info.get("sr_wkt"), | |
| 131 | + sr_proj4= info.get("sr_proj4"), | |
| 132 | + band_count=info.get("band_count"), | |
| 133 | + create_time=this_time | |
| 134 | + ) | |
| 135 | + db.session.add(image) | |
| 104 | 136 | db.session.commit() |
| 105 | - res["data"] = guid | |
| 137 | + # res["data"] = guid | |
| 106 | 138 | res["result"] = True |
| 107 | 139 | |
| 108 | 140 | except Exception as e: |
| ... | ... | @@ -110,7 +142,35 @@ class Api(ApiTemplate): |
| 110 | 142 | |
| 111 | 143 | return res |
| 112 | 144 | |
| 113 | - | |
| 145 | + def recur_paths_local(self,paths): | |
| 146 | + for path in paths: | |
| 147 | + if path["type"].__eq__("dir"): | |
| 148 | + data_list: list = [] | |
| 149 | + for f in os.listdir(path["path"]): | |
| 150 | + file_path = os.path.normpath(os.path.join(path["path"], f)) | |
| 151 | + file_info = {"name": f, "path": file_path} | |
| 152 | + if file_path.lower().endswith("tiff") or file_path.lower().endswith("tif"): | |
| 153 | + file_info["type"] = "tif" | |
| 154 | + data_list.append(file_info) | |
| 155 | + elif file_path.lower().endswith("img"): | |
| 156 | + file_info["type"] = "img" | |
| 157 | + data_list.append(file_info) | |
| 158 | + elif os.path.isdir(file_path): | |
| 159 | + file_info["type"] = "dir" | |
| 160 | + data_list.append(file_info) | |
| 161 | + for p in self.recur_paths_local(data_list): | |
| 162 | + yield p | |
| 163 | + else: | |
| 164 | + yield path | |
| 165 | + | |
| 166 | + def recur_paths_remote(self,paths,client): | |
| 167 | + for path in paths: | |
| 168 | + if path["type"].__eq__("dir"): | |
| 169 | + path_list = json.loads(client.getImageList(path["path"])) | |
| 170 | + for p in self.recur_paths_remote(path_list,client): | |
| 171 | + yield p | |
| 172 | + else: | |
| 173 | + yield path | |
| 114 | 174 | api_doc = { |
| 115 | 175 | "tags": ["影像接口"], |
| 116 | 176 | "parameters": [ |
| ... | ... | @@ -118,10 +178,10 @@ class Api(ApiTemplate): |
| 118 | 178 | "in": "formData", |
| 119 | 179 | "type": "string", |
| 120 | 180 | "description": "data_server"}, |
| 121 | - {"name": "path", | |
| 181 | + {"name": "paths", | |
| 122 | 182 | "in": "formData", |
| 123 | 183 | "type": "string", |
| 124 | - "description": "path"} | |
| 184 | + "description": "paths"} | |
| 125 | 185 | ], |
| 126 | 186 | "responses": { |
| 127 | 187 | 200: { | ... | ... |
| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | from app.util.component.ApiTemplate import ApiTemplate |
| 8 | 8 | from app.util.component.ModelVisitor import ModelVisitor |
| 9 | 9 | from app.modules.image.models import ImageService |
| 10 | - | |
| 10 | +from sqlalchemy import or_ | |
| 11 | 11 | |
| 12 | 12 | class Api(ApiTemplate): |
| 13 | 13 | |
| ... | ... | @@ -32,8 +32,17 @@ class Api(ApiTemplate): |
| 32 | 32 | alias = self.para.get("alias") |
| 33 | 33 | name = self.para.get("name") |
| 34 | 34 | |
| 35 | - | |
| 36 | - image_services = ImageService.query.all() | |
| 35 | + image_services = ImageService.query | |
| 36 | + # 并集 | |
| 37 | + if alias and name: | |
| 38 | + image_services = image_services.filter(or_(ImageService.alias.like("%" + alias + "%") , ImageService.name.like("%" + name + "%"))) | |
| 39 | + else: | |
| 40 | + if alias: | |
| 41 | + image_services = image_services.filter(ImageService.alias.like("%" + alias + "%")) | |
| 42 | + if name: | |
| 43 | + image_services = image_services.filter(ImageService.name.like("%" + name + "%")) | |
| 44 | + | |
| 45 | + image_services = image_services.limit(page_size).offset(page_index).all() | |
| 37 | 46 | |
| 38 | 47 | res["data"] = ModelVisitor.objects_to_jsonarray(image_services) |
| 39 | 48 | res["result"] = True | ... | ... |
| ... | ... | @@ -11,6 +11,7 @@ from app.util.component.SliceScheme import SliceScheme |
| 11 | 11 | from app.util.component.FileProcess import FileProcess |
| 12 | 12 | import os |
| 13 | 13 | import json |
| 14 | +import datetime | |
| 14 | 15 | class Api(ApiTemplate): |
| 15 | 16 | |
| 16 | 17 | api_name = "注册影像服务" |
| ... | ... | @@ -32,13 +33,14 @@ class Api(ApiTemplate): |
| 32 | 33 | dir_path, store_file = FileProcess.save(parent) |
| 33 | 34 | scheme = SliceScheme(store_file).parameter |
| 34 | 35 | |
| 35 | - service = ImageService(guid=service_guid, name=name, slice_scheme=json.dumps(scheme)) | |
| 36 | + service = ImageService(guid=service_guid, name=name, slice_scheme=json.dumps(scheme), | |
| 37 | + create_time=datetime.datetime.now()) | |
| 36 | 38 | service_exetent = [] |
| 37 | 39 | |
| 38 | 40 | for g in guids: |
| 39 | 41 | image = Image.query.filter_by(guid=g).one_or_none() |
| 40 | 42 | if image: |
| 41 | - image_extent = json.loads(image.extent) | |
| 43 | + image_extent = json.loads(image.origin_extent) | |
| 42 | 44 | if not service_exetent: |
| 43 | 45 | service_exetent = image_extent |
| 44 | 46 | else: |
| ... | ... | @@ -50,6 +52,9 @@ class Api(ApiTemplate): |
| 50 | 52 | service.extent = json.dumps(service_exetent) |
| 51 | 53 | db.session.add(service) |
| 52 | 54 | db.session.commit() |
| 55 | + | |
| 56 | + #获得overview | |
| 57 | + | |
| 53 | 58 | res["data"] = service_guid |
| 54 | 59 | res["result"] = True |
| 55 | 60 | except Exception as e: | ... | ... |
app/modules/image/image_tag_list.py
0 → 100644
| ... | ... | @@ -29,13 +29,11 @@ import json |
| 29 | 29 | from kazoo.client import KazooClient |
| 30 | 30 | from app import GLOBAL_DIC |
| 31 | 31 | from threading import Thread |
| 32 | -from thrift.transport import TSocket | |
| 33 | -from thrift.transport import TTransport | |
| 34 | -from thrift.protocol import TBinaryProtocol | |
| 35 | -from .ImageDataService import ImageDataService | |
| 32 | + | |
| 33 | +from app.util.component.ThriftConnect import ThriftConnect | |
| 36 | 34 | from flask import current_app |
| 37 | 35 | import gzip |
| 38 | - | |
| 36 | +import random | |
| 39 | 37 | class Api(ApiTemplate): |
| 40 | 38 | |
| 41 | 39 | api_name = "切片" |
| ... | ... | @@ -90,47 +88,76 @@ class Api(ApiTemplate): |
| 90 | 88 | slice_para = json.loads(image_service.slice_scheme) |
| 91 | 89 | extent = SliceScheme.get_polygon(slice_para, self.level, self.row, self.col) |
| 92 | 90 | |
| 93 | - # 结果矩阵 | |
| 94 | - empty_list = [numpy.zeros((256, 256), dtype=int) + 65536, | |
| 95 | - numpy.zeros((256, 256), dtype=int) + 65536, | |
| 96 | - numpy.zeros((256, 256), dtype=int) + 65536] | |
| 91 | + height, width = 256,256 | |
| 97 | 92 | |
| 98 | 93 | # 多线程获取分布式数据 |
| 94 | + if abs(extent[0])>180: | |
| 95 | + intersect_image = [im for im in images if self.determin_intersect(json.loads(im.origin_extent),extent)] | |
| 96 | + else: | |
| 97 | + intersect_image = [im for im in images if self.determin_intersect(json.loads(im.geo_origin_extent), extent)] | |
| 98 | + | |
| 99 | + | |
| 100 | + if len(intersect_image) > 1: | |
| 101 | + | |
| 102 | + # 结果矩阵 | |
| 103 | + empty_list = [numpy.zeros((height, width), dtype=int) + 65536, | |
| 104 | + numpy.zeros((height, width), dtype=int) + 65536, | |
| 105 | + numpy.zeros((height, width), dtype=int) + 65536] | |
| 106 | + | |
| 107 | + pixel_array = numpy.zeros((height, width, 3), dtype=int) | |
| 108 | + thread_list = [] | |
| 109 | + | |
| 110 | + for image in intersect_image: | |
| 111 | + # 该影像的服务器,随机选取一个 | |
| 112 | + image_servers = image.server.split(",") | |
| 113 | + indx = int(random.random() * len(image_servers)) | |
| 114 | + image_server = image_servers[indx] | |
| 115 | + | |
| 116 | + thread: MyThread = MyThread(self.get_data, | |
| 117 | + args=(zoo, image_server, image, extent, bands, height, width)) | |
| 118 | + thread.start() | |
| 119 | + thread_list.append(thread) | |
| 120 | + | |
| 121 | + for thread in thread_list: | |
| 122 | + thread.join() | |
| 123 | + data = thread.get_result() | |
| 124 | + | |
| 125 | + # 掩膜在中央接口生成,合图 | |
| 126 | + mask = numpy.zeros((height, width), dtype=int) | |
| 127 | + mask2 = numpy.zeros((height, width), dtype=int) | |
| 128 | + jizhun = data[:, :, 0] | |
| 129 | + mask[jizhun == 65536] = 1 | |
| 130 | + mask[jizhun != 65536] = 0 | |
| 131 | + mask2[jizhun == 65536] = 0 | |
| 132 | + mask2[jizhun != 65536] = 1 | |
| 133 | + # 掩膜计算 | |
| 134 | + for i, d in enumerate(empty_list): | |
| 135 | + empty_list[i] = empty_list[i] * mask + data[:, :, i] * mask2 | |
| 136 | + | |
| 137 | + for ii in [0, 1, 2]: | |
| 138 | + # opencv 颜色排序为GBR | |
| 139 | + pixel_array[:, :, 2 - ii] = empty_list[ii] | |
| 140 | + | |
| 141 | + | |
| 142 | + elif len(intersect_image) == 1: | |
| 143 | + # 该影像的服务器,随机选取一个 | |
| 144 | + image = intersect_image[0] | |
| 145 | + image_servers = image.server.split(",") | |
| 146 | + indx = int(random.random() * len(image_servers)) | |
| 147 | + image_server = image_servers[indx] | |
| 148 | + pixel_array_t = self.get_data(zoo, image_server, image, extent, bands, height, width) | |
| 149 | + pixel_array = numpy.zeros((height, width, 3), dtype=int) | |
| 150 | + for ii in [0, 1, 2]: | |
| 151 | + # opencv 颜色排序为GBR | |
| 152 | + pixel_array[:, :, 2 - ii] = pixel_array_t[:, :, ii] | |
| 99 | 153 | |
| 100 | - intersect_image = [im for im in images if self.determin_intersect(json.loads(im.extent),extent)] | |
| 101 | 154 | |
| 102 | - pixel_array = numpy.zeros((256, 256,3), dtype=int) | |
| 155 | + else: | |
| 156 | + # 结果矩阵 | |
| 157 | + pixel_array = numpy.zeros((height, width, 3), dtype=int) + 65536 | |
| 103 | 158 | |
| 104 | - for image in intersect_image: | |
| 105 | - if image.host.__eq__("本地服务器"): | |
| 106 | - data = self.get_local_data(image,extent,bands) | |
| 107 | - else: | |
| 108 | - ser = "{}:{}".format(image.host,image.port) | |
| 109 | - if zoo.exists("/rpc/{}".format(ser)): | |
| 110 | - data= self.get_remote_data(image,extent,bands) | |
| 111 | - else: | |
| 112 | - data = numpy.zeros((256, 256, 3), dtype=int) + 65536 | |
| 113 | - | |
| 114 | - # 掩膜在中央接口生成 | |
| 115 | - mask = numpy.zeros((256, 256), dtype=int) | |
| 116 | - mask2 = numpy.zeros((256, 256), dtype=int) | |
| 117 | - jizhun = data[:, :, 0] | |
| 118 | - mask[jizhun == 65536] = 1 | |
| 119 | - mask[jizhun != 65536] = 0 | |
| 120 | - mask2[jizhun == 65536] = 0 | |
| 121 | - mask2[jizhun != 65536] = 1 | |
| 122 | - # 掩膜计算 | |
| 123 | - for i, d in enumerate(empty_list): | |
| 124 | - empty_list[i] = empty_list[i] * mask + data[:, :, i] * mask2 | |
| 125 | - | |
| 126 | - | |
| 127 | - for ii in [0, 1, 2]: | |
| 128 | - # opencv 颜色排序为GBR | |
| 129 | - pixel_array[:, :, 2 - ii] = empty_list[ii] | |
| 130 | - | |
| 131 | - #将图片生成在内存中,然后直接返回response | |
| 159 | + # 将图片生成在内存中,然后直接返回response | |
| 132 | 160 | im_data = self.create_by_opencv(image_type, pixel_array, quality) |
| 133 | - | |
| 134 | 161 | return Response(im_data, mimetype=image_type.lower()) |
| 135 | 162 | |
| 136 | 163 | except Exception as e: |
| ... | ... | @@ -139,8 +166,7 @@ class Api(ApiTemplate): |
| 139 | 166 | result["message"] = e.__str__() |
| 140 | 167 | return result |
| 141 | 168 | |
| 142 | - | |
| 143 | - def determine_level(self,xysize,origin_extent,extent,max_level): | |
| 169 | + def determine_level(self, xysize, origin_extent, extent, max_level): | |
| 144 | 170 | ''' |
| 145 | 171 | 根据范围判断调用金字塔的哪一层 |
| 146 | 172 | :param xysize: |
| ... | ... | @@ -152,28 +178,43 @@ class Api(ApiTemplate): |
| 152 | 178 | x = xysize[0] |
| 153 | 179 | y = xysize[1] |
| 154 | 180 | level = -1 |
| 155 | - pixel = x*y * (((extent[2]-extent[0])*(extent[3]-extent[1]))/((origin_extent[2]-origin_extent[0])*(origin_extent[3]-origin_extent[1]))) | |
| 156 | - while pixel>100000 and level<max_level-1: | |
| 157 | - level+=1 | |
| 158 | - x=x/2 | |
| 159 | - y=y/2 | |
| 181 | + pixel = x * y * (((extent[2] - extent[0]) * (extent[3] - extent[1])) / ( | |
| 182 | + (origin_extent[2] - origin_extent[0]) * (origin_extent[3] - origin_extent[1]))) | |
| 183 | + while pixel > 100000 and level < max_level - 1: | |
| 184 | + level += 1 | |
| 185 | + x = x / 2 | |
| 186 | + y = y / 2 | |
| 160 | 187 | pixel = x * y * (((extent[2] - extent[0]) * (extent[3] - extent[1])) / ( |
| 161 | 188 | (origin_extent[2] - origin_extent[0]) * (origin_extent[3] - origin_extent[1]))) |
| 162 | 189 | return level |
| 163 | 190 | |
| 164 | - def create_by_opencv(self,image_type, pixel_array, quality): | |
| 191 | + def create_by_opencv(self, image_type, pixel_array, quality): | |
| 165 | 192 | |
| 166 | 193 | if image_type.__eq__("image/jpeg") or image_type.__eq__("image/jpg"): |
| 167 | 194 | r, buf = cv2.imencode(".jpg", pixel_array, [cv2.IMWRITE_JPEG_QUALITY, quality]) |
| 168 | 195 | image_out = buf.tobytes() |
| 169 | 196 | else: |
| 170 | - four = numpy.zeros((256, 256), dtype=int) + 255 | |
| 197 | + height, width = pixel_array[:, :, 0].shape | |
| 198 | + four = numpy.zeros((height, width), dtype=int) + 255 | |
| 171 | 199 | four[pixel_array[:, :, 0] == 65536] = 0 |
| 172 | 200 | r, buf = cv2.imencode(".png", numpy.dstack((pixel_array, four))) |
| 173 | 201 | image_out = buf.tobytes() |
| 174 | 202 | return image_out |
| 175 | 203 | |
| 176 | - def get_remote_data(self,image,extent,bands): | |
| 204 | + def get_data(self, zoo, image_server, image, extent, bands, height, width): | |
| 205 | + | |
| 206 | + if image_server.__eq__("本地服务器"): | |
| 207 | + data = self.get_local_wms_data(image, extent, bands, height, width) | |
| 208 | + else: | |
| 209 | + ser = image_server | |
| 210 | + if zoo.exists("/rpc/{}".format(ser)): | |
| 211 | + data = self.get_remote_wms_data(image, extent, bands, height, width) | |
| 212 | + else: | |
| 213 | + data = numpy.zeros((height, width, 3), dtype=int) + 65536 | |
| 214 | + | |
| 215 | + return data | |
| 216 | + | |
| 217 | + def get_remote_wms_data(self, image, extent, bands, height, width): | |
| 177 | 218 | ''' |
| 178 | 219 | 通过RPC获取远程数据 |
| 179 | 220 | :param image: |
| ... | ... | @@ -181,24 +222,21 @@ class Api(ApiTemplate): |
| 181 | 222 | :param bands: |
| 182 | 223 | :return: |
| 183 | 224 | ''' |
| 184 | - | |
| 185 | - transport = TSocket.TSocket(image.host, image.port) | |
| 186 | - transport = TTransport.TBufferedTransport(transport) | |
| 187 | - protocol = TBinaryProtocol.TBinaryProtocol(transport) | |
| 188 | - client = ImageDataService.Client(protocol) | |
| 189 | - transport.open() | |
| 225 | + thrift_connect = ThriftConnect(image.server) | |
| 190 | 226 | t1 = time.time() |
| 191 | - data = client.getData(image.path, extent, json.loads(image.extent), bands) | |
| 192 | - transport.close() | |
| 193 | - current_app.logger.info("time {}".format(time.time() - t1)) | |
| 227 | + image_extent = image.origin_extent if abs(extent[0]) > 180 else image.geo_origin_extent | |
| 194 | 228 | |
| 229 | + data = thrift_connect.client.getData(image.path, extent, json.loads(image_extent), bands, width, height) | |
| 230 | + | |
| 231 | + thrift_connect.close() | |
| 232 | + current_app.logger.info("time {}".format(time.time() - t1)) | |
| 195 | 233 | data = gzip.decompress(data) |
| 196 | - data = numpy.frombuffer(data, dtype=int) | |
| 197 | - data = data.reshape((256, 256, 3)) | |
| 234 | + data = numpy.frombuffer(data, dtype='int64') | |
| 235 | + data = data.reshape((height, width, 3)) | |
| 198 | 236 | |
| 199 | 237 | return data |
| 200 | 238 | |
| 201 | - def get_local_data(self,image,extent,bands): | |
| 239 | + def get_local_wms_data(self, image, extent, bands, height, width): | |
| 202 | 240 | ''' |
| 203 | 241 | 获取本地数据 |
| 204 | 242 | :param image: |
| ... | ... | @@ -206,7 +244,7 @@ class Api(ApiTemplate): |
| 206 | 244 | :param bands: |
| 207 | 245 | :return: |
| 208 | 246 | ''' |
| 209 | - pixel_array = numpy.zeros((256, 256, 3), dtype=int) | |
| 247 | + pixel_array = numpy.zeros((height, width, 3), dtype=int) | |
| 210 | 248 | ceng = 0 |
| 211 | 249 | img: Dataset = gdal.Open(image.path, 0) |
| 212 | 250 | t1 = time.time() |
| ... | ... | @@ -214,14 +252,16 @@ class Api(ApiTemplate): |
| 214 | 252 | |
| 215 | 253 | # 自决定金字塔等级 |
| 216 | 254 | xysize = [img.RasterXSize, img.RasterYSize] |
| 217 | - origin_extent = image.extent | |
| 255 | + | |
| 256 | + origin_extent = image.origin_extent if abs(extent[0]) > 180 else image.geo_origin_extent | |
| 218 | 257 | band_data: Band = img.GetRasterBand(band) |
| 258 | + | |
| 219 | 259 | max_level = band_data.GetOverviewCount() |
| 220 | 260 | |
| 221 | 261 | # 超出空间范围 |
| 222 | 262 | if extent[2] < origin_extent[0] or extent[0] > origin_extent[2] or extent[1] > origin_extent[ |
| 223 | 263 | 3] or extent[3] < origin_extent[1]: |
| 224 | - empty = numpy.zeros((256, 256), dtype=int) + 65536 | |
| 264 | + empty = numpy.zeros((height, width), dtype=int) + 65536 | |
| 225 | 265 | # 空间范围相交 |
| 226 | 266 | else: |
| 227 | 267 | image_level = self.determine_level(xysize, origin_extent, extent, max_level) |
| ... | ... | @@ -253,7 +293,7 @@ class Api(ApiTemplate): |
| 253 | 293 | |
| 254 | 294 | y_g = math.ceil((extent[3] - extent[1]) / grid_y) |
| 255 | 295 | |
| 256 | - empty = overview.ReadAsArray(off_x, off_y, x_g, y_g, 256, 256) | |
| 296 | + empty = overview.ReadAsArray(off_x, off_y, x_g, y_g, width, height) | |
| 257 | 297 | |
| 258 | 298 | |
| 259 | 299 | # 部分相交 |
| ... | ... | @@ -276,8 +316,8 @@ class Api(ApiTemplate): |
| 276 | 316 | # 相对于出图的偏移量 |
| 277 | 317 | |
| 278 | 318 | # 出图的网格大小 |
| 279 | - out_grid_x = (extent[2] - extent[0]) / (256 * 1.0) | |
| 280 | - out_grid_y = (extent[3] - extent[1]) / (256 * 1.0) | |
| 319 | + out_grid_x = (extent[2] - extent[0]) / (width * 1.0) | |
| 320 | + out_grid_y = (extent[3] - extent[1]) / (height * 1.0) | |
| 281 | 321 | |
| 282 | 322 | out_off_x = int(math.ceil((inter_extent[0] - extent[0]) / out_grid_x)) |
| 283 | 323 | out_off_y = int(math.ceil((extent[3] - inter_extent[3]) / out_grid_y)) |
| ... | ... | @@ -290,7 +330,7 @@ class Api(ApiTemplate): |
| 290 | 330 | overview_raster: ndarray = overview.ReadAsArray(off_x, off_y, x_g, y_g, out_x_g, |
| 291 | 331 | out_y_g) |
| 292 | 332 | |
| 293 | - dat = numpy.zeros((256, 256), dtype=int) + 65536 | |
| 333 | + dat = numpy.zeros((height, width), dtype=int) + 65536 | |
| 294 | 334 | dat[out_off_y:out_off_y + out_y_g, out_off_x:out_off_x + out_x_g] = overview_raster |
| 295 | 335 | |
| 296 | 336 | empty = dat |
| ... | ... | @@ -299,11 +339,43 @@ class Api(ApiTemplate): |
| 299 | 339 | ceng += 1 |
| 300 | 340 | return pixel_array |
| 301 | 341 | |
| 302 | - def determin_intersect(self,extent1,extent2): | |
| 342 | + def determin_intersect(self, extent1, extent2): | |
| 303 | 343 | g1 = GeometryAdapter.envelop_2_polygon(extent1) |
| 304 | 344 | g2 = GeometryAdapter.envelop_2_polygon(extent2) |
| 305 | 345 | return g1.Intersect(g2) |
| 306 | 346 | |
| 347 | + api_doc = { | |
| 348 | + "tags": ["影像接口"], | |
| 349 | + "parameters": [ | |
| 350 | + {"name": "guid", | |
| 351 | + "in": "formData", | |
| 352 | + "type": "string"}, | |
| 353 | + {"name": "tilematrix", | |
| 354 | + "in": "formData", | |
| 355 | + "type": "string"}, | |
| 356 | + {"name": "tilerow", | |
| 357 | + "in": "formData", | |
| 358 | + "type": "string"}, | |
| 359 | + {"name": "tilecol", | |
| 360 | + "in": "formData", | |
| 361 | + "type": "string"}, | |
| 362 | + {"name": "format", | |
| 363 | + "in": "formData", | |
| 364 | + "type": "string"}, | |
| 365 | + {"name": "quality", | |
| 366 | + "in": "formData", | |
| 367 | + "type": "string"} | |
| 368 | + | |
| 369 | + ], | |
| 370 | + "responses": { | |
| 371 | + 200: { | |
| 372 | + "schema": { | |
| 373 | + "properties": { | |
| 374 | + } | |
| 375 | + } | |
| 376 | + } | |
| 377 | + } | |
| 378 | + } | |
| 307 | 379 | |
| 308 | 380 | class MyThread(Thread): |
| 309 | 381 | def __init__(self,func,args=()): |
| ... | ... | @@ -318,18 +390,5 @@ class MyThread(Thread): |
| 318 | 390 | except Exception: |
| 319 | 391 | return None |
| 320 | 392 | |
| 321 | -api_doc={ | |
| 322 | -"tags":["影像接口"], | |
| 323 | -"parameters":[ | |
| 324 | 393 | |
| 325 | -], | |
| 326 | -"responses":{ | |
| 327 | - 200:{ | |
| 328 | - "schema":{ | |
| 329 | - "properties":{ | |
| 330 | - } | |
| 331 | - } | |
| 332 | - } | |
| 333 | - } | |
| 334 | -} | |
| 335 | 394 | ... | ... |
app/modules/image/image_tilebac.py
0 → 100644
| 1 | +# coding=utf-8 | |
| 2 | +#author: 4N | |
| 3 | +#createtime: 2021/3/24 | |
| 4 | +#email: nheweijun@sina.com | |
| 5 | + | |
| 6 | +from app.util import * | |
| 7 | +import traceback | |
| 8 | +from osgeo import gdal | |
| 9 | +from osgeo.gdal import * | |
| 10 | +from numpy import ndarray | |
| 11 | +import numpy | |
| 12 | +from flask import Response | |
| 13 | +import io | |
| 14 | +import os | |
| 15 | +from PIL import Image | |
| 16 | + | |
| 17 | +import time | |
| 18 | +import cv2 | |
| 19 | +from app.modules.image.models import ImageService,Image | |
| 20 | +from app.models import db | |
| 21 | +from app.util.component.ApiTemplate import ApiTemplate | |
| 22 | +import uuid | |
| 23 | +from app.util.component.SliceScheme import SliceScheme | |
| 24 | +from app.util.component.FileProcess import FileProcess | |
| 25 | +from app.util.component.ParameterUtil import ParameterUtil | |
| 26 | +from app.util.component.GeometryAdapter import GeometryAdapter | |
| 27 | +import os | |
| 28 | +import json | |
| 29 | +from kazoo.client import KazooClient | |
| 30 | +from app import GLOBAL_DIC | |
| 31 | +from threading import Thread | |
| 32 | +from thrift.transport import TSocket | |
| 33 | +from thrift.transport import TTransport | |
| 34 | +from thrift.protocol import TBinaryProtocol | |
| 35 | +from .ImageDataService import ImageDataService | |
| 36 | +from flask import current_app | |
| 37 | +import gzip | |
| 38 | +import random | |
| 39 | +class Api(ApiTemplate): | |
| 40 | + | |
| 41 | + api_name = "切片" | |
| 42 | + | |
| 43 | + def __init__(self,guid,level, row, col): | |
| 44 | + super().__init__() | |
| 45 | + self.guid = guid | |
| 46 | + self.level = level | |
| 47 | + self.row = row | |
| 48 | + self.col = col | |
| 49 | + | |
| 50 | + def process(self): | |
| 51 | + | |
| 52 | + result = {} | |
| 53 | + parameter: dict = self.para | |
| 54 | + | |
| 55 | + try: | |
| 56 | + if parameter.get("guid"): | |
| 57 | + self.guid = parameter.get("guid") | |
| 58 | + | |
| 59 | + image_service = ImageService.query.filter_by(guid = self.guid).one_or_none() | |
| 60 | + images = image_service.images.all() | |
| 61 | + | |
| 62 | + zoo = GLOBAL_DIC.get("zookeeper") | |
| 63 | + if zoo is None: | |
| 64 | + zoo :KazooClient = KazooClient(hosts=configure.zookeeper, timeout=100) | |
| 65 | + zoo.start() | |
| 66 | + GLOBAL_DIC["zookeeper"] = zoo | |
| 67 | + else : | |
| 68 | + if not zoo.connected: | |
| 69 | + zoo.start() | |
| 70 | + | |
| 71 | + bands = [1, 2, 3] | |
| 72 | + | |
| 73 | + | |
| 74 | + # 转换参数 | |
| 75 | + parameter = ParameterUtil.to_lower(parameter) | |
| 76 | + | |
| 77 | + | |
| 78 | + if parameter.get("tilematrix"): | |
| 79 | + if parameter.get("tilematrix").__contains__(":"): | |
| 80 | + self.level = int(parameter.get("tilematrix").split(":")[-1]) | |
| 81 | + else: | |
| 82 | + self.level = int(parameter.get("tilematrix")) | |
| 83 | + if parameter.get("tilerow"): | |
| 84 | + self.row = int(parameter.get("tilerow")) | |
| 85 | + if parameter.get("tilecol"): | |
| 86 | + self.col = int(parameter.get("tilecol")) | |
| 87 | + | |
| 88 | + image_type = parameter.get("format") if parameter.get("format") else "image/png" | |
| 89 | + quality = int(parameter.get("quality")) if parameter.get("quality") else 30 | |
| 90 | + slice_para = json.loads(image_service.slice_scheme) | |
| 91 | + extent = SliceScheme.get_polygon(slice_para, self.level, self.row, self.col) | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + # 多线程获取分布式数据 | |
| 96 | + | |
| 97 | + intersect_image = [im for im in images if self.determin_intersect(json.loads(im.extent),extent)] | |
| 98 | + | |
| 99 | + pixel_array = numpy.zeros((256, 256,3), dtype=int) | |
| 100 | + | |
| 101 | + if len(intersect_image) > 1: | |
| 102 | + | |
| 103 | + # 结果矩阵 | |
| 104 | + empty_list = [numpy.zeros((256,256), dtype=int) + 65536, | |
| 105 | + numpy.zeros((256,256), dtype=int) + 65536, | |
| 106 | + numpy.zeros((256,256), dtype=int) + 65536] | |
| 107 | + | |
| 108 | + pixel_array = numpy.zeros((256,256,3), dtype=int) | |
| 109 | + thread_list = [] | |
| 110 | + | |
| 111 | + for image in intersect_image: | |
| 112 | + #该影像的服务器,随机选取一个 | |
| 113 | + image_servers = image.server.split(",") | |
| 114 | + indx = int(random.random()*len(image_servers)) | |
| 115 | + image_server = image_servers[indx] | |
| 116 | + | |
| 117 | + thread: MyThread = MyThread(self.get_data, args=(zoo,image_server,image,extent,bands)) | |
| 118 | + thread.start() | |
| 119 | + thread_list.append(thread) | |
| 120 | + | |
| 121 | + | |
| 122 | + for thread in thread_list: | |
| 123 | + thread.join() | |
| 124 | + data = thread.get_result() | |
| 125 | + | |
| 126 | + # 掩膜在中央接口生成,合图 | |
| 127 | + mask = numpy.zeros((256,256), dtype=int) | |
| 128 | + mask2 = numpy.zeros((256,256), dtype=int) | |
| 129 | + jizhun = data[:, :, 0] | |
| 130 | + mask[jizhun == 65536] = 1 | |
| 131 | + mask[jizhun != 65536] = 0 | |
| 132 | + mask2[jizhun == 65536] = 0 | |
| 133 | + mask2[jizhun != 65536] = 1 | |
| 134 | + # 掩膜计算 | |
| 135 | + for i, d in enumerate(empty_list): | |
| 136 | + empty_list[i] = empty_list[i] * mask + data[:, :, i] * mask2 | |
| 137 | + for ii in [0, 1, 2]: | |
| 138 | + # opencv 颜色排序为GBR | |
| 139 | + pixel_array[:, :, 2 - ii] = empty_list[ii] | |
| 140 | + | |
| 141 | + elif len(intersect_image) == 1: | |
| 142 | + | |
| 143 | + # 该影像的服务器,随机选取一个 | |
| 144 | + image = intersect_image[0] | |
| 145 | + image_servers = image.server.split(",") | |
| 146 | + indx = int(random.random() * len(image_servers)) | |
| 147 | + image_server = image_servers[indx] | |
| 148 | + pixel_array_t = self.get_data(zoo, image_server, image, extent, bands) | |
| 149 | + pixel_array = numpy.zeros((256,256, 3), dtype=int) | |
| 150 | + for ii in [0, 1, 2]: | |
| 151 | + # opencv 颜色排序为GBR | |
| 152 | + pixel_array[:, :, 2 - ii] = pixel_array_t[:, :, ii] | |
| 153 | + | |
| 154 | + else: | |
| 155 | + # 结果矩阵 | |
| 156 | + pixel_array = numpy.zeros((256, 256, 3), dtype=int) + 65536 | |
| 157 | + | |
| 158 | + #将图片生成在内存中,然后直接返回response | |
| 159 | + im_data = self.create_by_opencv(image_type, pixel_array, quality) | |
| 160 | + | |
| 161 | + return Response(im_data, mimetype=image_type.lower()) | |
| 162 | + | |
| 163 | + except Exception as e: | |
| 164 | + print(traceback.format_exc()) | |
| 165 | + result["state"] = -1 | |
| 166 | + result["message"] = e.__str__() | |
| 167 | + return result | |
| 168 | + | |
| 169 | + | |
| 170 | + def determine_level(self,xysize,origin_extent,extent,max_level): | |
| 171 | + ''' | |
| 172 | + 根据范围判断调用金字塔的哪一层 | |
| 173 | + :param xysize: | |
| 174 | + :param origin_extent: | |
| 175 | + :param extent: | |
| 176 | + :param max_level: | |
| 177 | + :return: | |
| 178 | + ''' | |
| 179 | + x = xysize[0] | |
| 180 | + y = xysize[1] | |
| 181 | + level = -1 | |
| 182 | + pixel = x*y * (((extent[2]-extent[0])*(extent[3]-extent[1]))/((origin_extent[2]-origin_extent[0])*(origin_extent[3]-origin_extent[1]))) | |
| 183 | + while pixel>100000 and level<max_level-1: | |
| 184 | + level+=1 | |
| 185 | + x=x/2 | |
| 186 | + y=y/2 | |
| 187 | + pixel = x * y * (((extent[2] - extent[0]) * (extent[3] - extent[1])) / ( | |
| 188 | + (origin_extent[2] - origin_extent[0]) * (origin_extent[3] - origin_extent[1]))) | |
| 189 | + return level | |
| 190 | + | |
| 191 | + def create_by_opencv(self,image_type, pixel_array, quality): | |
| 192 | + | |
| 193 | + if image_type.__eq__("image/jpeg") or image_type.__eq__("image/jpg"): | |
| 194 | + r, buf = cv2.imencode(".jpg", pixel_array, [cv2.IMWRITE_JPEG_QUALITY, quality]) | |
| 195 | + image_out = buf.tobytes() | |
| 196 | + else: | |
| 197 | + four = numpy.zeros((256, 256), dtype=int) + 255 | |
| 198 | + four[pixel_array[:, :, 0] == 65536] = 0 | |
| 199 | + r, buf = cv2.imencode(".png", numpy.dstack((pixel_array, four))) | |
| 200 | + image_out = buf.tobytes() | |
| 201 | + return image_out | |
| 202 | + | |
| 203 | + def get_data(self,zoo,image_server,image,extent,bands): | |
| 204 | + | |
| 205 | + if image_server.__eq__("本地服务器"): | |
| 206 | + data = self.get_local_data(image, extent, bands) | |
| 207 | + else: | |
| 208 | + ser = image_server | |
| 209 | + if zoo.exists("/rpc/{}".format(ser)): | |
| 210 | + data = self.get_remote_data(image, extent) | |
| 211 | + else: | |
| 212 | + data = numpy.zeros((256, 256, 3), dtype=int) + 65536 | |
| 213 | + return data | |
| 214 | + | |
| 215 | + | |
| 216 | + def get_remote_data(self,image,extent,bands): | |
| 217 | + ''' | |
| 218 | + 通过RPC获取远程数据 | |
| 219 | + :param image: | |
| 220 | + :param extent: | |
| 221 | + :param bands: | |
| 222 | + :return: | |
| 223 | + ''' | |
| 224 | + | |
| 225 | + transport = TSocket.TSocket(image.host, image.port) | |
| 226 | + transport = TTransport.TBufferedTransport(transport) | |
| 227 | + protocol = TBinaryProtocol.TBinaryProtocol(transport) | |
| 228 | + client = ImageDataService.Client(protocol) | |
| 229 | + transport.open() | |
| 230 | + t1 = time.time() | |
| 231 | + data = client.getData(image.path, extent, json.loads(image.extent), bands,256,256) | |
| 232 | + transport.close() | |
| 233 | + current_app.logger.info("time {}".format(time.time() - t1)) | |
| 234 | + | |
| 235 | + data = gzip.decompress(data) | |
| 236 | + data = numpy.frombuffer(data, dtype=int) | |
| 237 | + data = data.reshape((256, 256, 3)) | |
| 238 | + | |
| 239 | + return data | |
| 240 | + | |
| 241 | + def get_local_data(self,image,extent,bands): | |
| 242 | + ''' | |
| 243 | + 获取本地数据 | |
| 244 | + :param image: | |
| 245 | + :param extent: | |
| 246 | + :param bands: | |
| 247 | + :return: | |
| 248 | + ''' | |
| 249 | + pixel_array = numpy.zeros((256, 256, 3), dtype=int) | |
| 250 | + ceng = 0 | |
| 251 | + img: Dataset = gdal.Open(image.path, 0) | |
| 252 | + t1 = time.time() | |
| 253 | + for band in bands: | |
| 254 | + | |
| 255 | + # 自决定金字塔等级 | |
| 256 | + xysize = [img.RasterXSize, img.RasterYSize] | |
| 257 | + origin_extent = image.extent | |
| 258 | + band_data: Band = img.GetRasterBand(band) | |
| 259 | + max_level = band_data.GetOverviewCount() | |
| 260 | + | |
| 261 | + # 超出空间范围 | |
| 262 | + if extent[2] < origin_extent[0] or extent[0] > origin_extent[2] or extent[1] > origin_extent[ | |
| 263 | + 3] or extent[3] < origin_extent[1]: | |
| 264 | + empty = numpy.zeros((256, 256), dtype=int) + 65536 | |
| 265 | + # 空间范围相交 | |
| 266 | + else: | |
| 267 | + image_level = self.determine_level(xysize, origin_extent, extent, max_level) | |
| 268 | + | |
| 269 | + if image_level == -1: | |
| 270 | + overview = band_data | |
| 271 | + else: | |
| 272 | + try: | |
| 273 | + overview: Band = band_data.GetOverview(image_level) | |
| 274 | + except: | |
| 275 | + raise Exception("该影像不存在该级别的金字塔数据!") | |
| 276 | + ox = overview.XSize | |
| 277 | + oy = overview.YSize | |
| 278 | + | |
| 279 | + # 网格大小 | |
| 280 | + grid_x = (origin_extent[2] - origin_extent[0]) / (ox * 1.0) | |
| 281 | + grid_y = (origin_extent[3] - origin_extent[1]) / (oy * 1.0) | |
| 282 | + | |
| 283 | + # 完全在影像范围内 | |
| 284 | + if extent[0] > origin_extent[0] and extent[1] > origin_extent[1] and extent[2] < \ | |
| 285 | + origin_extent[2] and extent[3] < origin_extent[3]: | |
| 286 | + | |
| 287 | + # 网格偏移量 | |
| 288 | + off_x = math.floor((extent[0] - origin_extent[0]) / grid_x) | |
| 289 | + off_y = math.floor((origin_extent[3] - extent[3]) / grid_y) | |
| 290 | + | |
| 291 | + # 截取后网格个数 | |
| 292 | + x_g = math.ceil((extent[2] - extent[0]) / grid_x) | |
| 293 | + | |
| 294 | + y_g = math.ceil((extent[3] - extent[1]) / grid_y) | |
| 295 | + | |
| 296 | + empty = overview.ReadAsArray(off_x, off_y, x_g, y_g, 256, 256) | |
| 297 | + | |
| 298 | + | |
| 299 | + # 部分相交 | |
| 300 | + else: | |
| 301 | + | |
| 302 | + inter_extent = [0, 0, 0, 0] | |
| 303 | + inter_extent[0] = origin_extent[0] if origin_extent[0] > extent[0] else extent[0] | |
| 304 | + inter_extent[1] = origin_extent[1] if origin_extent[1] > extent[1] else extent[1] | |
| 305 | + inter_extent[2] = origin_extent[2] if origin_extent[2] < extent[2] else extent[2] | |
| 306 | + inter_extent[3] = origin_extent[3] if origin_extent[3] < extent[3] else extent[3] | |
| 307 | + | |
| 308 | + # 网格偏移量 | |
| 309 | + off_x = math.floor((inter_extent[0] - origin_extent[0]) / grid_x) | |
| 310 | + off_y = math.floor((origin_extent[3] - inter_extent[3]) / grid_y) | |
| 311 | + | |
| 312 | + # 截取后网格个数 | |
| 313 | + x_g = math.floor((inter_extent[2] - inter_extent[0]) / grid_x) | |
| 314 | + y_g = math.floor((inter_extent[3] - inter_extent[1]) / grid_y) | |
| 315 | + | |
| 316 | + # 相对于出图的偏移量 | |
| 317 | + | |
| 318 | + # 出图的网格大小 | |
| 319 | + out_grid_x = (extent[2] - extent[0]) / (256 * 1.0) | |
| 320 | + out_grid_y = (extent[3] - extent[1]) / (256 * 1.0) | |
| 321 | + | |
| 322 | + out_off_x = int(math.ceil((inter_extent[0] - extent[0]) / out_grid_x)) | |
| 323 | + out_off_y = int(math.ceil((extent[3] - inter_extent[3]) / out_grid_y)) | |
| 324 | + | |
| 325 | + out_x_g = int(math.floor((inter_extent[2] - inter_extent[0]) / out_grid_x)) | |
| 326 | + out_y_g = int(math.floor((inter_extent[3] - inter_extent[1]) / out_grid_y)) | |
| 327 | + | |
| 328 | + # 相交部分在出图的哪个位置 | |
| 329 | + | |
| 330 | + overview_raster: ndarray = overview.ReadAsArray(off_x, off_y, x_g, y_g, out_x_g, | |
| 331 | + out_y_g) | |
| 332 | + | |
| 333 | + dat = numpy.zeros((256, 256), dtype=int) + 65536 | |
| 334 | + dat[out_off_y:out_off_y + out_y_g, out_off_x:out_off_x + out_x_g] = overview_raster | |
| 335 | + | |
| 336 | + empty = dat | |
| 337 | + | |
| 338 | + pixel_array[:, :, ceng] = empty | |
| 339 | + ceng += 1 | |
| 340 | + return pixel_array | |
| 341 | + | |
| 342 | + def determin_intersect(self,extent1,extent2): | |
| 343 | + g1 = GeometryAdapter.envelop_2_polygon(extent1) | |
| 344 | + g2 = GeometryAdapter.envelop_2_polygon(extent2) | |
| 345 | + return g1.Intersect(g2) | |
| 346 | + | |
| 347 | + api_doc = { | |
| 348 | + "tags": ["影像接口"], | |
| 349 | + "parameters": [ | |
| 350 | + {"name": "guid", | |
| 351 | + "in": "formData", | |
| 352 | + "type": "string"}, | |
| 353 | + {"name": "tilematrix", | |
| 354 | + "in": "formData", | |
| 355 | + "type": "string"}, | |
| 356 | + {"name": "tilerow", | |
| 357 | + "in": "formData", | |
| 358 | + "type": "string"}, | |
| 359 | + {"name": "tilecol", | |
| 360 | + "in": "formData", | |
| 361 | + "type": "string"}, | |
| 362 | + {"name": "format", | |
| 363 | + "in": "formData", | |
| 364 | + "type": "string"}, | |
| 365 | + {"name": "quality", | |
| 366 | + "in": "formData", | |
| 367 | + "type": "string"} | |
| 368 | + | |
| 369 | + ], | |
| 370 | + "responses": { | |
| 371 | + 200: { | |
| 372 | + "schema": { | |
| 373 | + "properties": { | |
| 374 | + } | |
| 375 | + } | |
| 376 | + } | |
| 377 | + } | |
| 378 | + } | |
| 379 | + | |
| 380 | +class MyThread(Thread): | |
| 381 | + def __init__(self,func,args=()): | |
| 382 | + super(MyThread,self).__init__() | |
| 383 | + self.func = func | |
| 384 | + self.args = args | |
| 385 | + def run(self): | |
| 386 | + self.result = self.func(*self.args) | |
| 387 | + def get_result(self): | |
| 388 | + try: | |
| 389 | + return self.result | |
| 390 | + except Exception: | |
| 391 | + return None | |
| 392 | + | |
| 393 | + | |
| 394 | + | ... | ... |
app/modules/image/image_wms.py
0 → 100644
| 1 | +# coding=utf-8 | |
| 2 | +#author: 4N | |
| 3 | +#createtime: 2021/3/24 | |
| 4 | +#email: nheweijun@sina.com | |
| 5 | + | |
| 6 | +from app.util import * | |
| 7 | +import traceback | |
| 8 | +from osgeo import gdal | |
| 9 | +from osgeo.gdal import * | |
| 10 | +from numpy import ndarray | |
| 11 | +import numpy | |
| 12 | +from flask import Response | |
| 13 | +import random | |
| 14 | +import time | |
| 15 | +import cv2 | |
| 16 | +from app.modules.image.models import ImageService | |
| 17 | +from app.util.component.ApiTemplate import ApiTemplate | |
| 18 | +from app.util.component.GeometryAdapter import GeometryAdapter | |
| 19 | +from app.util.component.ThriftConnect import ThriftConnect | |
| 20 | +from app.util.component.ParameterUtil import ParameterUtil | |
| 21 | +import json | |
| 22 | +from kazoo.client import KazooClient | |
| 23 | +from app import GLOBAL_DIC | |
| 24 | +from threading import Thread | |
| 25 | +from flask import current_app | |
| 26 | +import gzip | |
| 27 | + | |
| 28 | +class Api(ApiTemplate): | |
| 29 | + | |
| 30 | + api_name = "WMS" | |
| 31 | + | |
| 32 | + def process(self): | |
| 33 | + | |
| 34 | + result = {} | |
| 35 | + parameter: dict = self.para | |
| 36 | + | |
| 37 | + try: | |
| 38 | + | |
| 39 | + parameter = ParameterUtil.to_lower(parameter) | |
| 40 | + guid = parameter.get("guid") | |
| 41 | + bbox = parameter.get("bbox") | |
| 42 | + width = int(parameter.get("width")) if parameter.get("width") else 256 | |
| 43 | + height = int(parameter.get("height")) if parameter.get("height") else 256 | |
| 44 | + image_type = parameter.get("format") if parameter.get("format") else "image/png" | |
| 45 | + quality = int(parameter.get("quality")) if parameter.get("quality") else 30 | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + #缓存起来 | |
| 51 | + image_service = ImageService.query.filter_by(guid = guid).one_or_none() | |
| 52 | + images = image_service.images.all() | |
| 53 | + | |
| 54 | + re = parameter.get("request") | |
| 55 | + | |
| 56 | + if re and re.__eq__("GetCapabilities"): | |
| 57 | + return self.get_capabilities(image_service) | |
| 58 | + | |
| 59 | + zoo = GLOBAL_DIC.get("zookeeper") | |
| 60 | + if zoo is None: | |
| 61 | + zoo :KazooClient = KazooClient(hosts=configure.zookeeper, timeout=100) | |
| 62 | + zoo.start() | |
| 63 | + GLOBAL_DIC["zookeeper"] = zoo | |
| 64 | + else : | |
| 65 | + if not zoo.connected: | |
| 66 | + zoo.start() | |
| 67 | + | |
| 68 | + bands = [1, 2, 3] | |
| 69 | + extent = [float(x) for x in bbox.split(",")] | |
| 70 | + extent = [extent[1],extent[0],extent[3],extent[2]] | |
| 71 | + | |
| 72 | + if abs(extent[0])>180: | |
| 73 | + intersect_image = [im for im in images if self.determin_intersect(json.loads(im.origin_extent),extent)] | |
| 74 | + else: | |
| 75 | + intersect_image = [im for im in images if self.determin_intersect(json.loads(im.geo_origin_extent), extent)] | |
| 76 | + | |
| 77 | + if len(intersect_image)>1: | |
| 78 | + | |
| 79 | + # 结果矩阵 | |
| 80 | + empty_list = [numpy.zeros((height,width), dtype=int) + 65536, | |
| 81 | + numpy.zeros((height,width), dtype=int) + 65536, | |
| 82 | + numpy.zeros((height,width), dtype=int) + 65536] | |
| 83 | + | |
| 84 | + pixel_array = numpy.zeros((height,width,3), dtype=int) | |
| 85 | + thread_list = [] | |
| 86 | + | |
| 87 | + for image in intersect_image: | |
| 88 | + #该影像的服务器,随机选取一个 | |
| 89 | + image_servers = image.server.split(",") | |
| 90 | + indx = int(random.random()*len(image_servers)) | |
| 91 | + image_server = image_servers[indx] | |
| 92 | + | |
| 93 | + thread: MyThread = MyThread(self.get_data, args=(zoo,image_server,image,extent,bands,height,width)) | |
| 94 | + thread.start() | |
| 95 | + thread_list.append(thread) | |
| 96 | + | |
| 97 | + | |
| 98 | + for thread in thread_list: | |
| 99 | + thread.join() | |
| 100 | + data = thread.get_result() | |
| 101 | + | |
| 102 | + # 掩膜在中央接口生成,合图 | |
| 103 | + mask = numpy.zeros((height,width), dtype=int) | |
| 104 | + mask2 = numpy.zeros((height,width), dtype=int) | |
| 105 | + jizhun = data[:, :, 0] | |
| 106 | + mask[jizhun == 65536] = 1 | |
| 107 | + mask[jizhun != 65536] = 0 | |
| 108 | + mask2[jizhun == 65536] = 0 | |
| 109 | + mask2[jizhun != 65536] = 1 | |
| 110 | + # 掩膜计算 | |
| 111 | + for i, d in enumerate(empty_list): | |
| 112 | + empty_list[i] = empty_list[i] * mask + data[:, :, i] * mask2 | |
| 113 | + | |
| 114 | + for ii in [0, 1, 2]: | |
| 115 | + # opencv 颜色排序为GBR | |
| 116 | + pixel_array[:, :, 2 - ii] = empty_list[ii] | |
| 117 | + | |
| 118 | + | |
| 119 | + elif len(intersect_image)==1: | |
| 120 | + # 该影像的服务器,随机选取一个 | |
| 121 | + image = intersect_image[0] | |
| 122 | + image_servers = image.server.split(",") | |
| 123 | + indx = int(random.random() * len(image_servers)) | |
| 124 | + image_server = image_servers[indx] | |
| 125 | + pixel_array_t = self.get_data(zoo,image_server,image,extent,bands,height,width) | |
| 126 | + pixel_array = numpy.zeros((height, width, 3), dtype=int) | |
| 127 | + for ii in [0, 1, 2]: | |
| 128 | + # opencv 颜色排序为GBR | |
| 129 | + pixel_array[:, :, 2 - ii] = pixel_array_t[:, :, ii] | |
| 130 | + else: | |
| 131 | + # 结果矩阵 | |
| 132 | + pixel_array = numpy.zeros((height, width, 3), dtype=int)+65536 | |
| 133 | + | |
| 134 | + # 将图片生成在内存中,然后直接返回response | |
| 135 | + im_data = self.create_by_opencv(image_type, pixel_array, quality) | |
| 136 | + return Response(im_data, mimetype=image_type.lower()) | |
| 137 | + | |
| 138 | + except Exception as e: | |
| 139 | + print(traceback.format_exc()) | |
| 140 | + result["state"] = -1 | |
| 141 | + result["message"] = e.__str__() | |
| 142 | + return result | |
| 143 | + | |
| 144 | + | |
| 145 | + def determine_level(self,xysize,origin_extent,extent,max_level): | |
| 146 | + ''' | |
| 147 | + 根据范围判断调用金字塔的哪一层 | |
| 148 | + :param xysize: | |
| 149 | + :param origin_extent: | |
| 150 | + :param extent: | |
| 151 | + :param max_level: | |
| 152 | + :return: | |
| 153 | + ''' | |
| 154 | + x = xysize[0] | |
| 155 | + y = xysize[1] | |
| 156 | + level = -1 | |
| 157 | + pixel = x*y * (((extent[2]-extent[0])*(extent[3]-extent[1]))/((origin_extent[2]-origin_extent[0])*(origin_extent[3]-origin_extent[1]))) | |
| 158 | + while pixel>100000 and level<max_level-1: | |
| 159 | + level+=1 | |
| 160 | + x=x/2 | |
| 161 | + y=y/2 | |
| 162 | + pixel = x * y * (((extent[2] - extent[0]) * (extent[3] - extent[1])) / ( | |
| 163 | + (origin_extent[2] - origin_extent[0]) * (origin_extent[3] - origin_extent[1]))) | |
| 164 | + return level | |
| 165 | + | |
| 166 | + def create_by_opencv(self,image_type, pixel_array, quality): | |
| 167 | + | |
| 168 | + if image_type.__eq__("image/jpeg") or image_type.__eq__("image/jpg"): | |
| 169 | + r, buf = cv2.imencode(".jpg", pixel_array, [cv2.IMWRITE_JPEG_QUALITY, quality]) | |
| 170 | + image_out = buf.tobytes() | |
| 171 | + else: | |
| 172 | + height, width = pixel_array[:, :, 0].shape | |
| 173 | + four = numpy.zeros((height,width), dtype=int) + 255 | |
| 174 | + four[pixel_array[:, :, 0] == 65536] = 0 | |
| 175 | + r, buf = cv2.imencode(".png", numpy.dstack((pixel_array, four))) | |
| 176 | + image_out = buf.tobytes() | |
| 177 | + return image_out | |
| 178 | + | |
| 179 | + def get_data(self,zoo,image_server,image,extent,bands,height,width): | |
| 180 | + | |
| 181 | + if image_server.__eq__("本地服务器"): | |
| 182 | + data = self.get_local_wms_data(image, extent, bands, height, width) | |
| 183 | + else: | |
| 184 | + ser = image_server | |
| 185 | + if zoo.exists("/rpc/{}".format(ser)): | |
| 186 | + data = self.get_remote_wms_data(image, extent, bands, height, width) | |
| 187 | + else: | |
| 188 | + data = numpy.zeros((height, width, 3), dtype=int) + 65536 | |
| 189 | + | |
| 190 | + return data | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + def get_remote_wms_data(self,image,extent,bands,height,width): | |
| 195 | + ''' | |
| 196 | + 通过RPC获取远程数据 | |
| 197 | + :param image: | |
| 198 | + :param extent: | |
| 199 | + :param bands: | |
| 200 | + :return: | |
| 201 | + ''' | |
| 202 | + thrift_connect = ThriftConnect(image.server) | |
| 203 | + t1 = time.time() | |
| 204 | + image_extent = image.origin_extent if abs(extent[0])>180 else image.geo_origin_extent | |
| 205 | + | |
| 206 | + data = thrift_connect.client.getData(image.path, extent, json.loads(image_extent), bands,width,height) | |
| 207 | + | |
| 208 | + thrift_connect.close() | |
| 209 | + current_app.logger.info("time {}".format(time.time() - t1)) | |
| 210 | + data = gzip.decompress(data) | |
| 211 | + data = numpy.frombuffer(data, dtype='int64') | |
| 212 | + data = data.reshape((height,width, 3)) | |
| 213 | + | |
| 214 | + return data | |
| 215 | + | |
| 216 | + def get_local_wms_data(self,image,extent,bands,height,width): | |
| 217 | + ''' | |
| 218 | + 获取本地数据 | |
| 219 | + :param image: | |
| 220 | + :param extent: | |
| 221 | + :param bands: | |
| 222 | + :return: | |
| 223 | + ''' | |
| 224 | + pixel_array = numpy.zeros((height,width, 3), dtype=int) | |
| 225 | + ceng = 0 | |
| 226 | + img: Dataset = gdal.Open(image.path, 0) | |
| 227 | + t1 = time.time() | |
| 228 | + for band in bands: | |
| 229 | + | |
| 230 | + # 自决定金字塔等级 | |
| 231 | + xysize = [img.RasterXSize, img.RasterYSize] | |
| 232 | + | |
| 233 | + origin_extent = image.origin_extent if abs(extent[0]) > 180 else image.geo_origin_extent | |
| 234 | + band_data: Band = img.GetRasterBand(band) | |
| 235 | + | |
| 236 | + max_level = band_data.GetOverviewCount() | |
| 237 | + | |
| 238 | + # 超出空间范围 | |
| 239 | + if extent[2] < origin_extent[0] or extent[0] > origin_extent[2] or extent[1] > origin_extent[ | |
| 240 | + 3] or extent[3] < origin_extent[1]: | |
| 241 | + empty = numpy.zeros((height,width), dtype=int) + 65536 | |
| 242 | + # 空间范围相交 | |
| 243 | + else: | |
| 244 | + image_level = self.determine_level(xysize, origin_extent, extent, max_level) | |
| 245 | + | |
| 246 | + if image_level == -1: | |
| 247 | + overview = band_data | |
| 248 | + else: | |
| 249 | + try: | |
| 250 | + overview: Band = band_data.GetOverview(image_level) | |
| 251 | + except: | |
| 252 | + raise Exception("该影像不存在该级别的金字塔数据!") | |
| 253 | + ox = overview.XSize | |
| 254 | + oy = overview.YSize | |
| 255 | + | |
| 256 | + # 网格大小 | |
| 257 | + grid_x = (origin_extent[2] - origin_extent[0]) / (ox * 1.0) | |
| 258 | + grid_y = (origin_extent[3] - origin_extent[1]) / (oy * 1.0) | |
| 259 | + | |
| 260 | + # 完全在影像范围内 | |
| 261 | + if extent[0] > origin_extent[0] and extent[1] > origin_extent[1] and extent[2] < \ | |
| 262 | + origin_extent[2] and extent[3] < origin_extent[3]: | |
| 263 | + | |
| 264 | + # 网格偏移量 | |
| 265 | + off_x = math.floor((extent[0] - origin_extent[0]) / grid_x) | |
| 266 | + off_y = math.floor((origin_extent[3] - extent[3]) / grid_y) | |
| 267 | + | |
| 268 | + # 截取后网格个数 | |
| 269 | + x_g = math.ceil((extent[2] - extent[0]) / grid_x) | |
| 270 | + | |
| 271 | + y_g = math.ceil((extent[3] - extent[1]) / grid_y) | |
| 272 | + | |
| 273 | + empty = overview.ReadAsArray(off_x, off_y, x_g, y_g, width,height) | |
| 274 | + | |
| 275 | + | |
| 276 | + # 部分相交 | |
| 277 | + else: | |
| 278 | + | |
| 279 | + inter_extent = [0, 0, 0, 0] | |
| 280 | + inter_extent[0] = origin_extent[0] if origin_extent[0] > extent[0] else extent[0] | |
| 281 | + inter_extent[1] = origin_extent[1] if origin_extent[1] > extent[1] else extent[1] | |
| 282 | + inter_extent[2] = origin_extent[2] if origin_extent[2] < extent[2] else extent[2] | |
| 283 | + inter_extent[3] = origin_extent[3] if origin_extent[3] < extent[3] else extent[3] | |
| 284 | + | |
| 285 | + # 网格偏移量 | |
| 286 | + off_x = math.floor((inter_extent[0] - origin_extent[0]) / grid_x) | |
| 287 | + off_y = math.floor((origin_extent[3] - inter_extent[3]) / grid_y) | |
| 288 | + | |
| 289 | + # 截取后网格个数 | |
| 290 | + x_g = math.floor((inter_extent[2] - inter_extent[0]) / grid_x) | |
| 291 | + y_g = math.floor((inter_extent[3] - inter_extent[1]) / grid_y) | |
| 292 | + | |
| 293 | + # 相对于出图的偏移量 | |
| 294 | + | |
| 295 | + # 出图的网格大小 | |
| 296 | + out_grid_x = (extent[2] - extent[0]) / (width * 1.0) | |
| 297 | + out_grid_y = (extent[3] - extent[1]) / (height * 1.0) | |
| 298 | + | |
| 299 | + out_off_x = int(math.ceil((inter_extent[0] - extent[0]) / out_grid_x)) | |
| 300 | + out_off_y = int(math.ceil((extent[3] - inter_extent[3]) / out_grid_y)) | |
| 301 | + | |
| 302 | + out_x_g = int(math.floor((inter_extent[2] - inter_extent[0]) / out_grid_x)) | |
| 303 | + out_y_g = int(math.floor((inter_extent[3] - inter_extent[1]) / out_grid_y)) | |
| 304 | + | |
| 305 | + # 相交部分在出图的哪个位置 | |
| 306 | + | |
| 307 | + overview_raster: ndarray = overview.ReadAsArray(off_x, off_y, x_g, y_g, out_x_g, | |
| 308 | + out_y_g) | |
| 309 | + | |
| 310 | + dat = numpy.zeros((height,width), dtype=int) + 65536 | |
| 311 | + dat[out_off_y:out_off_y + out_y_g, out_off_x:out_off_x + out_x_g] = overview_raster | |
| 312 | + | |
| 313 | + empty = dat | |
| 314 | + | |
| 315 | + pixel_array[:, :, ceng] = empty | |
| 316 | + ceng += 1 | |
| 317 | + return pixel_array | |
| 318 | + | |
| 319 | + def determin_intersect(self,extent1,extent2): | |
| 320 | + g1 = GeometryAdapter.envelop_2_polygon(extent1) | |
| 321 | + g2 = GeometryAdapter.envelop_2_polygon(extent2) | |
| 322 | + return g1.Intersect(g2) | |
| 323 | + | |
| 324 | + def get_capabilities(self,image_service:ImageService): | |
| 325 | + | |
| 326 | + xml = '''<?xml version="1.0" encoding="utf-8" ?> | |
| 327 | + <WMS_Capabilities version="1.3.0"> | |
| 328 | + <Service> | |
| 329 | + <Name>WMS</Name> | |
| 330 | + <Title>{service_title}</Title> | |
| 331 | + <Abstract>{abstract}</Abstract> | |
| 332 | + <Keywords>GIMS</Keywords> | |
| 333 | + <OnlineResource/> | |
| 334 | + <Fees>none</Fees> | |
| 335 | + <AccessConstraints>none</AccessConstraints> | |
| 336 | + </Service> | |
| 337 | + <Capability> | |
| 338 | + <Request> | |
| 339 | + <GetCapabilities> | |
| 340 | + <Format>text/xml</Format> | |
| 341 | + <DCPType> | |
| 342 | + <HTTP> | |
| 343 | + <Get> | |
| 344 | + <OnlineResource xlink:href="{url}" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/> | |
| 345 | + </Get> | |
| 346 | + </HTTP> | |
| 347 | + </DCPType> | |
| 348 | + </GetCapabilities> | |
| 349 | + <GetMap> | |
| 350 | + <Format>png</Format> | |
| 351 | + <Format>jpeg</Format> | |
| 352 | + <Format>gif</Format> | |
| 353 | + <Format>image/png</Format> | |
| 354 | + <Format>image/jpeg</Format> | |
| 355 | + <Format>image/gif</Format> | |
| 356 | + <DCPType> | |
| 357 | + <HTTP> | |
| 358 | + <Get> | |
| 359 | + <OnlineResource xlink:href="{url}" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/> | |
| 360 | + </Get> | |
| 361 | + </HTTP> | |
| 362 | + </DCPType> | |
| 363 | + </GetMap> | |
| 364 | + <Map> | |
| 365 | + <Format> | |
| 366 | + <PNG/> | |
| 367 | + <GIF/> | |
| 368 | + <JPG/> | |
| 369 | + </Format> | |
| 370 | + <DCPType> | |
| 371 | + <HTTP> | |
| 372 | + <Get onlineResource="{url}"/> | |
| 373 | + </HTTP> | |
| 374 | + </DCPType> | |
| 375 | + </Map> | |
| 376 | + <Capabilities> | |
| 377 | + <Format> | |
| 378 | + <WMS_XML/> | |
| 379 | + </Format> | |
| 380 | + <DCPType> | |
| 381 | + <HTTP> | |
| 382 | + <Get onlineResource="{url}"/> | |
| 383 | + </HTTP> | |
| 384 | + </DCPType> | |
| 385 | + </Capabilities> | |
| 386 | + <FeatureInfo> | |
| 387 | + <Format> | |
| 388 | + <XML/> | |
| 389 | + <MIME/> | |
| 390 | + </Format> | |
| 391 | + <DCPType> | |
| 392 | + <HTTP> | |
| 393 | + <Get onlineResource="{url}"/> | |
| 394 | + </HTTP> | |
| 395 | + </DCPType> | |
| 396 | + </FeatureInfo> | |
| 397 | + </Request> | |
| 398 | + <Exception> | |
| 399 | + <Format> | |
| 400 | + <WMS_XML/> | |
| 401 | + <INIMAGE/> | |
| 402 | + <BLANK/> | |
| 403 | + </Format> | |
| 404 | + </Exception> | |
| 405 | + <Layer> | |
| 406 | + <Name>{service_name}</Name> | |
| 407 | + <Title>{service_title}</Title> | |
| 408 | + <CRS>{crs}</CRS> | |
| 409 | + <BoundingBox CRS="{crs}" maxx="{maxx}" maxy="{maxy}" minx="{minx}" miny="{miny}"/> | |
| 410 | + <Layer queryable="1"> | |
| 411 | + <CRS>{crs}</CRS> | |
| 412 | + <Name>{layer_name}</Name> | |
| 413 | + <Title>{layer_title}</Title> | |
| 414 | + <BoundingBox CRS="{crs}" maxx="{maxx}" maxy="{maxy}" minx="{minx}" miny="{miny}"/> | |
| 415 | + </Layer> | |
| 416 | + </Layer> | |
| 417 | + </Capability> | |
| 418 | + </WMS_Capabilities>''' | |
| 419 | + | |
| 420 | + extent = json.loads(image_service.extent) | |
| 421 | + xml = xml.format(service_title=image_service.name, | |
| 422 | + service_name=image_service.name, | |
| 423 | + abstract=image_service.description if image_service.description else "None" , | |
| 424 | + crs="ESPG:4326", | |
| 425 | + layer_name=image_service.name, | |
| 426 | + layer_title=image_service.name, | |
| 427 | + maxx=extent[2], | |
| 428 | + maxy = extent[3], | |
| 429 | + minx = extent[0], | |
| 430 | + miny = extent[1], | |
| 431 | + url="http://{}/API/Image/WMS?guid={}".format(configure.deploy_ip_host,image_service.guid)) | |
| 432 | + | |
| 433 | + | |
| 434 | + r = Response(response=xml, status=200, mimetype="application/xml") | |
| 435 | + r.headers["Content-Type"] = "text/xml; charset=utf-8" | |
| 436 | + return r | |
| 437 | + | |
| 438 | + api_doc = { | |
| 439 | + "tags": ["影像接口"], | |
| 440 | + "parameters": [ | |
| 441 | + {"name": "guid", | |
| 442 | + "in": "query", | |
| 443 | + "type": "string"}, | |
| 444 | + {"name": "bbox", | |
| 445 | + "in": "query", | |
| 446 | + "type": "string"}, | |
| 447 | + {"name": "width", | |
| 448 | + "in": "query", | |
| 449 | + "type": "string"}, | |
| 450 | + {"name": "height", | |
| 451 | + "in": "query", | |
| 452 | + "type": "string"}, | |
| 453 | + {"name": "format", | |
| 454 | + "in": "query", | |
| 455 | + "type": "string"}, | |
| 456 | + {"name": "quality", | |
| 457 | + "in": "query", | |
| 458 | + "type": "string"} | |
| 459 | + ], | |
| 460 | + "responses": { | |
| 461 | + 200: { | |
| 462 | + "schema": { | |
| 463 | + "properties": { | |
| 464 | + } | |
| 465 | + } | |
| 466 | + } | |
| 467 | + } | |
| 468 | + } | |
| 469 | + | |
| 470 | +class MyThread(Thread): | |
| 471 | + def __init__(self,func,args=()): | |
| 472 | + super(MyThread,self).__init__() | |
| 473 | + self.func = func | |
| 474 | + self.args = args | |
| 475 | + def run(self): | |
| 476 | + self.result = self.func(*self.args) | |
| 477 | + def get_result(self): | |
| 478 | + try: | |
| 479 | + return self.result | |
| 480 | + except Exception: | |
| 481 | + return None | |
| 482 | + | |
| 483 | + | |
| 484 | + | ... | ... |
| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | #email: nheweijun@sina.com |
| 5 | 5 | |
| 6 | 6 | |
| 7 | -from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, Time,Binary | |
| 7 | +from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, Time,Binary,Float | |
| 8 | 8 | from app.models import db |
| 9 | 9 | |
| 10 | 10 | |
| ... | ... | @@ -16,21 +16,31 @@ class Image(db.Model): |
| 16 | 16 | __tablename__ = 'dmdms_image' |
| 17 | 17 | guid = Column(String(256), primary_key=True) |
| 18 | 18 | name = Column(String) |
| 19 | - alias=Column(String) | |
| 20 | - raster_y_size=Column(Integer) | |
| 19 | + alias = Column(String) | |
| 20 | + raster_y_size = Column(Integer) | |
| 21 | 21 | raster_x_size = Column(Integer) |
| 22 | - overview = Column(Integer) | |
| 23 | - extent = Column(String) | |
| 22 | + overview_count = Column(Integer) | |
| 23 | + origin_extent = Column(String) | |
| 24 | + geo_origin_extent = Column(String) | |
| 24 | 25 | null_value = Column(Integer) |
| 25 | 26 | available = Column(Integer)#影像是否可用,不可用可能在创建金字塔中 |
| 26 | - bands = Column(Integer) | |
| 27 | - path=Column(String) | |
| 28 | - host=Column(String) | |
| 29 | - port=Column(Integer) | |
| 27 | + band_count = Column(Integer) | |
| 28 | + path = Column(String) | |
| 29 | + server = Column(String) | |
| 30 | + # host=Column(String) | |
| 31 | + # port=Column(Integer) | |
| 32 | + size = Column(Float) | |
| 30 | 33 | #坐标wkt |
| 31 | - projection = Column(Text) | |
| 34 | + sr_wkt = Column(Text) | |
| 35 | + sr_proj4 = Column(Text) | |
| 36 | + epsg = Column(Integer) | |
| 37 | + create_time = Column(DateTime) | |
| 38 | + update_time = Column(DateTime) | |
| 39 | + cell_x_size = Column(Float) | |
| 40 | + cell_y_size = Column(Float) | |
| 41 | + region = Column(Text) | |
| 32 | 42 | #年份 |
| 33 | - ym= Column(String(256)) | |
| 43 | + ym = Column(String(256)) | |
| 34 | 44 | |
| 35 | 45 | dmdms_image_rel = db.Table('dmdms_image_rel', |
| 36 | 46 | Column('image_guid',String, ForeignKey('dmdms_image.guid')), |
| ... | ... | @@ -53,12 +63,34 @@ class ImageService(db.Model): |
| 53 | 63 | description = Column(Text) |
| 54 | 64 | slice_scheme = Column(Text) |
| 55 | 65 | extent = Column(String(256)) |
| 66 | + node = Column(Integer) | |
| 56 | 67 | #可视范围geojson |
| 57 | 68 | visual_range = Column(Text) |
| 58 | 69 | #影像服务缩略图 |
| 59 | 70 | overview = Column(Binary) |
| 71 | + | |
| 60 | 72 | images = db.relationship('Image', |
| 61 | 73 | secondary=dmdms_image_rel, |
| 62 | 74 | backref='image_services', |
| 63 | 75 | lazy='dynamic' |
| 64 | 76 | ) |
| 77 | + | |
| 78 | + | |
| 79 | +dmdms_image_tag_rel = db.Table('dmdms_image_tag_rel', | |
| 80 | + Column('image_guid',String, ForeignKey('dmdms_image.guid')), | |
| 81 | + Column('tag_guid', String, ForeignKey('dmdms_image_tag.guid')) | |
| 82 | + ) | |
| 83 | + | |
| 84 | +class ImageTag(db.Model): | |
| 85 | + ''' | |
| 86 | + 影像标签 | |
| 87 | + ''' | |
| 88 | + __tablename__ = 'dmdms_image_tag' | |
| 89 | + guid = Column(String(256), primary_key=True) | |
| 90 | + name=Column(String(256)) | |
| 91 | + alias = Column(String(256)) | |
| 92 | + images = db.relationship('Image', | |
| 93 | + secondary=dmdms_image_tag_rel, | |
| 94 | + backref='image_tags', | |
| 95 | + lazy='dynamic' | |
| 96 | + ) | ... | ... |
| ... | ... | @@ -51,11 +51,10 @@ class Api(ApiTemplate): |
| 51 | 51 | data = self.download_gdb(table_names, ds,database_guid) |
| 52 | 52 | |
| 53 | 53 | result["data"] = data |
| 54 | - result["state"] = 1 | |
| 54 | + result["result"] = True | |
| 55 | 55 | except Exception as e: |
| 56 | - print(traceback.format_exc()) | |
| 57 | - result["message"]= e.__str__() | |
| 58 | - result["state"]=-1 | |
| 56 | + raise e | |
| 57 | + | |
| 59 | 58 | finally: |
| 60 | 59 | if ds: |
| 61 | 60 | ds.Destroy() | ... | ... |
| ... | ... | @@ -225,7 +225,8 @@ class EntryDataVacuate: |
| 225 | 225 | if geo is not None: |
| 226 | 226 | out_geom:Geometry = GeometryAdapter.change_geom(geo, geom_type) |
| 227 | 227 | out_feature.SetGeometry(out_geom) |
| 228 | - | |
| 228 | + # 出现fid为0经常有问题 | |
| 229 | + out_feature.SetFID(out_feature.GetFID() + 1) | |
| 229 | 230 | pg_layer.CreateFeature(out_feature) |
| 230 | 231 | |
| 231 | 232 | #插入抽稀图层 | ... | ... |
app/util/component/ThriftConnect.py
0 → 100644
| 1 | +# coding=utf-8 | |
| 2 | +#author: 4N | |
| 3 | +#createtime: 2021/9/8 | |
| 4 | +#email: nheweijun@sina.com | |
| 5 | + | |
| 6 | +from thrift.transport import TSocket | |
| 7 | +from thrift.transport import TTransport | |
| 8 | +from thrift.protocol import TBinaryProtocol | |
| 9 | +from app.modules.image.ImageDataService import ImageDataService | |
| 10 | + | |
| 11 | +class ThriftConnect: | |
| 12 | + ''' | |
| 13 | + thrift连接类 | |
| 14 | + ''' | |
| 15 | + | |
| 16 | + client = None | |
| 17 | + transport = None | |
| 18 | + | |
| 19 | + def __init__(self,data_server): | |
| 20 | + host = data_server.split(":")[0] | |
| 21 | + port = int(data_server.split(":")[1]) | |
| 22 | + self.transport: TSocket = TSocket.TSocket(host, port) | |
| 23 | + self.transport = TTransport.TBufferedTransport(self.transport) | |
| 24 | + protocol = TBinaryProtocol.TBinaryProtocol(self.transport) | |
| 25 | + self.client = ImageDataService.Client(protocol) | |
| 26 | + self.transport.open() | |
| 27 | + | |
| 28 | + def close(self): | |
| 29 | + self.transport.close() | |
| \ No newline at end of file | ... | ... |
| 1 | 1 | # coding=utf-8 |
| 2 | 2 | |
| 3 | 3 | # 程序部署ip:host |
| 4 | -deploy_ip_host = "172.26.99.160:8840" | |
| 4 | +deploy_ip_host = "172.26.40.105:8840" | |
| 5 | 5 | # 系统数据库 |
| 6 | 6 | |
| 7 | -SQLALCHEMY_DATABASE_URI = "postgresql://postgres:chinadci@172.26.99.160:5432/dmap_dms_test" | |
| 7 | +SQLALCHEMY_DATABASE_URI = "postgresql://postgres:chinadci@172.26.60.100:5432/dmap_manager" | |
| 8 | 8 | |
| 9 | 9 | # 指定精华表所在位置(必须为空间库),设置为None则存放在各自的实体库中 |
| 10 | 10 | VACUATE_DB_URI = None | ... | ... |
| ... | ... | @@ -24,7 +24,7 @@ fi |
| 24 | 24 | #启动容器和apache |
| 25 | 25 | echo "正在启动容器..." |
| 26 | 26 | set="--privileged=true -e TZ="Asia/Shanghai" --restart=always -e ALLOW_IP_RANGE=0.0.0.0/0" |
| 27 | -docker run -d --name dmapmanager $set -p $port:80 -v $curPath:/usr/src/app -v $curPath/httpd.conf:/etc/httpd/conf/httpd.conf dci/dmapdms:4.0 /usr/sbin/init | |
| 27 | +docker run -d --name dmapmanager $set -p $port:80 -v $curPath:/usr/src/app -v $curPath/httpd.conf:/etc/httpd/conf/httpd.conf dci/dmapmanager:4.0 /usr/sbin/init | |
| 28 | 28 | docker exec -d dmapmanager systemctl start httpd |
| 29 | 29 | sleep 5 |
| 30 | 30 | curl localhost:$port/release | ... | ... |
| ... | ... | @@ -18,6 +18,5 @@ import json |
| 18 | 18 | # |
| 19 | 19 | # print(sys.getsizeof(json.dumps(pixel_array.tolist()))) |
| 20 | 20 | # print(sys.getsizeof((content))) |
| 21 | - | |
| 22 | -for x in range(-3,3,1): | |
| 23 | - print(x) | |
| \ No newline at end of file | ||
| 21 | +import random | |
| 22 | +print(int(random.random()*2)) | |
| \ No newline at end of file | ... | ... |
updatadb.txt
0 → 100644
version.txt
0 → 100644
| 1 | +#2021.09.10 Version:4.0.0 | ... | ... |
请
注册
或
登录
后发表评论