1,实现思路

  1. 使用java调用cmd命令执行python脚本
  2. python环境使用arcgis pro安装目录下的 \ArcGIS\Pro\bin\Python\envs\arcgispro-py3
  3. 发布数据库表前需要先用创建数据库的sde文件(创建sde文件不需要连接arcgis)
  4. 发布表时,先在本地的空项目模板中添加数据库表作为图层,然后上传发布这个项目

2,java实现

2.1 调用入口

/**
     * 创建数据库连接文件,传入数据库连接相关对象
     * 保存路径在python中设置
     *
     * @param userDbInfo
     */
    public void createArcgisSde(ExternalDataBaseDO userDbInfo) {
        List<Object> params = new ArrayList<>();
        params.add("cmd.exe");
        params.add("/c");
        params.add("python");
        //创建sde的python脚本路径
        String pyScriptName = scriptLocation + File.separator + PY_CREATE_SDE;
        params.add(pyScriptName);
        params.add(userDbInfo.getHost());
        params.add(userDbInfo.getPort());
        params.add(userDbInfo.getDataBaseName());
        params.add(userDbInfo.getUserName());
        params.add(userDbInfo.getPassWord());
        params.add(userDbInfo.getId());
        String[] arr = params.toArray(new String[params.size()]);
        int i = execSync(pyScriptName, arr);
        if (i != 0) {
            log.error("调用" + scriptLocation + PY_CREATE_SDE + "python异常!");
        }
    }

     /**
     * 调用python发布sqlserver数据库要素类到arcgis
     * @param sde sde全路径  D:\\ITS\\itsPython\\192.168.10.153.sde
     * @param host arcgis的url https://aaa.myarcgis.com/arcgis
     * @param name arcgis用户名 aaa
     * @param password arcgis密码 xxxx
     * @param table 要发布的表名 SD
     * @return
     * @throws Exception
     */
    public String publishTableToArcgis(String sde, String host, String name, String password, String table) throws Exception {
        host = host.replace("https", "http");
        host = host.replace(":6443", "");
        sde = sde.replace("\\", "/");
        List<Object> params = new ArrayList<>();
        params.add("cmd.exe");
        params.add("/c");
        params.add("python");
        String pyScriptName = scriptLocation + File.separator + PY_PUBLISH_TABLE;
        params.add(pyScriptName);
        params.add(sde);
        params.add(host);
        params.add(name);
        params.add(password);
        params.add(table);
        String[] arr = params.toArray(new String[params.size()]);
        log.info("发布空间表参数:{}", Arrays.toString(arr));
        //publish_single_table("D:\\ITS\\itsPython\\192.168.10.153.sde", "https://lzw.gis107.com/arcgis", "lzwpro", "lzwpro123", "dbo.SD")
        int i = execSync(pyScriptName, arr);
        if (i == 0) {
            //拼接发布地址 https://lzw.gis107.com/server/rest/services/SD/MapServer
            return getPublishTableUrl(host, table);
        } else {
            log.error("发布表失败:{}", i);
            return "error";
        }
    }

2.2 创建一个异步任务

执行python时可能会执行很久,这是一个 通用的方法,需要监听脚本执行状态

private int execSync(String fileName, String params[]) throws IOException {
        log.info("同步读取python文件 init fileName={}", fileName);
        String logfile = "D:/ITS/pythonlog/" + generateFileName();
        File file = new File(logfile);
        ProcessBuilder pb = new ProcessBuilder(params);
        //重定向标准输出到文件,避免弹出黑窗口
        pb.redirectOutput(ProcessBuilder.Redirect.appendTo(file));
        pb.redirectError(ProcessBuilder.Redirect.appendTo(file));
        Process process = pb.start();
        taskPool.submit(() -> {
            log.info("读取python文件 开始 fileName={}", fileName);
            BufferedReader errorReader = null;
            // 脚本执行异常时的输出信息
            errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            List<String> errorString = read(fileName, errorReader);
            log.info("读取python文件 fileName={}  errorString={}", fileName, errorString);
        });

        taskPool.submit(() -> {
            // 脚本执行正常时的输出信息
            BufferedReader inputReader = null;
            inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            List<String> returnString = read(fileName, inputReader);
            log.info("读取python文件 fileName={}  returnString={}", fileName, returnString);
        });

        try {
            boolean res = process.waitFor(1L, TimeUnit.DAYS);
            if (res) {
                int i = process.exitValue();
                log.info("执行python文件 fileName={} == 结束 == {}", fileName, i);
                return i;
            }
            return 1;
        } catch (InterruptedException e) {
            log.error("同步读取python文件 fileName=" + fileName + " 等待结果返回异常", e);
            return 1;
        }
    }
    
    public static String generateFileName() {
        LocalDate currentDate = LocalDate.now();
        LocalDate mondayDate = currentDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String fileName = "python-" + mondayDate.format(formatter) + ".log";
        return fileName;
    }

3,python脚本

3.1 创建sde文件

import arcpy, os

# 从用户输入获取连接参数

def create_sde(ip, port, dbname, username, password, dbId):
    # 创建临时 SDE 连接文件
    temp_sde = os.path.join(arcpy.env.scratchFolder, "temp.sde")
    arcpy.Delete_management(temp_sde)
    arcpy.CreateDatabaseConnection_management(os.path.dirname(temp_sde),
                                               os.path.basename(temp_sde),
                                               "SQL_SERVER", ip+","+port, "DATABASE_AUTH",
                                               username, password, "SAVE_USERNAME",
                                               dbname)
    #arcpy.CreateDatabaseConnection_management("C:/temp", "myDatabase.sde", "SQL_SERVER", "myServer", "DATABASE_AUTH", "myUser", "myPassword", "#", "myDatabase", "#", "#", "#", "#", "#")
    # 复制并重命名临时 SDE 连接文件到指定位置D:\ITS\map\sde
    output_sde = "D:\\ITS\\map\\sde\\" + dbId + ".sde"
    if os.path.isfile(output_sde):
        os.remove(output_sde)
    arcpy.Copy_management(temp_sde, output_sde)

    # 删除临时 SDE 连接文件
    arcpy.Delete_management(temp_sde)

if __name__ == '__main__':
    a = []
    for i in range(1, len(sys.argv)):
        print("arg:" + sys.argv[i])
        a.append(sys.argv[i])

    create_sde(a[0], a[1], a[2], a[3], a[4], a[5])
    #create_sde("192.168.10.153", "1433", "测试中文数据库", "sa", "admin888888", "测试sde文件名")

3.2 发布表

  • 发布表是在本地一个空的项目作为模板(D:\ITS\map\MyProject2),添加数据库表,生成草稿文件来发布;
  • 空项目文件可以使用arcgis pro创建项目删除所有地图得到;
  • 添加表图层是要根据空间表实际类型来,区分矢量表和栅格表;
  • 指定发布目录是要确保目录在arcgis已创建,发布图层这里设置的覆盖且直接公开.
# -*- coding: UTF-8 -*-
import arcpy
import sys
import os
import calendar
import time
import shutil

# 将标准输出和标准错误输出都重定向到同一个文件中
log_file = open("D:/ITS/pythonlog.txt", "w")
sys.stdout = log_file
sys.stderr = log_file

# 覆盖
arcpy.env.overwriteOutput = True

projectDir = r"D:\ITS\map"
templateProject = "MyProject2"
# 创建临时目录
targetProject = "project_" + str(calendar.timegm(time.gmtime()))
targetProjectPath = os.path.join(projectDir, targetProject)
aprxName = "MyProject2.aprx"


def publish_single_table(sde, host, name, password, table):
    arcpy.env.workspace = sde
    # 数据库表追加feature前缀,用来区分要素类型,后期单表会发布成map服务
    service_name = "feature_" + table
    shutil.copytree(os.path.join(projectDir, templateProject), targetProjectPath)
    mpmath = os.path.join(targetProjectPath, aprxName)
    aprx = arcpy.mp.ArcGISProject(mpmath)  # aprx存储路径
    aprx_map = aprx.listMaps("*")[0]  # 要将数据添加到aprx中的哪个地图下
    # 先从矢量表找目标表,找不到就去栅格表找
    sde_list = arcpy.ListFeatureClasses()
    print("遍历矢量图层!")
    for item in sde_list:
        # 不确定sde文件中每张表的格式,有可能是 库名.组织.表名,也可能是 组织.表名,只匹配最后表名
        if item.split(".")[-1] == table:
            print("添加图层:" + sde + "/" + item)
            aprx_map.addDataFromPath(sde + "/" + item)
            break
    else:
        print('矢量图层没找到 ' + table)
        # 如果未找到目标则遍历栅格图层
        sde_list=arcpy.ListRasters()
        print("遍历栅格图层!")
        for item in sde_list:
            if item.split(".")[-1] == table:
                aprx_map.addDataFromPath(sde + "/" + item)
                break
        else:
            print('栅格图层未找到 ' + table)
            raise Exception(table + "  table is not find")
    # lyrx = arcpy.mp.LayerFile("D:\\ITS\\map\\style\\县界.lyrx")
    # listLayers = aprxMap.listLayers()
    # for layer in listLayers:
    #     arcpy.ApplySymbologyFromLayer_management(layer, "D:\\ITS\\map\\style\\县界.lyrx")
    aprx.save()

    # Sign in to portal
    a = arcpy.SignInToPortal(host, name, password)

    # Set output file names
    out_dir = os.path.join(targetProjectPath, "out")
    os.makedirs(out_dir)
    sd_draft_filename = service_name + ".sddraft"
    sd_draft_output_filename = os.path.join(out_dir, sd_draft_filename)
    sd_filename = service_name + ".sd"
    sd_output_filename = os.path.join(out_dir, sd_filename)

    # Reference map to publish
    # aprx = arcpy.mp.ArcGISProject("D:\\ITS\\map\\MyProjectMyProject.aprx")
    m = aprx.listMaps()[0]

    # Create FeatureSharingDraft and set metadata, portal folder, and export data properties
    server_type = "HOSTING_SERVER"
    sd_draft = m.getWebLayerSharingDraft(server_type, "FEATURE", service_name)
    hosts = host.split("/")
    b = hosts[len(hosts) - 1]
    print(b)
    sd_draft.federatedServerUrl = host.replace(b, "server")
    sd_draft.credits = "These are credits"
    sd_draft.description = "This is description"
    sd_draft.summary = "This is summary"
    sd_draft.tags = "tag1, tag2"
    sd_draft.useLimitations = "These are use limitations"
    sd_draft.serverFolder = "gptest"
    sd_draft.allowExporting = True
    sd_draft.overwriteExistingService = True

    # Create Service Definition Draft file
    sd_draft.exportToSDDraft(sd_draft_output_filename)

    # Stage Service
    print("Start Staging")
    arcpy.StageService_server(sd_draft_output_filename, sd_output_filename)

    # Share to portal
    print("Start Uploading")
    arcpy.UploadServiceDefinition_server(sd_output_filename, sd_draft.federatedServerUrl, service_name, None, None, "gptest",None, True, None, True, None, None)

    print("Finish Publishing")
    # https://lzw.gis107.com/server/rest/services/ROAD/MapServer


if __name__ == '__main__':
    a = []
    for i in range(1, len(sys.argv)):
        print("arg:" + sys.argv[i])
        a.append(sys.argv[i])
    #执行一些操作并输出信息或错误信息
    publish_single_table(a[0], a[1], a[2], a[3], a[4])
    #publish_single_table("D:\\ITS\\map\\sde\\192.168.10.153.sde", "http://lzw.server.com/arcgis", "lzwpro", "lzwpro123", "STBHHX")

    #shutil.rmtree(targetProjectPath)

4,可能会遇到的问题

如果arcgis pro的版本与arcgis的服务版本不兼容,pro下的python也会报一些奇怪的错,我使用的3.0.1的pro和10.6的arcgis;

如果执行python脚本包url问题,可以切换https与http试下,与arcgis的版本有关;

调试时在arcgis的manage可以查看执行日志 https://aaa.myarcgis.com:6443/arcgis/manager

gis面试题java gis java_gis面试题java