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