소스 검색

做进程切片、存醋

zhangnaiwen 1 년 전
부모
커밋
2a2e16a2f8

+ 37 - 0
bin/worker.py

@@ -0,0 +1,37 @@
+import os
+import sys
+from multiprocessing import Process
+
+import rq
+
+from config import Config
+from connection import Connection
+
+src_root = os.path.join(os.path.abspath(__file__).split('bin')[0], 'src')
+sys.path.append(src_root)
+
+
+def start_worker(config):
+    connection = Connection(config)
+
+    with rq.Connection(connection=connection.redis_conn):
+        rq.Worker(['default']).work()
+
+
+if __name__ == '__main__':
+    manage_path = os.path.dirname(os.path.abspath(__file__))
+    config_yml_path = os.path.join(os.path.dirname(manage_path), 'config', 'config.yml')
+
+    config = Config()
+    config.load(config_yml_path)
+
+    # process_list = []
+    # for i in range(int(config.common.WORKER_NUM)):
+    #     process_list.append(Process(target=start_worker, args=(config,)))
+    #
+    # for p in process_list:
+    #     p.start()
+    # for p in process_list:
+    #     p.join()
+
+    start_worker(config)

+ 14 - 4
conf/supervisor.conf

@@ -1,8 +1,18 @@
-[program:operation_management_center]
-command = uwsgi /work/operation_management_center/conf/uwsgi.ini
+[program:sky_stareath_tiler]
+command = uwsgi /work/sky_stareath_tiler/conf/uwsgi.ini
 
 autorestart = true
 redirect_stderr = true
 stopsignal = QUIT
-stderr_logfile = /data/logs/operation_management_center_error.log
-stdout_logfile = /data/logs/operation_management_center.log
+stderr_logfile = /data/logs/sky_stareath_tiler_error.log
+stdout_logfile = /data/logs/sky_stareath_tiler.log
+
+
+[program:sky_stareath_tiler_worker]
+command = python /work/sky_stareath_tiler/bin/worker.py
+
+autorestart = true
+redirect_stderr = true
+stopsignal = QUIT
+stderr_logfile = /data/logs/sky_stareath_tiler_worker_error.log
+stdout_logfile = /data/logs/sky_stareath_tiler_worker.log

+ 3 - 3
conf/uwsgi.ini

@@ -15,7 +15,7 @@ http = $(UWSGI_HTTP)
 endif =
 
 # PID文件
-pidfile = /var/run/operation_management_center.pid
+pidfile = /var/run/sky_stareath_tiler.pid
 
 # 主入口模块
 module = app.webapp
@@ -81,7 +81,7 @@ disable-logging = $(UWSGI_DISABLE_LOGGING)
 endif =
 
 # 记录uwsgi自身的日志
-logto = /data/logs/operation_management_center.log
+logto = /data/logs/uwsgi.log
 # daemonize = /data/logs/nginx_agent.log
 
 
@@ -128,4 +128,4 @@ listen = $(UWSGI_LISTEN)
 endif =
 
 # python包环境
-pythonpath=/work/operation_management_center/src
+pythonpath=/work/sky_stareath_tiler/src

+ 17 - 0
config/config.yml

@@ -1,7 +1,24 @@
 common:
   CACHE_PATH: /Users/mac/data/cache
   OUTPUT_PATH:  /Users/mac/data/output
+  IMAGE_PATH: /Users/mac/data/image  # 影像数据目录
+  TILESET_PATH: /Users/mac/data/tileset  # 3Dtiles数据目录
+  WORKER_NUM: 5
+  JOB_TIMEOUT: 3600
 
+dms:
+  URL: http://121.43.55.7:10081
+  columnId: 1364
+  modelId: 1249
+
+oauth:
+  URL:
+  userName:
+  password:
+  clientId:
+  serviceId:
+nginx:
+  URL:
 redis:
   # redis配置
   HOST: 127.0.0.1 # 使用容器运行服务时,该HOST信息无用,容器内部会自动获取redis容器的ip

+ 4 - 2
src/app/api/__init__.py

@@ -1,7 +1,9 @@
 from flask_restx import Api
 
-from app.api.mission import ns
+from app.api.mission import ns as mission_ns
+from app.api.file import ns as file_ns
 
 api = Api(version='v1.0', title='', description='', doc='/api')
 
-api.add_namespace(ns)
+api.add_namespace(mission_ns)
+api.add_namespace(file_ns)

+ 40 - 0
src/app/api/file.py

@@ -0,0 +1,40 @@
+import json
+import traceback
+
+from flask import request, jsonify
+from flask_restx import Resource, Namespace, reqparse
+
+from app.defines import StatesCode
+from app.utils.utils import get_directory_tree
+from config import Config
+
+ns = Namespace('file', description='文件管理 API接口')
+
+config = Config()
+
+files_parser = reqparse.RequestParser(bundle_errors=True)
+files_parser.add_argument(name='type', type=str, location='args', required=False, help='数据类型 :tif、3Dtiles')
+
+
+@ns.route('/get_files')
+class GetFilesApi(Resource):
+    @ns.doc(id='get_files', description='获取原始数据目录文件列表')
+    @ns.expect(files_parser)
+    def get(self):
+
+        try:
+            type = request.args.get('type')
+            if type == 'tif':
+                path = config.common.IMAGE_PATH
+            elif type == '3Dtiles':
+                path = config.common.TILESET_PATH
+            else:
+                return jsonify(code=StatesCode.PARA_ERROR, message='数据类型错误')
+
+            structure = get_directory_tree(path)
+
+            return jsonify(code=StatesCode.SUCCESS, message='获取成功', data=json.dumps(structure))
+        except Exception as e:
+            traceback.print_exc()
+
+            return {'code': StatesCode.UNKNOWN_ERROR, 'message': str(e)}

+ 36 - 0
src/app/api/image.py

@@ -0,0 +1,36 @@
+import os
+import traceback
+
+from flask import request, jsonify
+from flask_restx import Resource, Namespace, reqparse
+
+from app.defines import StatesCode
+from app.utils.create_geojsonl import verify_geotiff
+
+ns = Namespace('image', description='数据管理 API接口')
+
+files_parser = reqparse.RequestParser(bundle_errors=True)
+files_parser.add_argument(name='path', type=str, location='args', required=False, help='数据目录')
+
+
+@ns.route('/verify_geotiff')
+class VerifyGeotiffApi(Resource):
+    @ns.doc(id='verify', description='验证数据')
+    @ns.expect()
+    def get(self):
+        """数据验证"""
+        path = request.args.get('path')
+        if not path:
+            return jsonify(code=StatesCode.PARA_ERROR, message='输入路径错误')
+
+        err_data = {}
+
+        for file in os.listdir(path):
+            if os.path.splitext(file)[-1] == ".tif":
+                try:
+
+                    verify_geotiff(os.path.join(path, file))
+                except Exception as err:
+                    err_data[file] = str(err)
+
+        return jsonify(code=StatesCode.SUCCESS, message='验证完成', data=err_data)

+ 52 - 26
src/app/api/mission.py

@@ -1,23 +1,27 @@
-import json
+import os.path
 import traceback
 
+import requests
 from flask import request, jsonify
 from flask_restx import Resource, Namespace, reqparse
 
 from app.defines import StatesCode
-from application import Application
+from app.mission_jobs.application import Application
+from config import Config
 from starearth.utils.general_utils import print_log
 
+config = Config()
 ns = Namespace('mission', description='任务管理 API接口')
 
 slice_mission_parser = reqparse.RequestParser(bundle_errors=True)
-slice_mission_parser.add_argument(name='data_path', type=str, location='from', required=False,help='数据目录')
-slice_mission_parser.add_argument(name='tile_size', type=int, location='from', required=False,help='瓦片大小')
-slice_mission_parser.add_argument(name='tile_format', type=int, location='from', required=False,help='瓦片格式:png、jpeg、tif')
-slice_mission_parser.add_argument(name='auto_zoom', type=int, location='from', required=False,help='是否自动切片,0:否,1:是')
-slice_mission_parser.add_argument(name='min_zoom', type=int, location='from', required=False,help='最小切片层级')
-slice_mission_parser.add_argument(name='max_zoom', type=int, location='from', required=False,help='最大切片层级')
-
+slice_mission_parser.add_argument(name='data_path', type=str, location='form', required=False, help='数据目录')
+slice_mission_parser.add_argument(name='tile_size', type=int, location='form', required=False, help='瓦片大小')
+slice_mission_parser.add_argument(name='tile_format', type=str, location='form', required=False,
+                                  help='瓦片格式:png、jpeg、tif')
+slice_mission_parser.add_argument(name='auto_zoom', type=int, location='form', required=False,
+                                  help='是否自动切片,0:否,1:是')
+slice_mission_parser.add_argument(name='min_zoom', type=int, location='form', required=False, help='最小切片层级')
+slice_mission_parser.add_argument(name='max_zoom', type=int, location='form', required=False, help='最大切片层级')
 
 
 @ns.route('/mission_slice_api')
@@ -30,12 +34,15 @@ class MissionAPI(Resource):
         try:
             form = request.form
             data_path = form.get('data_path')
-            tile_size = form.get('tile_size')
-            tile_grid = form.get('tile_grid')
-            tile_format = form.get('tile_format')
-            auto_zoom = form.get('auto_zoom')
-            min_zoom = form.get('min_zoom')
-            max_zoom = form.get('max_zoom')
+            tile_size = int(form.get('tile_size', 256))
+            tile_grid = form.get('tile_grid', 'WebMercatorQuad')
+            tile_format = form.get('tile_format', 'png')
+            auto_zoom = int(form.get('auto_zoom', 1))
+            min_zoom = int(form.get('min_zoom', 1))
+            max_zoom = int(form.get('max_zoom', 19))
+
+            if not data_path:
+                return jsonify(code=StatesCode.PARA_ERROR, message='输入路径错误')
 
             application = Application()
             application.new_mission(
@@ -46,20 +53,39 @@ class MissionAPI(Resource):
                 auto_zoom=auto_zoom,
                 min_zoom=min_zoom,
                 max_zoom=max_zoom
-
             )
 
             # 添加至dem
-            # try:
-            #     key_status_upload(proxy_host=config.common.RPOXY_SERVICE_HOST,       # 上报服务的IP
-            #                       proxy_port=str(config.common.RPOXY_SERVICE_PORT),  # 上报服务的端口
-            #                       software_key=root_mission_id,                      # 任务标识
-            #                       software_name=mission_name,                        # 任务名称
-            #                       software_status='0',                               # 字符串类型的数字
-            #                       software_warn_desc='任务创建成功',                   # 描述
-            #                       is_enable=config.common.ENABLE_PROXY_BASE)         # 是否上报
-            # except Exception as err:
-            #     print_log('任务上报失败')
+            try:
+                # 获取token
+                login_data = {
+                    'userName': config.oauth.userName,
+                    'password': config.oauth.password,
+                    'clientId': config.oauth.clientId,
+                    'serviceId': config.oauth.serviceId
+                }
+                login_rep = requests.post(config.oauth.URL + '/api/user/login', data=login_data).json()
+                headers = {'token': login_rep.get('message')}
+                dms_data = {
+                    "title": os.path.basename(data_path),
+                    "content": os.path.basename(data_path),
+                    "c_tile_grid": tile_grid,
+                    "c_name": os.path.basename(data_path),
+                    "c_tile_format": tile_format,
+                    "c_epsg": '4326',
+                    "c_url": config.nginx.URL + config.common.OUTPUT_PATH,
+                    "c_content": "0",
+                    "c_note": "0",
+                    "c_tile_size": tile_size,
+                    "c_zoom_min": min_zoom,
+                    "c_zoom_max": max_zoom,
+                    "c_auto_zoom": auto_zoom
+                }
+                dms_rep = requests.post(config.dms.URL + '/content/addContent', headers=headers, data=dms_data)
+
+            except Exception as err:
+                traceback.print_exc()
+                print_log('dms添加失败')
 
             return {"code": StatesCode.SUCCESS, "message": "任务添加成功"}
 

+ 3 - 1
src/application.py → src/app/mission_jobs/application.py

@@ -1,3 +1,5 @@
+import os
+
 import rq
 
 from app.mission_jobs.image_slice_job import image_slice_job
@@ -14,10 +16,10 @@ class Application:
         # 影像切片
         config = Config()
         connection = Connection(config)
-
         # 添加任务
         q = rq.Queue(name='default', connection=connection.redis_conn)
         output_path = config.common.OUTPUT_PATH
+
         q.enqueue(image_slice_job,
                   kwargs={"data_path": data_path,
                           "output_path": output_path,

+ 13 - 5
src/mission/image_slice.py → src/app/mission_jobs/image_slice.py

@@ -2,11 +2,12 @@ import os
 import shutil
 
 from app.defines import TILE_GRID_TYPE
-from app.utils.create_geojsonl import create_default_geojsonl, calc_minmax_zoom2, verify_geotiff
+from app.utils.create_geojsonl import create_default_geojsonl, calc_minmax_zoom2, verify_geotiff, calc_minmax_zoom
 from config import Config
 from starearth import FileSystem, Grid
 from starearth.storage.osm_zxy import StorageOSMZXY
 from slice import slice
+from starearth.utils.general_utils import print_log
 
 config = Config()
 
@@ -26,6 +27,8 @@ def slice_zxy(
         channels=[1, 2, 3],
         merging=3,
 ):
+    print_log('开始切片...')
+
     epsg = verify_geotiff(input_file, None)
 
     filesystem_cache = FileSystem(config.common.CACHE_PATH)
@@ -39,7 +42,8 @@ def slice_zxy(
     sliceTiler_type = sliceTiler_type_dict[tile_grid]
 
     if auto_zoom:
-        min_zoom, max_zoom = calc_minmax_zoom2(input_file, epsg, tile_size)
+        # min_zoom, max_zoom = calc_minmax_zoom2(input_file, epsg, tile_size)
+        min_zoom, max_zoom = calc_minmax_zoom(input_file, epsg, tile_size)
 
     geojsonl = os.path.splitext(input_file)[0] + '.geojsonl'
 
@@ -62,11 +66,13 @@ def slice_zxy(
             render_type=render_type,
             lut=None,
         )
+    print_log('执行切片完成.')
 
     base_name = os.path.splitext(os.path.basename(input_file))[0]
+    print_log("开始OSM_ZXY存储..")
 
     storage_osmzxy_obj = StorageOSMZXY(
-        tiles_path=os.path.join(tmp_tiles, base_name),
+        tiles_path=os.path.join(str(tmp_tiles), base_name),
         min_zoom=min_zoom,
         max_zoom=max_zoom,
         merging=merging,
@@ -76,5 +82,7 @@ def slice_zxy(
     storage_osmzxy_obj.storage()
 
     # 删除临时目录
-    if os.path.exists(os.path.join(tmp_tiles, base_name)):
-        shutil.rmtree(os.path.join(tmp_tiles, base_name))
+    # if os.path.exists(os.path.join(str(tmp_tiles), base_name)):
+    #     shutil.rmtree(os.path.join(str(tmp_tiles), base_name))
+
+    print_log("OSM_ZXY存储完毕..")

+ 5 - 6
src/app/mission_jobs/image_slice_job.py

@@ -2,22 +2,20 @@ import os
 
 import rq
 
+from app.mission_jobs.image_slice import slice_zxy
 from config import Config
 from connection import Connection
-from mission.image_slice import slice_zxy
 
 
 def image_slice_job(data_path, output_path, tile_size, tile_grid, tile_format, auto_zoom, min_zoom, max_zoom):
-
     config = Config()
     connection = Connection(config)
 
     # 添加任务
     q = rq.Queue(name='default', connection=connection.redis_conn)
-
-    for file_name in data_path:
+    for file_name in os.listdir(data_path):
         input_file = os.path.join(data_path, file_name)
-        if os.path.splitext(input_file)[-1] == ".tif":
+        if os.path.splitext(file_name)[-1] == ".tif":
             q.enqueue(slice_zxy,
                       kwargs={"input_file": input_file,
                               "output_path": output_path,
@@ -26,5 +24,6 @@ def image_slice_job(data_path, output_path, tile_size, tile_grid, tile_format, a
                               "tile_format": tile_format,
                               "auto_zoom": auto_zoom,
                               "min_zoom": min_zoom,
-                              "max_zoom": max_zoom}
+                              "max_zoom": max_zoom},
+                      job_timeout=int(config.common.JOB_TIMEOUT)
                       )

+ 28 - 0
src/app/utils/create_geojsonl.py

@@ -119,6 +119,34 @@ def create_default_geojsonl(input_file, geojsonl, properties=None):
     pass
 
 
+def calc_minmax_zoom(new_input_file, epsg, image_tile_size):
+    '''
+    自动计算切片级别
+    :param new_input_file: 输入的影像文件
+    :param epsg: 输入影像的投影,形如`EPSG:3857`
+    :return: 最小级别min_z, 最大级别max_z
+    '''
+    ds = gdal.Open(new_input_file)
+    if epsg != 'EPSG:3857':
+        from_srs = osr.SpatialReference()
+        from_srs.ImportFromEPSG(int(epsg.split(':')[1]))
+        to_srs = osr.SpatialReference()
+        to_srs.ImportFromEPSG(3857)
+        ds = gdal.AutoCreateWarpedVRT(ds, from_srs.ExportToWkt(), to_srs.ExportToWkt())
+
+    gt = ds.GetGeoTransform()
+
+    mercator = NewGlobalMercator(tile_size=image_tile_size)
+
+    # 根据影像的大小和瓦片大小确定最小切片级别
+    min_z = mercator.ZoomForPixelSize(gt[1] * max(ds.RasterXSize, ds.RasterYSize) / float(image_tile_size))
+
+    # 根据分辨率确定最大切片级别
+    max_z = mercator.ZoomForPixelSize(gt[1])
+
+    return min_z, max_z
+
+
 def calc_minmax_zoom2(input_file, epsg, image_tile_size):
     """
     自动计算切片级别2

+ 12 - 0
src/app/utils/utils.py

@@ -0,0 +1,12 @@
+import os
+
+
+def get_directory_tree(path):
+    tree = {'name': os.path.basename(path)}
+    if os.path.isdir(path):
+        tree['type'] = 'directory'
+        tree['children'] = [get_directory_tree(os.path.join(path, child)) for child in os.listdir(path)]
+
+    else:
+        tree['type'] = 'file'
+    return tree

+ 15 - 15
src/connection.py

@@ -11,12 +11,12 @@ class Connection:
         self.redis_port = config.redis.PORT
         self.redis_db = config.redis.DB
 
-        # db配置信息
-        self.db_host = config.database.HOST
-        self.db_port = config.database.PORT
-        self.db_user = config.database.USER
-        self.db_pwd = config.database.PASSWORD
-        self.db_db = config.database.DB
+        # # db配置信息
+        # self.db_host = config.database.HOST
+        # self.db_port = config.database.PORT
+        # self.db_user = config.database.USER
+        # self.db_pwd = config.database.PASSWORD
+        # self.db_db = config.database.DB
 
     def __call__(self, *args, **kwargs):
         redis = Redis \
@@ -48,12 +48,12 @@ class Connection:
 
         return Redis(host=self.redis_host, port=self.redis_port, db=self.redis_db, health_check_interval=30)
 
-    @property
-    def db_uri(self):
-        """获取postgressql的uri"""
-
-        return "postgresql+psycopg2://{user}:{pwd}@{host}:{port}/{db}".format(user=self.db_user,
-                                                                              pwd=self.db_pwd,
-                                                                              host=self.db_host,
-                                                                              port=self.db_port,
-                                                                              db=self.db_db)
+    # @property
+    # def db_uri(self):
+    #     """获取postgressql的uri"""
+    #
+    #     return "postgresql+psycopg2://{user}:{pwd}@{host}:{port}/{db}".format(user=self.db_user,
+    #                                                                           pwd=self.db_pwd,
+    #                                                                           host=self.db_host,
+    #                                                                           port=self.db_port,
+    #                                                                           db=self.db_db)

+ 43 - 43
src/manage.py

@@ -1,43 +1,43 @@
-from urllib import parse
-
-from flask import current_app
-from sqlalchemy import create_engine, insert
-from sqlalchemy.orm import Session
-from sqlalchemy_utils import database_exists, create_database
-
-from app import Config
-from app.modle import Base
-from app.modle.users import User
-from app.webapp import application
-
-
-@application.cli.command('dbinit')
-def dbinit():
-    """初始化数据库,存入任务模板参数"""
-
-    print('initialize db ...')
-
-    config = Config()
-
-    uri = f'postgresql+psycopg2://{config.database.USER}:{parse.quote(config.database.PASSWORD)}@{config.database.HOST}:{config.database.PORT}/{config.database.DB}'
-
-    engine = create_engine(uri)
-
-    # 判断数据库是否存在,如果不存在则创建一个
-    if not database_exists(engine.url):
-        create_database(engine.url)
-
-    Base.metadata.create_all(engine)
-
-    print('initialize db ok ...\n')
-
-    print('Please Enter Ctrl+C to exit ...')
-
-
-@application.cli.command('ceshi')
-def ceshi():
-    print('this is a test')
-
-
-if __name__ == '__main__':
-    dbinit()
+# from urllib import parse
+#
+# from flask import current_app
+# from sqlalchemy import create_engine, insert
+# from sqlalchemy.orm import Session
+# from sqlalchemy_utils import database_exists, create_database
+#
+# from app import Config
+# from app.modle import Base
+# from app.modle.users import User
+# from app.webapp import application
+#
+#
+# @application.cli.command('dbinit')
+# def dbinit():
+#     """初始化数据库,存入任务模板参数"""
+#
+#     print('initialize db ...')
+#
+#     config = Config()
+#
+#     uri = f'postgresql+psycopg2://{config.database.USER}:{parse.quote(config.database.PASSWORD)}@{config.database.HOST}:{config.database.PORT}/{config.database.DB}'
+#
+#     engine = create_engine(uri)
+#
+#     # 判断数据库是否存在,如果不存在则创建一个
+#     if not database_exists(engine.url):
+#         create_database(engine.url)
+#
+#     Base.metadata.create_all(engine)
+#
+#     print('initialize db ok ...\n')
+#
+#     print('Please Enter Ctrl+C to exit ...')
+#
+#
+# @application.cli.command('ceshi')
+# def ceshi():
+#     print('this is a test')
+#
+#
+# if __name__ == '__main__':
+#     dbinit()

+ 2 - 4
src/slice.py

@@ -146,7 +146,6 @@ def slice \
     # min_zoom, max_zoom = 12, 13  # 测试用,手动设置一个级别范围
     print_log('min-max:%s-%s' % (min_zoom, max_zoom))
 
-    print_log('开始切片...')
     # star_time = time.time()
 
     # 选择不同的渲染类型
@@ -229,10 +228,9 @@ def slice \
 
         json.dump(tiles, fp)
 
-    tiles_nums = 0
-    tiles_nums += len(tiles)
+    # tiles_nums = 0
+    # tiles_nums += len(tiles)
 
-    print_log('执行切片完成.')
     # end_time = time.time()
 
     # print_log('切片数量为:{} 个'.format(tiles_nums))

+ 66 - 20
src/starearth/storage/osm_zxy.py

@@ -1,9 +1,18 @@
+import multiprocessing
 import os
 import json
+
+import numpy
 from PIL import Image
 from tilecloud import TileStore, Tile, TileCoord
 
 
+def cbe(exception):
+    print(exception)
+
+    pass
+
+
 class StorageOSMZXY:
     def __init__(self, tiles_path, min_zoom, max_zoom, merging, output_path, tile_format):
         self.tiles_path = tiles_path
@@ -13,26 +22,7 @@ class StorageOSMZXY:
         self.output_path = output_path
         self.tile_format = tile_format
 
-    def storage(self):
-        # 开始处理切片目录下的瓦片
-
-        date_dir_path = os.path.join(self.tiles_path, 'tiles')
-        tile_list_path = os.path.join(self.tiles_path, 'tiles_list.json')
-
-        # 获取瓦片列表
-        with open(tile_list_path, 'r') as fb:
-            tile_list = json.load(fb)
-
-        # 拼接瓦片源路径和目的路径
-        local_tiles_src = "file://{}/%(z)d_%(y)d_%(x)d.{}".format(date_dir_path, self.tile_format)
-        local_tiles_dst = "file://{}/%(z)d/%(x)d/%(y)d.{}".format(self.output_path, self.tile_format)
-
-        ts_input = TileStore.load(local_tiles_src)
-        ts_output = TileStore.load(local_tiles_dst)
-
-        # 使用瓦片列表构造Tile对象
-        tiles = [Tile(TileCoord(z, x, y)) for x, y, z in tile_list]
-
+    def _run_mp(self, tiles, ts_input, ts_output, local_tiles_dst, local_tiles_src):
         for tile in tiles:
             tile = ts_input.get_one(tile)
 
@@ -85,3 +75,59 @@ class StorageOSMZXY:
             else:
                 # 存储瓦片
                 ts_output.put_one(tile)
+
+    def storage(self):
+        # 开始处理切片目录下的瓦片
+
+        date_dir_path = os.path.join(self.tiles_path, 'tiles')
+        tile_list_path = os.path.join(self.tiles_path, 'tiles_list.json')
+
+        # 获取瓦片列表
+        with open(tile_list_path, 'r') as fb:
+            tile_list = json.load(fb)
+
+        # 拼接瓦片源路径和目的路径
+        local_tiles_src = "file://{}/%(z)d_%(y)d_%(x)d.{}".format(date_dir_path, self.tile_format)
+        local_tiles_dst = "file://{}/%(z)d/%(x)d/%(y)d.{}".format(self.output_path, self.tile_format)
+
+        ts_input = TileStore.load(local_tiles_src)
+        ts_output = TileStore.load(local_tiles_dst)
+
+        # 使用瓦片列表构造Tile对象
+        tiles = [Tile(TileCoord(z, x, y)) for x, y, z in tile_list]
+
+        datas = numpy.array_split(tiles, self.max_zoom - self.min_zoom)
+
+        # for data in datas:
+        #     process = multiprocessing.Process(
+        #         target=self._run_mp,
+        #         args=(
+        #             data,
+        #             ts_input,
+        #             ts_output,
+        #             local_tiles_src,
+        #             local_tiles_dst
+        #         )
+        #     )
+        #     print(111)
+        #     process.start()
+        #     process.join()
+
+        pool = multiprocessing.Pool(self.max_zoom - self.min_zoom)
+
+        for data in datas:
+            pool.apply_async \
+                    (
+                    self._run_mp,
+                    (
+                        data,
+                        ts_input,
+                        ts_output,
+                        local_tiles_src,
+                        local_tiles_dst
+                    ),
+                    error_callback=cbe
+                )
+
+        pool.close()
+        pool.join()

+ 0 - 0
src/utils/__init__.py