在前端实现地理分析功能——Cesium,ArcPy,Flask实现通视分析(LOS)与缓冲区分析(Buffer)

准备工作:

以本机为例

  1. 安装Cesium1.4.5,并配置服务(添加地形需要开启Cesium服务)
  2. 安装ArcMap10.4,对应Arcpy为2.7
  3. 安装配置ArcServer(计算结果为server服务)
  4. 安装Flask框架(可以直接在Pycharm里面安装Flask插件)

正文:

1 数据准备

使用工具:arcmap,arcserver,tif转terrain工具;

准备数据:地形数据点云.shp

1.1 地形数据处理

地形数据点云.shp —> 地形.tin  —>  地形.tif  —> 地形.terrain (具体方法就不细说了)

把“地形.terrain”文件夹放到..\Cesium1.45\Apps\mapserver目录下,以备后续使用

1.2 发布LOS服务

P.S.在磁盘跟目录新建文件夹ARCGISDATA,存放所有arcmap用到的文件

(1)3D分析工具 —> 可见性 —> 通视分析工具 —> 输入“分析路径line.shp”(line不能超出地形.tif范围)

得到通视分析结果(两个.shp文件,一条线,一个点)

(2)发布

勾选需要发布的要素

发布结果

Buffer同理

2 构建前端页面

使用工具:vscode或pycharm(IDE随意),Cseium,ArcServer

2.1加载地形

先启动Cesium服务,以后使用是一定要记得开启Cesium服务,不然地图不会加载的。

Cesium使用方法不细说了,这里附上地形加载代码

<script>
   //还记得前面强调的地形文件存放地址吗,现在用到了
   //localhost:8080是Cesium服务默认端口
     var tp = new Cesium.CesiumTerrainProvider({
        url: "http://localhost:8080/Apps/mapserver/dailian11/大连11"
    });

    var viewer = new Cesium.Viewer('cesiumContainer', {
        animation: false,
        baseLayerPicker: true,
        fullscreenButton: false,
        geocoder: false,
        homeButton: true,
        infoBox: true,
        sceneModePicker: true,
        selectionIndicator: true,
        timeline: false,
        navigationHelpButton: false,
        navigationInstructionsInitiallyVisible: false,
     //加载地形
        terrainProvider: tp,
        imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
            url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"
        })
    });
</script>

查看地形是否加载

 2.2加载通视分析,缓冲区分析结果

通视分析和缓冲区分析结果采用异步加载的方式,代码如下

HTML

    <div class="shell">
        <h5>&nbsp; 分析结果加载&nbsp;</h5>
        &emsp;<button type="button" id="buttonLOS03">LOS</button>&emsp;<br />
        &emsp;<button type="button" id="buttonBuffer01">Buffer</button>&emsp;<br />
        &emsp;<button type="button" id="LOSCLEAR">CLEAR</button><br />&emsp;
        <div class="LOSscri">
        </div>
        <div class="LOSscribuffer">
        </div>
        </button>
    </div>
   <div class="shell3">
    <div class="btn-group" style="width: 400px">
        <hr />
        <h4>
            <strong>&emsp;
                <button type="button" class="btn No1point">
                    第一个点
                </button>
            </strong>
            : (<span id='aa'>经度</span>,<span id='bb'>纬度</span>)
        </h4>
        <h4>
            <strong>&emsp;
                <button type="button" class="btn No2point">
                    第二个点
                </button>
            </strong>
            : (<span id='cc'>经度</span>,<span id='dd'>纬度</span>)
            &emsp;<br />
            <span id="result02">&emsp;</span>
        </h4>
        <h4>
            &emsp;<button type="button" class="btn No3point">
                LOS计算
            </button>
            &emsp;<button type="button" class="btn No4point">
                缓冲区计算
            </button>
        </h4>
        <hr />
    </div>
  </div>

CSS

    .shell {
        position: fixed;
        background-color: rgba(216, 207, 207, 1);
        left: 5%;
        top: 10%;
        width: auto;
        height: auto;
        border: #080202 solid 5px;
    }
    .shell3{
        position: fixed;
        background-color:rgba(216, 207, 207, 1);
        right: 5%;
        bottom: 12%;
        width: auto;
        height: auto;
        border: #080202 solid 3px;
     } 

JS

计算模块:

    //获取鼠标位置
    function getPosition() {
        //得到当前三维场景
        var scene = viewer.scene;
        //得到当前三维场景的椭球体
        var ellipsoid = scene.globe.ellipsoid;
        //设置样式
        var entity = viewer.entities.add({
            label: {
                show: false,
                font: '24px Helvetica',
                fillColor: Cesium.Color.SKYBLUE,
                outlineColor: Cesium.Color.BLACK,
                outlineWidth: 2,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                pixelOffset: new Cesium.Cartesian2(200, -50)
            }
        });
        var longitudeString = null;
        var latitudeString = null;
        var cartesian = null;
        // 定义当前场景的画布元素的事件处理
        var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
        //设置鼠标移动事件的处理函数,这里负责监听x,y坐标值变化

        handler.setInputAction(function (movement) {
            //通过指定的椭球或者地图对应的坐标系,将鼠标的二维坐标转换为对应椭球体三维坐标
            cartesian = viewer.camera.pickEllipsoid(movement.endPosition, ellipsoid);
            if (cartesian) {
                //将笛卡尔坐标转换为地理坐标
                var cartographic = ellipsoid.cartesianToCartographic(cartesian);
                // //将弧度转为度的十进制度表示
                longitudeString = Cesium.Math.toDegrees(cartographic.longitude).toFixed(6);
                latitudeString = Cesium.Math.toDegrees(cartographic.latitude).toFixed(6);
                //获取地理坐标和高度
                // lonunit_m = cartesian.longitude;
                // latunit_m = cartesian.latitude;
                height = scene.globe.getHeight(cartographic);
                entity.position = cartesian;
                entity.label.show = true;
                // entity.label.text = cartesia + '(' + cartographic.longitude + ', ' + cartographic.latitude + "," + height + ')';
                entity.label.text =
                    'Cartesian: ' + cartesian + '\u00B0' +
                    '\nLon: ' + longitudeString +
                    '\nLat: ' + latitudeString +
                    '\nHeight: ' + height + '\n' + '\n' + '\n' + '\n';
            } else {
                entity.label.show = false;
            }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        // 坐标输出
        $(function () {
            $(".No1point").click(function (event) {
                $(window).unbind("dblclick");
                $(window).dblclick(function (event) {
                    $("#aa").html(longitudeString);
                    $("#bb").html(latitudeString);
                    var lon = $("#aa").html();
                    var lat = $("#bb").html();
                    var entity = viewer.entities.add({
                        name: 'start point',
                        position: Cesium.Cartesian3.fromDegrees(lon, lat, height),
                        point: {
                            color: Cesium.Color.YELLOW,
                            pixelSize: 30
                        }
                    });
                });
            });
            $(".No2point").click(function (event) {
                $(window).unbind("dblclick");
                $(window).dblclick(function (event) {
                    $("#cc").html(longitudeString);
                    $("#dd").html(latitudeString);
                    var lon = $("#cc").html();
                    var lat = $("#dd").html();
                    var entity = viewer.entities.add({
                        name: 'end point',
                        position: Cesium.Cartesian3.fromDegrees(lon, lat, height),
                        point: {
                            color: Cesium.Color.RED,
                            pixelSize: 30
                        }
                    });
                });
            });
            $(".No3point").click(function (event) {
                $(window).unbind("dblclick");
                // $(window).dblclick(function (event) {
                //     console.log("结束");
                // });
                $("#result02").html("<p>&emsp;正在计算LOS,请等待...</p>");
                $.ajax({
                    // url: 'http://localhost:5000/oneLinetoLOS',
                    url: '/oneLinetoLOS02',
                    // url: '/LinestoLOS',
                    data: {
                        a2: $('#aa').html(),
                        b2: $('#bb').html(),
                        c2: $('#cc').html(),
                        d2: $('#dd').html()
                    },
                    dataType: 'JSON',
                    type: 'GET',
                    success: function () {
                        $("#result02").html("<p>&emsp;计算完成,LOSline03查看</p>");
                    }
                });
            });
            $(".No4point").click(function (event) {
                $(window).unbind("dblclick");
                $("#result02").html("<p>&emsp;正在计算Buffer,请等待...</p>");
                $.ajax({
                    url: '/Buffer01',
                    data: {
                        a3: $('#aa').html(),
                        b3: $('#bb').html(),
                        c3: $('#cc').html(),
                        d3: $('#dd').html()
                    },
                    dataType: 'JSON',
                    type: 'GET',
                    success: function (responseText, status, xhr) {
                        if (responseText != '') {
                            $("#result02").html("<p>&emsp;计算完成,Buffer01查看</p>");
                        } else {
                            $('#result02').html("<p>&emsp;error</p>");
                        }
                    }
                });
            });
        });
    }
    getPosition();
         

ArcServer服务加载模块:

在F:\ARCGISDATA\python\templates目录下新建3个txt文件

LOSlineremove.txt;Buffer01.txt; 80LOSline01.txt

LOSlineremove.txt

    <script>
   viewer.imageryLayers.removeAll();
            var ArcGisMap = new Cesium.ArcGisMapServerImageryProvider({
                url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"
            });
            viewer.imageryLayers.addImageryProvider(ArcGisMap).alpha = 1;
    </script>

Buffer01.txt 

    <script>

    var esriproviderBuffer01 = new Cesium.ArcGisMapServerImageryProvider({
            url: 'https://localhost:6443/arcgis/rest/services/Buffer02/MapServer'
        });
    viewer.imageryLayers.addImageryProvider(esriproviderBuffer01).alpha = 1;

    </script>

80LOSline01.txt 

    <script>
    var esriprovider8001 = new Cesium.ArcGisMapServerImageryProvider({
        url: 'https://localhost:6443/arcgis/rest/services/80LOSline01/MapServer'
    });
    viewer.imageryLayers.addImageryProvider(esriprovider8001).alpha = 1;
    </script>

展示模块:

    $(function () {
        $('button#buttonLOS03').click(function (event) {
            $.ajax({
                url: "/80LOSline01.txt",
                success: function (responseText, status, xhr) {
                    if (responseText != '') {
                        $('.LOSscri').html(responseText);
                    } else {
                        $('.LOSscri').html("<li>没有搜索到</li>");
                    }
                }
            })
        });
        $('button#buttonBuffer01').click(function (event) {
            $.ajax({
                url: "/Buffer01.txt",
                success: function (responseText, status, xhr) {
                    if (responseText != '') {
                        $('.LOSscribuffer').html(responseText);
                    } else {
                        $('.LOSscribuffer').html("<li>没有搜索到</li>");
                    }
                }
            })
        });
        $('button#LOSCLEAR').click(function (event) {
            //alert("键盘键松开触发,HTML: " + $('#search').val());
            $.ajax({
                url: "/LOSlineremove.txt",    //  请求路径
                // type: "GET", //请求方式
                // data: $("form").serialize(),//请求参数
                success: function (responseText, status, xhr) {//异步执行成功执行的回调函数
                   $('.LOSscri').html(responseText);//地图上打标志
                }
            })
        });
    });

2.3成果

3后端Python框架搭建

FLASK框架教程:http://docs.jinkan.org/docs/flask/quickstart.html

ArcPy帮助文档:http://desktop.arcgis.com/zh-cn/arcmap/latest/analyze/arcpy/what-is-arcpy-.htm

3.1地理分析功能编写——通视分析,缓冲区分析,高斯投影

通视分析

新建run.py文件,写入以下代码

#coding:utf-8
from flask import Flask, render_template, request, jsonify
import arcpy
from arcpy import env
# 自定义函数
from LonLat2XYpy2 import lonlat2xy
from Buffer_anatest import buffertest

/////////////////

# 鼠标点击地图,通视分析
@app.route('/oneLinetoLOS02')
def oneLinetoLOS02():
    print("LOSana start")
    # point_start = [93342.212,4308755.437]
    # point_end = [93325.940, 4308611.767]
    aa = request.args.get("a2", 0, type=float)
    # print('aa: %s '%aa)
    bb = request.args.get("b2", 0, type=float)
    cc = request.args.get("c2", 0, type=float)
    dd = request.args.get("d2", 0, type=float)
    pointstart = lonlat2xy(aa, bb)
    pointend = lonlat2xy(cc, dd)
    # print('point_start: %s ' % (pointstart,))
    # print('point_end: %s ' % (pointend,))
    line01 = [pointstart, pointend]
    line02 = [pointstart, pointend]
    feature_info = [line01, line02]
    # print('line01: %s '%line01)
    features = []
    for feature in feature_info:
        features.append(
            arcpy.Polyline(
                arcpy.Array([arcpy.Point(*coords) for coords in feature])))
    # Set workspace
    env.workspace = "F:/ARCGISDATA"
    # Execute Delete
    LOSline01 = "80LOSline01.shp"
    arcpy.Delete_management(LOSline01)
    LOSposition = "80LOSposition01.shp"
    arcpy.Delete_management(LOSposition)
    insurface01 = "alltif1.tif"
    inline01 = features
    outlos01 = "80LOSline01.shp"
    outobs01 = "80LOSposition01.shp"
    # print('in_line01: %s '%inline01)
    arcpy.LineOfSight_3d(insurface01, inline01, outlos01, outobs01)
    print("LOSana end")
    return jsonify(result=line02)

缓冲区分析

新建Buffer_anatest.py

# Import system modules
import arcpy
from arcpy import env

def buffertest(p1,p2):
    print("buffertest start")
    env.workspace = "F:/ARCGISDATA"
    line01 = [p1, p2]
    line02 = [p2, p1]
    feature_info = [line01, line02]
    # print('line01: %s ' % line01)
    features = []
    for feature in feature_info:
            features.append(
                arcpy.Polyline(
                    arcpy.Array([arcpy.Point(*coords) for coords in feature])))
    # Set local variables
    infeatures = features
    outfeatures = "bufferana2.shp"
    bufferDistance = 30
    # Execute Buffer
    arcpy.Buffer_analysis(infeatures, outfeatures, bufferDistance)
    print("buffertest end")
    return 1

高斯投影函数:(由于在arcmap处理地图时,使用的Xian_80投影坐标系,所以这里需要做经纬度坐标转投影坐标)

LatLon2XYpy2.py

# coding:utf-8
# 参考文献《基于高斯投影的经纬度距离参数修正方法》
import math

def lonlat2xy(longitude, latitude):
    # WGS-84
    # b = 6356752.3142
    # c = 6399593.6258
    # alpha = 1 / 298.257223563
    # e2 = 0.0066943799013
    # epep = 0.00673949674227

    # 80
    a = 6378140.0
    # b = 6356755.288157528
    e1 = 0.00669438499959 
    e2 = 0.00673950181947 
    # alpha = 1 / 298.2570
    B = (math.pi / 180.0) * latitude
    Lcm = 126.0 # Central_Meridian: 126.0
    tsin = math.sin(B)
    tcos = math.cos(B)
    t = math.tan(B)
    A = (math.pi / 180.0) * (longitude - Lcm) * tcos
    C = e2 * pow(tcos, 2)
    M = a * ((1 - e1 / 4.0 - pow(e1, 2) * 3.0 / 64.0 - pow(e1, 3) * 5.0 / 256.0) * B
        - (pow(e1, 2) * 3.0 / 8.0 + pow(e1, 2) * 3.0 / 32.0 + pow(e1, 3) * 45.0 / 1024.0) * math.sin(2.0 * B)
        + (pow(e1, 2) * 15.0 / 256.0 + pow(e1, 3) * 45.0 / 1024.0) * math.sin(4.0 * B)
        - pow(e1, 3) * 35.0 / 3072.0 * math.sin(6 * B))
    N = a / math.sqrt(1 - e1 * pow(tsin, 2))
    # zi dai pian yi - 15535.6 i dont know
    y = - 15535.6 + M + N * t * (0.5 * pow(A, 2) + (5.0 - pow(t, 2) + 9.0 * C + 4 * pow(C, 2)) * pow(A, 4) / 24.0
        + (61.0 - 58.0 * pow(t, 2) + pow(t, 4) + 270 * C -330 * pow(t, 2) * C) * pow(A, 6) / 720.0)
    x = 500000.0 + N * (A + (1.0 - pow(t, 2) + C) * pow(A, 3) / 6.0
        + (5.0 - 18.0 * pow(t, 2) + pow(t, 4) + 14.0 * C - 58.0 * C * pow(t, 2)) * pow(A, 5) / 120.0)
    # x0 = 4309134.741- x
    # y0 = 92760.635 - y
    # return x0, y0,x ,y
    return x, y

3.2 Flask服务主文件编写

在run.py中加入以下代码,下面展示完整run.py文件

#coding:utf-8
from flask import Flask, render_template, request, jsonify
import arcpy
from arcpy import env
# 自定义函数
from LonLat2XYpy2 import lonlat2xy
from Buffer_anatest import buffertest

app = Flask(__name__)

@app.route('/')
def hello():
    return '<h1>Hello World</h1>'

@app.route('/80LOSline01.txt')
def txt01():
    return render_template('80LOSline01.txt')

@app.route('/Buffer01.txt')
def txt02():
    return render_template('Buffer01.txt')

@app.route('/LOSlineremove.txt')
def txt03():
    return render_template('LOSlineremove.txt')

# 地图主界面
@app.route('/Intro_Cesium_python.html')
def los():
    return render_template('Cesium_python.html')

# 鼠标点击地图,通视分析
@app.route('/oneLinetoLOS02')
def oneLinetoLOS02():
    print("LOSana start")
    # point_start = [93342.212,4308755.437]
    # point_end = [93325.940, 4308611.767]
    aa = request.args.get("a2", 0, type=float)
    # print('aa: %s '%aa)
    bb = request.args.get("b2", 0, type=float)
    cc = request.args.get("c2", 0, type=float)
    dd = request.args.get("d2", 0, type=float)
    pointstart = lonlat2xy(aa, bb)
    pointend = lonlat2xy(cc, dd)
    # print('point_start: %s ' % (pointstart,))
    # print('point_end: %s ' % (pointend,))
    line01 = [pointstart, pointend]
    line02 = [pointstart, pointend]
    feature_info = [line01, line02]
    features = []
    for feature in feature_info:
        features.append(
            arcpy.Polyline(
                arcpy.Array([arcpy.Point(*coords) for coords in feature])))
    # Set workspace
    env.workspace = "F:/ARCGISDATA"
    # Execute Delete
    LOSline01 = "80LOSline01.shp"
    arcpy.Delete_management(LOSline01)
    LOSposition = "80LOSposition01.shp"
    arcpy.Delete_management(LOSposition)
    insurface01 = "alltif1.tif"
    inline01 = features
    outlos01 = "80LOSline01.shp"
    outobs01 = "80LOSposition01.shp"
    arcpy.LineOfSight_3d(insurface01, inline01, outlos01, outobs01)
    print("LOSana end")
    return jsonify(result=line02)

# 鼠标点击地图-缓冲区分析
@app.route('/Buffer01')
def Buffer01():
    print("Buffer01 start")
    aa = request.args.get("a3", 0, type=float)
    bb = request.args.get("b3", 0, type=float)
    cc = request.args.get("c3", 0, type=float)
    dd = request.args.get("d3", 0, type=float)
    pointstart = lonlat2xy(aa, bb)
    pointend = lonlat2xy(cc, dd)
    Bufline01 = "bufferana2.shp"
    arcpy.Delete_management(Bufline01)
    print("jieguo start")
    jieguo = buffertest(pointstart, pointend)
    print("jieguo end")
    return jsonify(result=jieguo)

if __name__ == '__main__':
    app.run(debug = True)

3.3 运行run.py

使用Pycharm打开F:\ARCGISDATA\文件夹

点击File —> Default Settings… —> 添加Flask模块,添加Python2.7路径

 右击run.py,点击Run‘run’,结果如下

 在浏览器输入http://127.0.0.1:5000/Intro_Cesium_python.html

即可访问网页

转载自:https://blog.csdn.net/qq_31121227/article/details/84583736

You may also like...