Forráskód Böngészése

点组坐标转换

zhangnaiwen 2 éve
szülő
commit
686d7eb1fb

+ 6 - 0
Dockerfile

@@ -0,0 +1,6 @@
+FROM coordinate_transformation_base:1.0.0
+
+COPY ./conf/uwsgi.ini /work/coordinate_transformation/conf/uwsgi.ini
+COPY ./src /work/coordinate_transformation/src
+
+WORKDIR /work/coordinate_transformation/src

+ 30 - 0
base/Dockerfile

@@ -0,0 +1,30 @@
+FROM debian11_py3.9.13_gdal3.4.3_small:1.0.0
+
+COPY ./requirements.txt /tmp/
+COPY ./conf/bootstrap.sh /opt/bootstrap.sh
+COPY ./conf/supervisord.conf /etc/
+COPY ./conf/supervisor.conf /etc/supervisord.d/supervisor.conf
+
+RUN apt-get -y update \
+    && apt-get -y upgrade \
+    && apt-get install -y supervisor libpq-dev python3-dev gcc \
+    && pip3 install -r /tmp/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple \
+    && pip3 install uwsgi -i https://pypi.tuna.tsinghua.edu.cn/simple \
+    # 默认是128,当server处理请求较慢,以至于socket监听队列被填满后,新来的请求会被拒绝。
+    && mkdir /opt/init  \
+    && echo "echo 2048 > /proc/sys/net/core/somaxconn" > /opt/init/sysctl.sh  \
+    && chmod +x /opt/bootstrap.sh \
+    # 清理和删除工作
+    && apt-get autoremove -y  \
+    && apt-get autoclean -y \
+    && apt-get clean -y \
+    && rm -rf /tmp/* /var/tmp/*  \
+    && find /var/cache/apt/archives /var/lib/apt/lists -not -name lock -type f -delete  \
+    && find /var/cache -type f -delete \
+    && find /var/log -type f | while read f; do echo -n '' > ${f}; done \
+    && rm -rf /var/lib/apt/lists/* \
+    && rm -rf ~/.cache/pip/*
+
+ENTRYPOINT ["/opt/bootstrap.sh"]
+
+EXPOSE 5000

+ 47 - 0
conf/bootstrap.sh

@@ -0,0 +1,47 @@
+#!/bin/bash
+
+set -e
+set -u
+
+# supervisord 配置文件
+SUPERVISOR_PARAMS='-c /etc/supervisord.conf'
+
+
+# 创建一些需要的目录
+mkdir -p /data/conf /data/run /data/logs/
+chmod 711 /data/conf /data/run /data/logs
+
+#export WORKER_REDIS_PORT=${REDIS_PORT:6}
+#export WORKER_REDIS_DB=${REDIS_DB:-0}
+
+# 遍历 `/opt/init/*.sh`,然后执行它
+if [ "$(ls /opt/init/)" ]; then
+  for cmd in /opt/init/*.sh; do
+    . $cmd
+  done
+fi
+
+
+# 可能会使用一个交互式的容器.
+if test -t 0; then
+  # 运行 supervisord 到后台
+  supervisord $SUPERVISOR_PARAMS
+
+  # 运行一些命令并退出
+  # 没有命令时,运行bash
+  if [[ $@ ]]; then
+    eval $@
+  else
+    export PS1='[\u@\h : \w]\$ '
+    /bin/bash
+  fi
+
+# 运行 supervisord 在前台, 保持直到停止容器.
+else
+  # 有额外的参数,先执行它.
+  # 可能会有些问题
+  if [[ $@ ]]; then
+    eval $@
+  fi
+  supervisord -n $SUPERVISOR_PARAMS
+fi

+ 8 - 0
conf/supervisor.conf

@@ -0,0 +1,8 @@
+[program:coordinate_transformation]
+command = uwsgi /work/coordinate_transformation/conf/uwsgi.ini
+
+autorestart = true
+redirect_stderr = true
+stopsignal = QUIT
+stderr_logfile = /data/logs/coordinate_transformation_error.log
+stdout_logfile = /data/logs/coordinate_transformation.log

+ 18 - 0
conf/supervisord.conf

@@ -0,0 +1,18 @@
+[supervisord]
+pidfile = /run/supervisord.pid
+logfile = /data/logs/supervisord.log
+# Set loglevel=debug, only then all logs from child services are printed out
+# to container logs (and thus available via `docker logs [container]
+loglevel = debug
+
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+
+[inet_http_server]
+port:127.0.0.1:9999
+
+[supervisorctl]
+serverurl=http://127.0.0.1:9999
+
+[include]
+files = /etc/supervisord.d/*.conf

+ 131 - 0
conf/uwsgi.ini

@@ -0,0 +1,131 @@
+[uwsgi]
+# 指定应用的用户(组)
+if-env = UWSGI_USER
+uid = $(UWSGI_USER)
+endif =
+
+if-env = UWSGI_GROUP
+gid = $(UWSGI_GROUP)
+endif =
+
+# 监听的ip与端口
+http = :5000
+if-env = UWSGI_HTTP
+http = $(UWSGI_HTTP)
+endif =
+
+# PID文件
+pidfile = /var/run/coordinate_transformation.pid
+
+# 主入口模块
+module = app.webapp
+
+# 主入口函数
+callable = application
+
+# 需要关闭master,否则会导致进程中使用全局变量会出错
+master = true
+
+# 这个会导致不正常,待测试
+#async=128
+
+# 进程数
+processes = 2
+if-env = UWSGI_PROCESSES
+processes = $(UWSGI_PROCESSES)
+endif =
+
+# 运行多线程
+enable-threads = 0
+if-env = UWSGI_ENABLE_THREADS
+enable-threads = $(UWSGI_ENABLE_THREADS)
+endif =
+
+# http的线程数
+http-processes = 2
+if-env = UWSGI_HTTP_PROCESSES
+http-processes = $(UWSGI_HTTP_PROCESSES)
+endif =
+
+# 是否保持http连接
+http-keepalive = 0
+if-env = UWSGI_HTTP_KEEPALIVE
+http-keepalive = $(UWSGI_HTTP_KEEPALIVE)
+endif =
+
+# 超时关闭连接的时间(秒)
+harakiri = 20
+if-env = UWSGI_HARAKIRI
+harakiri = $(UWSGI_HARAKIRI)
+endif =
+
+# 连接时间
+http-timeout = 60
+if-env = UWSGI_TIMEOUT
+http-timeout = $(UWSGI_TIMEOUT)
+endif =
+
+# 打开http body缓冲
+if-env = UWSGI_POST_BUFFERING
+post-buffering = $(UWSGI_POST_BUFFERING)
+endif =
+
+# python解释器的优化,=0不优化
+if-env = UWSGI_PY_OPTIMIZE
+optimize = $(UWSGI_PY_OPTIMIZE)
+endif =
+
+# 关闭http请求的日志
+if-env = UWSGI_DISABLE_LOGGING
+disable-logging = $(UWSGI_DISABLE_LOGGING)
+endif =
+
+# 记录uwsgi自身的日志
+logto = /data/logs/coordinate_transformation.log
+# daemonize = /data/logs/nginx_agent.log
+
+
+# 等待其它进程重启(直到接收到的请求处理完才重启)/关闭的最大时间(秒)
+reload-mercy = 2
+
+# CPU亲和性
+cpu-affinity = 1
+
+# 进程的内存限制(address space/vsz)
+if-env = UWSGI_LIMIT_AS
+limit-as = $(UWSGI_LIMIT_AS)
+endif =
+
+# 占用内存(address space)大于指定值(MB),重启服务
+if-env = UWSGI_RELOAD_ON_AS
+reload-on-as = $(UWSGI_RELOAD_ON_AS)
+endif =
+
+# 占用内存(rss)大于指定值(MB),重启服务
+if-env = UWSGI_RELOAD_ON_RSS
+reload-on-rss = $(UWSGI_RELOAD_ON_RSS)
+endif =
+
+# 超过指定请求数,会创建新的进程
+if-env = UWSGI_MAX_REQUESTS
+max-requests = $(UWSGI_MAX_REQUESTS)
+endif =
+
+# 退出时清除环境(自动删除unix socket文件和pid文件)
+vacuum = true
+
+# python代码变化后自动重启服务,仅用用开发环境
+if-env = UWSGI_PY_AUTORELOAD
+py-autoreload = $(UWSGI_PY_AUTORELOAD)
+endif =
+
+# master进程关闭会自动杀死workers
+no-orphans = true
+
+# 设置socket的监听队列大小
+if-env = UWSGI_LISTEN
+listen = $(UWSGI_LISTEN)
+endif =
+
+# python包环境
+pythonpath=/work/coordinate_transformation/src

+ 1 - 0
config/上海2000.prj

@@ -0,0 +1 @@
+PROJCRS["sh2000",BASEGEOGCRS["G_SH2000",DATUM["D_SH2000",ELLIPSOID["S_SH2000",6378245,298.3,LENGTHUNIT["metre",1,ID["EPSG",9001]]]],PRIMEM["Greenwich",0,ANGLEUNIT["Degree",0.0174532925199433]]],CONVERSION["unnamed",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["Degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",121.2751921,ANGLEUNIT["Degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",1,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",-3457147.81,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["(E)",east,ORDER[1],LENGTHUNIT["metre",1,ID["EPSG",9001]]],AXIS["(N)",north,ORDER[2],LENGTHUNIT["metre",1,ID["EPSG",9001]]]]

+ 1 - 0
config/城地转84.prj

@@ -0,0 +1 @@
+PROJCRS["sh2000",BASEGEOGCRS["G_SH2000",DATUM["D_SH2000",ELLIPSOID["S_SH2000",6378245,298.3,LENGTHUNIT["metre",1,ID["EPSG",9001]]]],PRIMEM["Greenwich",0,ANGLEUNIT["Degree",0.0174532925199433]]],CONVERSION["unnamed",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["Degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",121.2751921,ANGLEUNIT["Degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",1,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",-18303.135,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",-3457098.5,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["(E)",east,ORDER[1],LENGTHUNIT["metre",1,ID["EPSG",9001]]],AXIS["(N)",north,ORDER[2],LENGTHUNIT["metre",1,ID["EPSG",9001]]]]

+ 26 - 0
docker-compose-v3.yml

@@ -0,0 +1,26 @@
+version: '3'
+services:
+  sky-gistools-server:
+      container_name: coordinate_transformation
+
+      image: coordinate_transformation:1.0.0
+
+      ports:
+        - "5000:5000"
+
+      environment:
+        - UWSGI_PROCESSES=8
+        - UWSGI_ENABLE_THREADS=true
+        - UWSGI_HTTP_PROCESSES=4
+        - UWSGI_HTTP_KEEPALIVE=false
+        - UWSGI_HARAKIRI=600
+        - UWSGI_TIMEOUT=600
+        - UWSGI_PY_OPTIMIZE=0
+
+      volumes:
+#        - /data/logs:/data/logs # 日志
+        - ./config:/work/coordinate_transformation/config  # 配置文件目录,包括软件配置信息
+
+      privileged: true
+
+      restart: always

+ 25 - 0
docker-compose.yml

@@ -0,0 +1,25 @@
+service:
+  container_name: coordinate_transformation
+
+  image: coordinate_transformation:1.0.0
+
+  ports:
+    - "5000:5000"
+
+  environment:
+    - UWSGI_PROCESSES=8
+    - UWSGI_ENABLE_THREADS=true
+    - UWSGI_HTTP_PROCESSES=4
+    - UWSGI_HTTP_KEEPALIVE=false
+    - UWSGI_HARAKIRI=600
+    - UWSGI_TIMEOUT=600
+    - UWSGI_PY_OPTIMIZE=0
+
+  volumes:
+#    - /data/logs:/data/logs # 日志
+    - ./config:/work/coordinate_transformation/config  # 配置文件目录,包括软件配置信息
+
+
+  privileged: true
+
+  restart: always

+ 2 - 0
requirements.txt

@@ -0,0 +1,2 @@
+flask_restx
+flask_cors

+ 4 - 0
scripts/built.sh

@@ -0,0 +1,4 @@
+#!/bin/bash
+
+docker build -f base/Dockerfile -t coordinate_transformation_base:1.0.0 .
+docker build -t coordinate_transformation:1.0.0 .

+ 0 - 0
src/__init__.py


+ 14 - 0
src/app/__init__.py

@@ -0,0 +1,14 @@
+from flask import Flask
+from flask_cors import CORS
+
+from app.api import api
+
+
+def create_app():
+    """创建app并初始化相关配置参数"""
+
+    app = Flask(__name__)
+    CORS(app)
+    api.init_app(app)
+
+    return app

+ 6 - 0
src/app/api/__init__.py

@@ -0,0 +1,6 @@
+from flask_restx import Api
+
+from app.api.transformation import ns as transformation
+api = Api(version='v1.0', title='', description='', doc='/api')
+
+api.add_namespace(transformation)

+ 88 - 0
src/app/api/transformation.py

@@ -0,0 +1,88 @@
+import json
+import os
+
+from osgeo import osr
+from flask import request, jsonify
+from flask_restx import Resource, Namespace, reqparse, fields
+
+from app.defines import StatesCode
+
+ns = Namespace('conversion', description='坐标转换 API接口')
+
+coords_list = reqparse.RequestParser(bundle_errors=True)
+coords_list.add_argument('coords_list', type=str, required=True, location='args', help='点数组 [[x ,y], [x, y]]')
+
+
+@ns.route('/sh2000_to_wgs84')
+class Sh2000ToWgs84(Resource):
+
+    @ns.doc(description='上海2000转wgs84')
+    @ns.expect(coords_list)
+    # @ns.marshal_with()
+    def get(self):
+        """上海2000转wgs84"""
+
+        coords_list = request.args.get('coords_list')
+
+        if coords_list:
+            coords_list = json.loads(coords_list)
+        else:
+            return jsonify(code=StatesCode.PARA_ERROR, message='坐标点数组不能为空')
+        try:
+            wkt_path = os.path.join(
+                os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))),
+                'config', '上海2000.prj'
+            )
+
+            with open(wkt_path, 'r') as f:
+                wkt_str = f.read()
+
+            srs_source = osr.SpatialReference()
+            srs_source.ImportFromWkt(wkt_str)
+
+            srs_dest = osr.SpatialReference()
+            srs_dest.ImportFromEPSG(4326)
+
+            transform = osr.CoordinateTransformation(srs_source, srs_dest)
+            coords = transform.TransformPoints(coords_list)
+
+            return jsonify(code=StatesCode.SUCCESS, message='成功', data=coords)
+        except Exception as e:
+            return jsonify(code=StatesCode.UNKNOWN_ERROR, message='失败', data=str(e))
+
+
+@ns.route('/urban construction_to_wgs84')
+class UrbanConstruction(Resource):
+
+    @ns.doc(description='城地转wgs84')
+    @ns.expect(coords_list)
+    def get(self):
+        """城地转wgs84"""
+        coords_list = request.args.get('coords_list')
+
+        if coords_list:
+            coords_list = json.loads(coords_list)
+        else:
+            return jsonify(code=StatesCode.PARA_ERROR, message='坐标点数组不能为空')
+
+        try:
+            wkt_path = os.path.join(
+                os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))),
+                'config', '城地转84.prj'
+            )
+
+            with open(wkt_path, 'r') as f:
+                wkt_str = f.read()
+
+            srs_source = osr.SpatialReference()
+            srs_source.ImportFromWkt(wkt_str)
+
+            srs_dest = osr.SpatialReference()
+            srs_dest.ImportFromEPSG(4326)
+
+            transform = osr.CoordinateTransformation(srs_source, srs_dest)
+            coords = transform.TransformPoints(coords_list)
+
+            return jsonify(code=StatesCode.SUCCESS, message='成功', data=coords)
+        except Exception as e:
+            return jsonify(code=StatesCode.UNKNOWN_ERROR, message='失败', data=str(e))

+ 6 - 0
src/app/defines/__init__.py

@@ -0,0 +1,6 @@
+class StatesCode:
+    SUCCESS = 0  # 成功
+    UNKNOWN_ERROR = -1  # 未知错误
+    PARA_MISSING = -2  # 参数缺失
+    PARA_ERROR = -3  # 参数错误
+

+ 0 - 0
src/app/helpers/__init__.py


+ 11 - 0
src/app/helpers/request_handlers.py

@@ -0,0 +1,11 @@
+from flask import request, g, jsonify
+
+
+def configure(app):
+    @app.before_request
+    def authenticate():
+        """
+        这里验证token
+        :return:
+        """
+        pass

+ 5 - 0
src/app/modle/__init__.py

@@ -0,0 +1,5 @@
+from sqlalchemy.orm import DeclarativeBase
+
+
+class Base(DeclarativeBase):
+    pass

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


+ 6 - 0
src/app/webapp.py

@@ -0,0 +1,6 @@
+from app import create_app
+
+application = create_app()
+
+if __name__ == '__main__':
+    application.run(host="0.0.0.0", debug=True)

+ 50 - 0
src/config.py

@@ -0,0 +1,50 @@
+import yaml
+
+
+class Object(dict):
+
+    def __init__(self):
+
+        super(Object, self).__init__()
+
+    def __getattr__(self, key):
+
+        return self.get(key)
+
+    def __setattr__(self, key, value):
+
+        if isinstance(value, dict):
+
+            o = Object()
+
+            o.set(value)
+
+            self[key] = o
+
+        else:
+
+            self[key] = value
+
+    def set(self, dictionary, clear=False):
+
+        if clear:
+            self.clear()
+
+        for key, value in dictionary.items():
+            self.__setattr__(key, value)
+
+
+class Config(Object):
+    _instance = None
+
+    def __new__(cls, *args, **kwargs):
+        if Config._instance is None:
+            Config._instance = super().__new__(cls)
+
+        return Config._instance
+
+    def load(self, filename):
+        with open(filename, 'r', encoding='utf8') as stream:
+            data = yaml.load(stream, Loader=yaml.FullLoader)
+
+            self.set(data)

+ 0 - 0
src/manage.py


+ 4 - 0
src/version.py

@@ -0,0 +1,4 @@
+# VERSION = "1.0.1r"
+# PROJECT_NAME = "sky-gistools-server"
+# DESCRIPTION = "gis多算法、坐标转换、几何检索等功能"
+# RELEASE_TIME = "2023-03-2 16:35:03"