1, Realize ideas
- Use java to call cmd command to execute python script
- The python environment uses \ArcGIS\Pro\bin\Python\envs\arcgispro-py3 in the arcgis pro installation directory
- Before publishing the database table, you need to use the sde file to create the database (creating the sde file does not need to connect to arcgis)
- When publishing a table, first add the database table as a layer in the local empty project template, and then upload and publish the project
2, Java implementation
2.1 Call entry
/**
* 创建数据库连接文件,传入数据库连接相关对象
* 保存路径在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 Create an asynchronous task
It may take a long time to execute python. This is a general method that needs to monitor the script execution status
private int execSync(String fileName, String params[]) throws IOException {
log.info("同步读取python文件 init fileName={}", fileName);
Process process;
if (OS.startsWith("Windows")) {
// windows执行脚本需要使用 cmd.exe /c 才能正确执行脚本
process = new ProcessBuilder(params).
start();
} else {
// linux执行脚本一般是使用python3 + 文件所在路径
// process = new ProcessBuilder("python3", LINUX_PATH + fileName, params).start();
process = new ProcessBuilder(params).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;
}
}
3, python script
3.1 Create sde file
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 Release table
- The release table is an empty project locally as a template (D:\ITS\map\MyProject2), add a database table, and generate a draft file to publish;
- Empty project files can be obtained by using arcgis pro to create a project and delete all maps;
- Adding a table layer is to distinguish vector table and raster table according to the actual type of spatial table;
- Specifying the publishing directory is to ensure that the directory has been created in arcgis, and the publishing layer is overwritten and directly exposed.
# -*- 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, problems that may be encountered
If the version of arcgis pro is not compatible with the service version of arcgis, the python under pro will also report some strange errors. I use 3.0.1 pro and 10.6 arcgis; if you execute the python script package url problem, you can switch between https
and Try http, it is related to the version of arcgis;
when debugging, you can view the execution log in the manage of arcgis https://aaa.myarcgis.com:6443/arcgis/manager