package org.sxkj.odm.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONObject;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.io.IOUtils;
|
import org.apache.logging.log4j.util.Strings;
|
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.Nullable;
|
import org.locationtech.jts.geom.Coordinate;
|
import org.locationtech.jts.geom.Point;
|
import org.springblade.core.log.exception.ServiceException;
|
import org.springblade.core.oss.model.BladeFile;
|
import org.springblade.core.redis.cache.BladeRedis;
|
import org.springblade.core.secure.utils.AuthUtil;
|
import org.springblade.core.tool.api.R;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.core.io.FileSystemResource;
|
import org.springframework.mock.web.MockMultipartFile;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.StringUtils;
|
import org.springframework.web.multipart.MultipartFile;
|
import org.sxkj.common.constant.CacheConstant;
|
import org.sxkj.common.constant.CommonConstant;
|
import org.sxkj.common.enums.AttachResultTypeEnum;
|
import org.sxkj.common.enums.WaylineTypeEnum;
|
import org.sxkj.odm.enums.OdmTypeEnum;
|
import org.sxkj.odm.enums.WebOdmProjectIdEnum;
|
import org.sxkj.common.func.Streams;
|
import org.sxkj.common.model.ResponseResult;
|
import org.sxkj.common.model.TimeRange;
|
import org.sxkj.common.redis.RedisOpsUtils;
|
import org.sxkj.common.utils.*;
|
import org.sxkj.odm.build.DevicePermissionBuilder;
|
import org.sxkj.odm.config.MinioDataConstant;
|
import org.sxkj.odm.config.OdmProperties;
|
import org.sxkj.odm.config.PyToolsServiceProperties;
|
import org.sxkj.odm.constant.OdmConstant;
|
import org.sxkj.odm.constant.RedisKeyConstant;
|
import org.sxkj.odm.constant.TifConstant;
|
import org.sxkj.odm.entity.*;
|
import org.sxkj.odm.enums.WebOdmStatusEnum;
|
import org.sxkj.odm.mapper.OdmTaskInfoMapper;
|
import org.sxkj.odm.service.*;
|
import org.sxkj.odm.utils.GeoJsonToWktConverterUtils;
|
import org.sxkj.odm.utils.ParseTifUtils;
|
import org.sxkj.odm.utils.TreeUtils;
|
import org.sxkj.odm.utils.VideoConverterUtils;
|
import org.sxkj.odm.vo.*;
|
import org.sxkj.resource.vo.AttachTypeStatisticsVO;
|
import org.sxkj.tools.feign.IObjConvertClient;
|
import org.sxkj.tools.feign.ITifConvertClient;
|
import org.sxkj.resource.entity.Attach;
|
import org.sxkj.resource.feign.IAttachClient;
|
import org.sxkj.system.cache.SysCache;
|
import org.sxkj.system.feign.ISysClient;
|
import org.sxkj.system.vo.TreeVo;
|
import org.sxkj.tools.model.TifConvertResult;
|
|
import javax.net.ssl.HttpsURLConnection;
|
import java.io.*;
|
import java.net.URL;
|
import java.nio.file.Files;
|
import java.nio.file.Path;
|
import java.nio.file.Paths;
|
import java.text.MessageFormat;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.*;
|
import java.util.concurrent.Future;
|
import java.util.concurrent.TimeUnit;
|
import java.util.stream.Collectors;
|
|
import static org.sxkj.odm.utils.FileUtils.deletedFile;
|
|
/**
|
* odm 任务信息服务实现层
|
*
|
* @author zhongrj
|
* @date 2024-09-11
|
*/
|
@Slf4j
|
@Service
|
public class OdmTaskInfoServiceImpl extends ServiceImpl<OdmTaskInfoMapper, OdmTaskInfo> implements IOdmTaskInfoService {
|
|
@Autowired
|
private WebOdmSendRequest sendRequest;
|
|
@Autowired
|
private PyTifSendRequest pyTifSendRequest;
|
|
@Autowired
|
private OdmTaskThreadService odmTaskThreadService;
|
|
@Autowired
|
private MinioDataConstant minioDataConstant;
|
|
@Autowired
|
private IAttachClient attachClient;
|
|
@Autowired
|
private AsyncService asyncService;
|
|
@Autowired
|
private ISysClient sysClient;
|
|
@Autowired
|
private IObjConvertClient objConvertClient;
|
|
@Autowired
|
private ITifConvertClient tifConvertClient;
|
|
@Value("${odm.down.temp.basePath}")
|
private String odmDownTempBasePath;
|
|
@Value("${odm.down.temp.preFix}")
|
private String odmDownPreFixPath;
|
|
@Value("${odm.down.temp.zipBasePath}")
|
private String odmDownTempZipBasePath;
|
|
@Autowired
|
private BladeRedis bladeRedis;
|
|
@Autowired
|
private OdmProperties odmProperties;
|
|
@Autowired
|
private PyToolsServiceProperties pyToolsServiceProperties;
|
|
@Autowired
|
private DevicePermissionBuilder permissionBuilder;
|
|
|
/**
|
* 自定义分页列表查询
|
*
|
* @param page
|
* @param odmTaskInfo
|
* @return
|
*/
|
@Override
|
public IPage<OdmTaskInfoVO> selectOdmTaskInfoPage(IPage<OdmTaskInfoVO> page, OdmTaskInfoVO odmTaskInfo) {
|
if (!AuthUtil.isAdministrator() && !Strings.isBlank(AuthUtil.getDeptId())) {
|
// odmTaskInfo.setDeptId(Long.parseLong(AuthUtil.getDeptId()));
|
List<Long> deptIdList = SysCache.getDeptChildIds(Long.valueOf(AuthUtil.getDeptId()));
|
odmTaskInfo.setDeptIdList(deptIdList);
|
odmTaskInfo.setPermissionCondition(permissionBuilder.buildDataPermissionCondition(AuthUtil.getUserId(), "wj"));
|
}
|
// 查询并返回
|
return page.setRecords(baseMapper.selectOdmTaskInfoPage(page, odmTaskInfo));
|
}
|
|
/**
|
* 创建 odm 任务--定时创建
|
*
|
* @return
|
*/
|
@Override
|
public Boolean createWebOdmTask() {
|
boolean flag = false;
|
int num = 0;
|
// 先查询在执行的任务数,超过1个就先不创建了
|
Integer processTaskNum = baseMapper.getProcessTaskNum();
|
if (processTaskNum >= OdmConstant.MAX_PROCESS_TASK_NUM) {
|
log.info("在运行的任务超过{}个,延时创建任务",OdmConstant.MAX_PROCESS_TASK_NUM);
|
return false;
|
}
|
// 查询所有未在 webodm 创建任务的任务信息
|
List<OdmTaskInfoVO> list = baseMapper.getNotByWebOdmCreateTaskList();
|
if (list.size() == 0) {
|
return false;
|
}
|
// 遍历
|
for (OdmTaskInfoVO odmTaskInfo : list) {
|
flag = createWebOdmTask(odmTaskInfo.getWaylineJobId(), odmTaskInfo.getGsd());
|
if (flag) {
|
num++;
|
}
|
}
|
if (num == list.size()) {
|
return true;
|
}
|
return false;
|
}
|
|
/**
|
* odm 任务创建
|
*
|
* @param jobId 航线任务id
|
* @param ortRes 正射影像的分辨率,单位为 cm/pixel
|
* @return
|
*/
|
@Override
|
public Boolean createWebOdmTask(String jobId, Double ortRes) {
|
// 查询当前航线任务的类型(过滤掉不参与图片合并的类型),当前只要 2:航测 4:正射举证 5:倾斜摄影 6:视频切斜摄影
|
Integer waylineType = baseMapper.getWaylineType(jobId, OdmConstant.ODM_TASK_OPTION_TYPE);
|
// 如果不存在
|
if (null == waylineType) {
|
return false;
|
}
|
// 查询航线任务对应的图片url
|
List<String> picList = baseMapper.getWaylinePicUrl(jobId, waylineType);
|
Integer projectId = WebOdmProjectIdEnum.ORT.getProjectId();
|
// 创建odm 任务,实景三维和白膜三维共用一个工程id:4
|
List<Integer> tiltTaskTypes = OdmConstant.TILT_TASK_TYPES;
|
List<Integer> altTaskTypes = OdmConstant.ALB_TASK_TYPES;
|
if (tiltTaskTypes.contains(waylineType) || altTaskTypes.contains(waylineType)) {
|
projectId = WebOdmProjectIdEnum.TD.getProjectId();
|
}
|
Boolean x = createOdmTask(jobId, ortRes, waylineType, picList, projectId);
|
if (x != null) return x;
|
// 完成任务创建
|
return false;
|
}
|
|
/**
|
* 创建 odm 任务
|
*
|
* @param jobId 航线任务id
|
* @param ortRes 正射影像的分辨率,单位为 cm/pixel
|
* @param waylineType 航线任务类型
|
* @param picList 图片地址集合
|
* @return
|
*/
|
@Nullable
|
private Boolean createOdmTask(String jobId, Double ortRes, Integer waylineType, List<String> picList, Integer projectId) {
|
log.info("当前航线任务:{},对应的图片数量:{}", jobId, picList.size());
|
if (picList.size() <= OdmConstant.MIN_PIC_NUM) {
|
log.error("当前航线任务:{},对应的图片数量:{},少于{}张,无法创建任务", jobId, picList.size(),OdmConstant.MIN_PIC_NUM);
|
throw new ServiceException("当前航线任务:" + jobId + ",对应的图片数量:" + picList.size() + ",少于" + OdmConstant.MIN_PIC_NUM + "张,无法创建任务");
|
}
|
// 设置 odm 任务创建参数
|
Map<String, Object> map = setCreteOdmTaskParam(waylineType, ortRes);
|
// url 格式化
|
String createTaskUrl = MessageFormat.format(OdmConstant.CREATE_TASK_API, projectId);
|
// 校验该次任务已经创建
|
if (isCreateWebOdmTask(jobId, waylineType)) {
|
log.error("webodm任务已经创建过了!jobId:{}", jobId);
|
return false;
|
}
|
// 创建任务
|
JSONObject jsonObject = sendRequest.sendPostJsonByMap(createTaskUrl, true, map);
|
// 保存关联信息到表
|
if (null == jsonObject) {
|
log.error("创建任务失败!jobId:{}", jobId);
|
return false;
|
}
|
// 上传对应的图片到 odm
|
// 设置多线程进行图片上传,返回上传成功的文件数量
|
int count = uploadWaylinePic(picList, jsonObject.getString("id"), projectId);
|
log.info("当前航线任务:{},已成功上传odm图片数量:{}", jobId, count);
|
if (count < OdmConstant.MIN_PIC_NUM) {
|
log.error("已上传odm图片数量为:{},不符合要求,至少需要上传{}张图片!", count,OdmConstant.MIN_PIC_NUM);
|
return false;
|
}
|
// 图片上传完成提交任务
|
String taskId = jsonObject.getString("id");
|
// url 格式化,odm 任务id 从创建任务返回的id 取
|
String commitTaskUrl = MessageFormat.format(OdmConstant.COMMIT_TASK_API, projectId, taskId);
|
jsonObject.put("images_count", count);
|
jsonObject.put("partial", false);
|
JSONObject commitTaskJson = sendRequest.sendPostJsonByJson(commitTaskUrl, true, jsonObject.toString());
|
if (null != commitTaskJson) {
|
// 获取odm 任务信息
|
OdmTaskInfo odmTaskInfo = getOdmTaskInfo(jobId);
|
// 更新 odm task 任务信息
|
boolean update = updateOdmTaskInfo(count, commitTaskJson, odmTaskInfo);
|
return update && true;
|
}
|
return null;
|
}
|
|
|
/**
|
* 定时补漏odm任务的的生成(由其他原因到时没有创建到任务的)
|
*
|
* @return
|
*/
|
@Override
|
public Boolean leakRepairOdmTask() {
|
int num = 0;
|
// 先查询在执行的任务数,超过1个就先不创建了
|
Integer processTaskNum = baseMapper.getProcessTaskNum();
|
if (processTaskNum >= OdmConstant.MAX_PROCESS_TASK_NUM) {
|
log.info("在运行的任务超过{}个,延时创建任务",OdmConstant.MAX_PROCESS_TASK_NUM);
|
return false;
|
}
|
// 查询没有生成的odm任务航线任务,不包含已经删除的航线任务
|
List<String> jobIds = baseMapper.getNoCreateTaskJob(OdmConstant.ODM_TASK_OPTION_TYPE);
|
if (jobIds.size() == 0) {
|
return false;
|
}
|
// 遍历生成任务
|
for (String jobId : jobIds) {
|
boolean flag = saveOdmTaskInfo(jobId);
|
if (flag) {
|
num++;
|
}
|
}
|
return num == jobIds.size();
|
}
|
|
/**
|
* 保存 odm 任务信息
|
*
|
* @param jobId
|
* @return
|
*/
|
@Override
|
public Boolean saveOdmTaskInfo(String jobId) {
|
// 查询当前航线任务的类型(过滤掉不参与图片合并的类型),当前只要 2:航测(正射举证) 4:图斑正射举证 5:三维倾斜摄影 6:视频三维白膜
|
Integer waylineType = baseMapper.getWaylineType(jobId, OdmConstant.ODM_TASK_OPTION_TYPE);
|
// 如果不存在
|
if (null == waylineType) {
|
return false;
|
}
|
// 视频白膜
|
if (waylineType == WaylineTypeEnum.ALB_COL.getType().intValue()) {
|
// 判断是否有再处理视频的任务,如果有则不处理,没有才处理,防止cpu,内存使用过多
|
String value = bladeRedis.get(RedisKeyConstant.ODM_VIDEO_TO_PIC_KEY);
|
if (!Strings.isBlank(value)) {
|
log.info("已存在处理的视频,待处理完成后执行,退出当前执行");
|
return false;
|
}
|
}
|
// 判断是否创建,没有则直接创建 odm 任务
|
if (isCreateTask(jobId)) {
|
return false;
|
}
|
// 查询航线任务对应的图片url
|
List<String> picList = baseMapper.getWaylinePicUrl(jobId, waylineType);
|
log.info("当前航线任务:{},对应的图片数量:{}", jobId, picList.size());
|
if (waylineType != WaylineTypeEnum.ALB_COL.getType().intValue() && picList.size() <= OdmConstant.MIN_PIC_NUM) {
|
log.error("当前航线任务:{},对应的图片数量:{},少于{}张,无法创建任务", jobId, picList.size(),OdmConstant.MIN_PIC_NUM);
|
return false;
|
}
|
// 创建
|
OdmTaskInfo odmTaskInfo = saveOdmTaskInfo(jobId, null, waylineType);
|
if (null != odmTaskInfo) {
|
return true;
|
}
|
return false;
|
}
|
|
/**
|
* 校验任务是否已经创建
|
*
|
* @param jobId
|
* @return
|
*/
|
private boolean isCreateTask(String jobId) {
|
// 根据jobId 查询任务信息
|
OdmTaskInfo odmTaskInfo = getOdmTaskInfo(jobId);
|
if (null == odmTaskInfo) {
|
return false;
|
}
|
return true;
|
}
|
|
/**
|
* 校验webodm任务是否已经创建
|
*
|
* @param jobId
|
* @return
|
*/
|
private boolean isCreateWebOdmTask(String jobId, Integer waylineType) {
|
// 根据jobId 查询任务信息
|
OdmTaskInfo odmTaskInfo = getOdmTaskInfo(jobId);
|
if (null == odmTaskInfo) {
|
// 没有就先创建
|
saveOdmTaskInfo(jobId, null, waylineType);
|
return false;
|
}
|
// 如果 taskId 已存在,则无需再创建webodm任务
|
if (Strings.isBlank(odmTaskInfo.getTaskId())) {
|
return false;
|
}
|
return true;
|
}
|
|
/**
|
* 获取odm 任务信息
|
*
|
* @param jobId
|
* @return
|
*/
|
private OdmTaskInfo getOdmTaskInfo(String jobId) {
|
// 根据jobId 查询任务信息
|
QueryWrapper<OdmTaskInfo> wrapper = new QueryWrapper<>();
|
wrapper.eq("wayline_job_id", jobId);
|
// 返回
|
return baseMapper.selectOne(wrapper);
|
}
|
|
/**
|
* 保存odm 任务信息
|
*
|
* @param jobId
|
* @param taskId
|
* @param waylineType 航线类型
|
* @return
|
*/
|
public OdmTaskInfo saveOdmTaskInfo(String jobId, String taskId, Integer waylineType) {
|
// 如果是视频的,需要单独处理
|
if (waylineType == WaylineTypeEnum.ALB_COL.getType().intValue()) {
|
videoToOdm(jobId);
|
} else {
|
OdmTaskInfo odmTaskInfo = new OdmTaskInfo();
|
// 根据jobId 查询对应的设备编号
|
String dockSn = baseMapper.getDeviceSnByJobId(jobId);
|
odmTaskInfo.setDeviceSn(dockSn);
|
OdmTaskInfo jobBaseInfo = baseMapper.selectJobBaseInfo(jobId);
|
odmTaskInfo.setAreaCode(jobBaseInfo.getAreaCode());
|
odmTaskInfo.setCreateUser(jobBaseInfo.getCreateUser());
|
odmTaskInfo.setUpdateUser(jobBaseInfo.getUpdateUser());
|
odmTaskInfo.setCreateDept(jobBaseInfo.getCreateDept());
|
odmTaskInfo.setCreateTime(new Date());
|
odmTaskInfo.setUpdateTime(new Date());
|
odmTaskInfo.setProjectId(WebOdmProjectIdEnum.ORT.getProjectId());
|
odmTaskInfo.setTaskId(taskId);
|
odmTaskInfo.setWaylineJobId(jobId);
|
if (null != waylineType && OdmConstant.TILT_TASK_TYPES.contains(waylineType)) {
|
// 设置为倾斜摄影
|
odmTaskInfo.setType(OdmTypeEnum.TILT.getType());
|
odmTaskInfo.setProjectId(WebOdmProjectIdEnum.TD.getProjectId());
|
}
|
boolean save = this.save(odmTaskInfo);
|
if (save) {
|
return odmTaskInfo;
|
}
|
}
|
return null;
|
}
|
|
/**
|
* 更新 odm 任务信息
|
*
|
* @param count
|
* @param jsonObject
|
* @return
|
*/
|
private boolean updateOdmTaskInfo(int count, JSONObject jsonObject, OdmTaskInfo odmTaskInfo) {
|
odmTaskInfo.setTaskId(jsonObject.getString("id"));
|
// 更新任务信息
|
odmTaskInfo.setImageCount(count);
|
odmTaskInfo.setUploadProgress(jsonObject.getDouble("upload_progress"));
|
odmTaskInfo.setResizeProgress(jsonObject.getDouble("resize_progress"));
|
odmTaskInfo.setRunningProgress(jsonObject.getDouble("running_progress"));
|
odmTaskInfo.setProcessingTime(jsonObject.getLong("processing_time"));
|
odmTaskInfo.setStatus(jsonObject.getInteger("status"));
|
// 更新并返回
|
return updateById(odmTaskInfo);
|
}
|
|
/**
|
* 设置多线程进行图片上传到 odm 对应的任务
|
*
|
* @param picList 图片url集合
|
* @param taskId odm 任务id
|
* @param projectId odm 项目id
|
* @return
|
*/
|
private int uploadWaylinePic(List<String> picList, String taskId, Integer projectId) {
|
int count = 0;
|
// 创建Future列表来保存每个任务的结果
|
List<Future<Boolean>> futures = new ArrayList<>();
|
for (String url : picList) {
|
// 提交任务
|
futures.add(odmTaskThreadService.submit(() -> {
|
// 这里执行你的异步任务
|
return sendUploadWaylinePic(url, taskId, projectId);
|
}));
|
}
|
// 收集并打印结果
|
for (Future<Boolean> future : futures) {
|
try {
|
Boolean result = future.get();
|
if (result) {
|
count++;
|
}
|
} catch (Exception e) {
|
log.error("异常信息-----》", e);
|
}
|
}
|
return count;
|
}
|
|
/**
|
* 发送图片上传请求
|
*
|
* @param url
|
* @param taskId
|
* @return
|
*/
|
private Boolean sendUploadWaylinePic(String url, String taskId, Integer projectId) {
|
// 获取图片进行上传到 odm,通过读取本地地址的方式获取图片流
|
url = minioDataConstant.getServerBasePath() + minioDataConstant.getBucket() + url;
|
File file = new File(url);
|
// 检查文件是否存在
|
if (!file.exists()) {
|
log.error("文件 {} 不存在!", url);
|
return false;
|
}
|
FileSystemResource fileResource = new FileSystemResource(file);
|
// url 格式化,odm project_id 固定为3,odm 任务id 从创建任务返回的id 取
|
String commitTaskUrl = MessageFormat.format(OdmConstant.UPLOAD_PIC_BY_TASK_API, projectId, taskId);
|
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
|
multiValueMap.add("file", fileResource);
|
// 发送上传请求
|
JSONObject jsonObject = sendRequest.sendPostFormData(commitTaskUrl, true, multiValueMap);
|
if (null == jsonObject) {
|
return false;
|
}
|
Boolean success = jsonObject.getBoolean("success");
|
if (success) {
|
return true;
|
}
|
return false;
|
}
|
|
/**
|
* 设置拼图任务参数和选项数据
|
*
|
* @param waylineType 航线任务类型
|
* @param ortRes 正射影像的分辨率,单位为 cm/pixel(默认是5)
|
* @return
|
*/
|
@NotNull
|
private Map<String, Object> setCreteOdmTaskParam(Integer waylineType, Double ortRes) {
|
// 设置任务创建参数
|
Map<String, Object> map = new HashMap<>(5);
|
map.put("auto_processing_node", true);
|
map.put("partial", true);
|
map.put("processing_node", OdmConstant.PROCESS_NODE_NUM);
|
// 图片压缩分辨率(-1 时代表不修改图片尺寸)
|
if (waylineType == WaylineTypeEnum.TILT_WAY.getType().intValue() || waylineType == WaylineTypeEnum.GEO_WAY.getType().intValue()){
|
// 斜面和几何做图片压缩调整,其他情况暂不做压缩
|
map.put("resize_to", OdmConstant.RESIZE_VALUE);
|
} else {
|
map.put("resize_to", OdmConstant.NO_RESIZE_VALUE);
|
}
|
// 获取选项参数信息
|
List<Map<String, Object>> options = OdmConstant.ODM_TASK_OPTION.get(waylineType);
|
if (null != ortRes) {
|
if (ortRes < 2) {
|
ortRes = 2.0;
|
}
|
Double finalOrtRes = ortRes;
|
Map<String, Object> ortResMap = new HashMap<>() {{
|
put("name", "orthophoto-resolution");
|
put("value", finalOrtRes);
|
}};
|
options.add(ortResMap);
|
}
|
map.put("options", options);
|
// 返回
|
return map;
|
}
|
|
/**
|
* 获取正射影像信息
|
*
|
* @param jobId 航线任务id
|
* @return
|
*/
|
@Override
|
public String getOrthoimageUrl(String jobId) {
|
// 根据jobId 查询导出对应的正射影像信息
|
OdmTaskInfo odmTaskInfo = getOdmTaskInfo(jobId);
|
if (null == odmTaskInfo) {
|
return null;
|
}
|
// 格式化url
|
String url = MessageFormat.format(OdmConstant.ORTHOPHOTO_EXPORT_API, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// 配置请求参数
|
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
|
multiValueMap.add("format", "gtiff");
|
multiValueMap.add("epsg", "4326");
|
//调用接口获取正射影像信息
|
JSONObject jsonObject = sendRequest.sendPostFormData(url, true, multiValueMap);
|
// 返回
|
if (null == jsonObject) {
|
return null;
|
}
|
// 拼接 url
|
String tmpUrl = odmProperties.getHttpsPreFixUrl() + OdmConstant.GET_PHONE_URL;
|
// 先适配 uuid
|
String downloadUrl = MessageFormat.format(tmpUrl, jsonObject.getString("celery_task_id"));
|
// 拼接
|
downloadUrl = downloadUrl + "?filename=" + jsonObject.getString("filename");
|
// 返回
|
return downloadUrl;
|
}
|
|
/**
|
* 定时更新odm 任务状态
|
*
|
* @return
|
*/
|
@Override
|
public Integer updateOdmTaskInfo() {
|
int count = 0;
|
// 查询 running_progress 小于 1 的任务,isUpdate 为 0 的
|
List<OdmTaskInfoVO> odmTaskInfoList = baseMapper.selectOdmTaskListLess1();
|
// 判断是否有
|
if (odmTaskInfoList.size() == 0) {
|
return count;
|
}
|
// 创建Future列表来保存每个任务的结果
|
List<Future<Boolean>> futures = new ArrayList<>();
|
for (OdmTaskInfoVO odmTaskInfo : odmTaskInfoList) {
|
// 提交任务
|
futures.add(odmTaskThreadService.submit(() -> {
|
// 这里执行你的异步任务,更新任务状态
|
return sendGetTaskInfoReqestAndUpdate(odmTaskInfo);
|
}));
|
}
|
// 收集并打印结果
|
for (Future<Boolean> future : futures) {
|
try {
|
Boolean result = future.get();
|
if (result) {
|
count++;
|
}
|
} catch (Exception e) {
|
log.error("异常信息-----》", e);
|
}
|
}
|
return count;
|
}
|
|
/**
|
* 更新任务状态
|
*
|
* @param odmTaskInfo
|
* @return
|
*/
|
private Boolean sendGetTaskInfoReqestAndUpdate(OdmTaskInfoVO odmTaskInfo) {
|
log.info("开始更新任务状态,任务id:{}", odmTaskInfo.getTaskId());
|
boolean flag = false;
|
// 格式化url
|
String url = MessageFormat.format(OdmConstant.GET_TASK_INFO_API, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
//调用接口从 webodm 获取任务信息
|
JSONObject jsonObject = sendRequest.sendGetRequest(url, true);
|
if (null == jsonObject) {
|
return flag;
|
}
|
// 获取 状态值:running_progress等并更新
|
odmTaskInfo.setUploadProgress(jsonObject.getDouble("upload_progress"));
|
odmTaskInfo.setResizeProgress(jsonObject.getDouble("resize_progress"));
|
odmTaskInfo.setRunningProgress(jsonObject.getDouble("running_progress"));
|
odmTaskInfo.setProcessingTime(jsonObject.getLong("processing_time"));
|
// 更新状态
|
Integer status = jsonObject.getInteger("status");
|
odmTaskInfo.setStatus(status);
|
// 设置更新同步状态
|
if (status == WebOdmStatusEnum.FAIL.getStatus().intValue() || status == WebOdmStatusEnum.COMPLETE.getStatus().intValue()) {
|
// 设置为已更新完成
|
odmTaskInfo.setIsUpdate(1);
|
}
|
if (status == WebOdmStatusEnum.FAIL.getStatus().intValue()) {
|
// 设置错误消息
|
odmTaskInfo.setLastError(jsonObject.getString("last_error"));
|
}
|
// 设置路径
|
if (status == WebOdmStatusEnum.COMPLETE.getStatus().intValue()) {
|
log.info("odm 拼图任务已完成,任务id:{},航线任务ID:{}", odmTaskInfo.getTaskId(), odmTaskInfo.getWaylineJobId());
|
// 获取转换 api url
|
// 设置地表模型tif 路径
|
String demTifPath = MessageFormat.format(OdmConstant.DEM_TIF_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// 获取运行环境信息
|
String activeProfile = SpringContextUtil.getActiveProfile();
|
// 设置地形文件(处理耗时较长)时配置 redis,防止重复调用
|
String value = bladeRedis.get(RedisKeyConstant.ODM_DEM_KEY);
|
if (!Strings.isBlank(value)) {
|
return false;
|
}
|
// 设置缓存
|
bladeRedis.setEx(RedisKeyConstant.ODM_DEM_KEY, "1", RedisKeyConstant.ODM_DEM_TIME);
|
// 设置 dem 地表模型tif 路径
|
odmTaskInfo.setDemTifPath(demTifPath);
|
// 设置 正射范围4点空间面数据
|
String geomStr = getGeom(odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId(), activeProfile);
|
if (!Strings.isBlank(geomStr)) {
|
odmTaskInfo.setGeom(geomStr);
|
}
|
// 格式化正射影像路径
|
String orthoimagePath = MessageFormat.format(OdmConstant.ORTHO_IMAGE_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// 格式化正射影像服务api
|
String orthoimageApi = MessageFormat.format(OdmConstant.ORTHO_IMAGE_API, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId()) + OdmConstant.ORTHO_IMAGE_API_SUX;
|
// 设置正射影像路径
|
odmTaskInfo.setOrthoimagePath(orthoimagePath);
|
// 设置正射影像图斑范围画面路径
|
odmTaskInfo.setOrtTbPath(tifParse(odmTaskInfo.getDkbh(), true, orthoimagePath));
|
// 设置正射影像api
|
odmTaskInfo.setOrthoimageApi(orthoimageApi);
|
// 航测文件倾斜摄影才有 3d-tiles 文件-修改为手动生成,自动生成的无法调参
|
// if (odmTaskInfo.getType() == 1) {
|
// String tiles3dPath = MessageFormat.format(OdmConstant.TILES_3D_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// odmTaskInfo.setTilesPath(tiles3dPath);
|
// }
|
// if (odmTaskInfo.getType() == 2) {
|
// 调用 api 对 点云laz 生成 3d-tiles,设置对应的路径,这里去生成白膜-暂时关闭白膜处理,修改为单独处理,这里处理耗时过长会导致其他状态更新异常
|
// setPointCloudTo3dTilesPath(odmTaskInfo, activeProfile);
|
// }
|
}
|
if (Strings.isBlank(odmTaskInfo.getGeom())) {
|
// 更新
|
flag = updateById(odmTaskInfo);
|
} else {
|
// 手动更新
|
int update = baseMapper.updateOdmTaskGeomInfoById(odmTaskInfo);
|
flag = update > 0 ? true : false;
|
}
|
if (flag) {
|
if (odmTaskInfo.getStatus()==WebOdmStatusEnum.COMPLETE.getStatus().intValue()
|
&& odmTaskInfo.getType()==OdmTypeEnum.ORT.getType().intValue()) {
|
// 更新正射影像tif 到 附件表
|
updateOrthoimageAttach(odmTaskInfo);
|
}
|
// 更新三维白膜 到 附件表
|
if (!Strings.isBlank(odmTaskInfo.getVoxGridTilesPath()) && odmTaskInfo.getType()==OdmTypeEnum.ALB.getType().intValue()) {
|
update3dTilesAttach(odmTaskInfo, odmTaskInfo.getVoxGridTilesPath());
|
}
|
// 更新切斜摄影 到 附件表
|
if (!Strings.isBlank(odmTaskInfo.getTilesPath()) && odmTaskInfo.getType()==OdmTypeEnum.TILT.getType().intValue()) {
|
update3dTilesAttach(odmTaskInfo, odmTaskInfo.getTilesPath());
|
}
|
}
|
// 删除缓存
|
bladeRedis.del(RedisKeyConstant.ODM_DEM_KEY);
|
// 返回
|
return flag;
|
}
|
|
/**
|
* 更新白膜/倾斜摄影 到 附件表
|
*
|
* @param odmTaskInfo
|
*/
|
public boolean update3dTilesAttach(OdmTaskInfoVO odmTaskInfo, String filePath) {
|
log.info("开始更新三维白膜/倾斜数据到附件表,odm任务id:{},航线任务id:{},文件路径:{}", odmTaskInfo.getTaskId(), odmTaskInfo.getWaylineJobId(), filePath);
|
// 通过路径获取文件
|
String path = odmTaskInfo.getBasePath() + filePath;
|
File file = new File(path);
|
// 检查文件是否存在
|
if (!file.exists()) {
|
log.error("三维白膜/倾斜数据文件 {} 不存在!", path);
|
return false;
|
}
|
Attach attach = new Attach();
|
// 计算设置文件大小
|
attach.setAttachSize(file.length());
|
// 设置文件路径,链接等
|
attach.setDomainUrl(odmProperties.getDomainUrl());
|
attach.setLink(odmProperties.getDomainUrl() + filePath);
|
attach.setName(path);
|
attach.setOriginalName(path);
|
attach.setExtension(".json");
|
// 设置航线对应的机构
|
attach.setTenantId("000000");
|
// 设置航线任务jobId
|
if (!Strings.isBlank(odmTaskInfo.getWaylineJobId())) {
|
attach.setPatrolTaskId(odmTaskInfo.getWaylineJobId());
|
}
|
// 设置类型-切斜摄影
|
if (odmTaskInfo.getType() == OdmTypeEnum.TILT.getType().intValue()) {
|
attach.setResultType(null);
|
}
|
// 设置类型-三维白膜
|
if (odmTaskInfo.getType() == OdmTypeEnum.ALB.getType().intValue()) {
|
attach.setResultType(null);
|
}
|
// 设置区域编号
|
attach.setAreaCode(odmTaskInfo.getAreaCode());
|
attach.setCreateUser(odmTaskInfo.getCreateUser());
|
attach.setUpdateUser(odmTaskInfo.getUpdateUser());
|
attach.setCreateDept(odmTaskInfo.getCreateDept());
|
attach.setCreateTime(new Date());
|
attach.setUpdateTime(new Date());
|
if (!Strings.isBlank(odmTaskInfo.getCompletedTime())) {
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
}
|
// 设置设备编号
|
if (!Strings.isBlank(odmTaskInfo.getDeviceSn())) {
|
attach.setDeviceSn(odmTaskInfo.getDeviceSn());
|
}
|
if (!Strings.isBlank(odmTaskInfo.getWorkspaceId())) {
|
attach.setWorkspaceId(odmTaskInfo.getWorkspaceId());
|
}
|
log.info("发起远程调用保存三维白膜/倾斜附件信息,tif 路径:{}", path);
|
Boolean isSaveAttach = attachClient.saveAttachInfo(attach);
|
|
// 返回
|
return isSaveAttach;
|
}
|
|
/**
|
* 调用 api 对 obj 生成 3d-tiles,设置对应的路径
|
*
|
* @param odmTaskInfo
|
* @param activeProfile
|
*/
|
private void setVoxGridTilesPath(OdmTaskInfoVO odmTaskInfo, String activeProfile) {
|
log.info("开始处理倾斜数据");
|
String objPath = odmProperties.getWebodmDataBasePath() + MessageFormat.format(OdmConstant.OBJ_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// 中心点文件路径
|
String posFilePath = odmProperties.getWebodmDataBasePath() + MessageFormat.format(OdmConstant.CENTER_FILE_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// obj 转体素化网格 3d-tiles
|
String objConvVoxGrid3dTilesUrl = pyToolsServiceProperties.getPyToolsService() + TifConstant.OBJ_VOX_GRID_3D_TILES_API;
|
// 设置请求参数
|
Map<String, Object> map = new HashMap<>(2);
|
map.put("obj_path", objPath);
|
map.put("pos_file_path", posFilePath);
|
// 远程调用接口获取结果
|
String result = pyTifSendRequest.sendPostJsonByMap(objConvVoxGrid3dTilesUrl, map);
|
log.info("当前任务编号:{},处理obj后的 3d-tiles 路径是:{}", odmTaskInfo.getTaskId(), result);
|
if (!Strings.isBlank(result)) {
|
String replaceAll = result.replaceAll("^\"|\"$", "");
|
String regex = odmProperties.getWebodmDataBasePath();
|
String[] split = replaceAll.split(regex);
|
// 设置体素网格 3d-tiles 文件路径
|
odmTaskInfo.setVoxGridTilesPath(split[1] + "/tileset.json");
|
}
|
}
|
|
/**
|
* 调用 api 对 点云生成 3d-tiles,设置对应的路径
|
*
|
* @param odmTaskInfo
|
* @param activeProfile
|
*/
|
private void setPointCloudTo3dTilesPath(OdmTaskInfoVO odmTaskInfo, String activeProfile) {
|
log.info("开始处理点云数据,通过点云提取建筑然后生成白膜数据...");
|
// obj 转体素化网格 3d-tiles
|
String pointCloudPath = odmProperties.getWebodmDataBasePath() + MessageFormat.format(OdmConstant.POINT_CLOUD_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// 拼接 url
|
String pointCloudTo3dTilesUrl = pyToolsServiceProperties.getPyToolsService() + TifConstant.POINT_CLOUD_TO_3D_TILES_API;
|
Map<String, Object> map = new HashMap<>(2);
|
map.put("piontcloud_path", pointCloudPath);
|
map.put("wayline_job_id", odmTaskInfo.getWaylineJobId());
|
// 远程调用接口获取结果
|
String result = pyTifSendRequest.sendPostJsonByMap(pointCloudTo3dTilesUrl, map);
|
log.info("当前任务编号:{},处理点云后的 3d-tiles 路径是:{}", odmTaskInfo.getTaskId(), result);
|
if (!Strings.isBlank(result)) {
|
String replaceAll = result.replaceAll("^\"|\"$", "");
|
String regex = odmProperties.getWebodmDataBasePath();
|
String[] split = replaceAll.split(regex);
|
// 设置体素网格 3d-tiles 文件路径
|
odmTaskInfo.setVoxGridTilesPath(split[1] + "/tileset.json");
|
}
|
}
|
|
/**
|
* 调用 api 对 obj 生成 3d-tiles,设置对应的路径
|
*
|
* @param odmTaskInfo
|
* @param activeProfile
|
*/
|
private void setObjTo3dTilesPath(OdmTaskInfoVO odmTaskInfo, String activeProfile) {
|
log.info("开始处理obj数据,通过obj生成实景三维倾斜数据...");
|
// 设置 obj 路径、中心点文件路径
|
String objPath = odmProperties.getWebodmDataBasePath() + MessageFormat.format(OdmConstant.OBJ_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// 中心点文件路径
|
String posFilePath = odmProperties.getWebodmDataBasePath() + MessageFormat.format(OdmConstant.CENTER_FILE_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// 设置请求参数
|
Map<String, Object> map = new HashMap<>(2);
|
map.put("obj_path", objPath);
|
map.put("pos_file_path", posFilePath);
|
// 远程调用接口获取结果
|
String result = objConvertClient.obj2TilesModel(map);
|
log.info("当前任务编号:{},处理obj后的 3d-tiles 路径是:{}", odmTaskInfo.getTaskId(), result);
|
if (!Strings.isBlank(result)) {
|
String replaceAll = result.replaceAll("^\"|\"$", "");
|
String regex = odmProperties.getWebodmDataBasePath();
|
String[] split = replaceAll.split(regex);
|
// 设置体素网格 3d-tiles 文件路径
|
odmTaskInfo.setTilesPath(split[1] + "/tileset.json");
|
}
|
}
|
|
/**
|
* 设置 正射范围4点空间面数据
|
*
|
* @param projectId 工程id
|
* @param taskId 任务id
|
* @param activeProfile
|
* @return
|
*/
|
private String getGeom(Integer projectId, String taskId, String activeProfile) {
|
// 读取文件获取EPSG:4326的面数据
|
try {
|
String modelInfoFilePath = odmProperties.getWebodmDataBasePath() + MessageFormat.format(OdmConstant.MODEL_INFO_FILE_PATH, projectId, taskId);
|
String wktData = GeoJsonToWktConverterUtils.jsonFileToWktData(modelInfoFilePath);
|
log.debug("得到的 wkt 面数据为:{}", wktData);
|
if (Strings.isBlank(wktData)){
|
return null;
|
}
|
return "'" + wktData + "'";
|
} catch (Exception e) {
|
e.printStackTrace();
|
log.error("地表模型和面数据解析异常");
|
throw new ServiceException("地表模型和面数据解析异常");
|
}
|
}
|
|
/**
|
* 设置 dem 地表模型tif 路径
|
*
|
* @param odmTaskInfo
|
* @param demTifPath
|
* @param activeProfile
|
*/
|
private void setDemTifPath(OdmTaskInfoVO odmTaskInfo, String demTifPath, String activeProfile) {
|
// 设置 tif 路径
|
demTifPath = odmProperties.getWebodmDataBasePath() + demTifPath;
|
// tif 转换 geotiff 4326
|
String parseTifUrl = pyToolsServiceProperties.getPyToolsService() + TifConstant.PARSE_TIF_CONV_API;
|
Map<String, Object> map = new HashMap<>(1);
|
map.put("dsm_path", demTifPath);
|
// 远程调用接口获取结果
|
String result = pyTifSendRequest.sendPostJsonByMap(parseTifUrl, map);
|
if (!Strings.isBlank(result)) {
|
String replaceAll = result.replaceAll("^\"|\"$", "");
|
String regex = odmProperties.getWebodmDataBasePath();
|
String[] split = replaceAll.split(regex);
|
// 设置 dem 数字地表模型tif 路径
|
odmTaskInfo.setDemTifPath(split[1]);
|
}
|
}
|
|
/**
|
* 更新正射影像tif 到 附件表
|
*
|
* @param odmTaskInfo
|
*/
|
private Boolean updateOrthoimageAttach(OdmTaskInfoVO odmTaskInfo) {
|
if (odmTaskInfo.getType() == 0) {
|
log.info("开始更新正射影像tif 到 附件表,航线任务id:{}...", odmTaskInfo.getWaylineJobId());
|
// 保存tif 数据到 附件表
|
// 通过路径获取文件
|
String path = odmTaskInfo.getBasePath() + odmTaskInfo.getOrthoimagePath();
|
File file = new File(path);
|
// 检查文件是否存在
|
if (!file.exists()) {
|
log.error("文件 {} 不存在!", path);
|
return false;
|
}
|
// 同时压缩图片
|
Attach attach = new Attach();
|
// 计算设置文件大小
|
attach.setAttachSize(file.length());
|
// 设置文件路径,链接等
|
attach.setDomainUrl(odmProperties.getDomainUrl());
|
attach.setLink(odmProperties.getDomainUrl() + odmTaskInfo.getOrthoimagePath());
|
attach.setName(path);
|
attach.setNickName(odmTaskInfo.getWaylineJobName());
|
attach.setOriginalName(path);
|
attach.setExtension(".tif");
|
// 设置航线对应的机构
|
attach.setAreaCode(odmTaskInfo.getAreaCode());
|
attach.setCreateUser(odmTaskInfo.getCreateUser());
|
attach.setUpdateUser(odmTaskInfo.getUpdateUser());
|
attach.setCreateDept(odmTaskInfo.getCreateDept());
|
attach.setCreateTime(new Date());
|
attach.setUpdateTime(new Date());
|
attach.setTenantId("000000");
|
// 设置航线任务jobId
|
if (!Strings.isBlank(odmTaskInfo.getWaylineJobId())) {
|
attach.setPatrolTaskId(odmTaskInfo.getWaylineJobId());
|
}
|
// 设置类型
|
attach.setResultType(null);
|
// 设置设备编号
|
if (!Strings.isBlank(odmTaskInfo.getDeviceSn())) {
|
attach.setDeviceSn(odmTaskInfo.getDeviceSn());
|
}
|
if (!Strings.isBlank(odmTaskInfo.getWorkspaceId())) {
|
attach.setWorkspaceId(odmTaskInfo.getWorkspaceId());
|
}
|
if (!Strings.isBlank(odmTaskInfo.getCompletedTime())) {
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
}
|
// 图片压缩
|
log.info("开始压缩tif图片,路径:{}", path);
|
Map<String, Object> map = new HashMap<>(1);
|
map.put("ort_path", path);
|
TifConvertResult tifConvertResult = tifConvertClient.tifConvJpeg(map);
|
log.info("tif 图片压缩结果:{}", tifConvertResult);
|
// 无需再上传
|
if (null != tifConvertResult && !Strings.isBlank(tifConvertResult.getBasePath())) {
|
log.info("tif 图片压缩成功,路径:{}", tifConvertResult.getBasePath());
|
Boolean isSaveAttach = attachClient.saveAttachInfo(attach);
|
if (isSaveAttach) {
|
log.info("tif 图片保存成功,发起远程调用保存tif附件信息");
|
return true;
|
}
|
}
|
}
|
return false;
|
}
|
|
|
public MultipartFile convertFileToMultipartFile(File file) {
|
try {
|
String fileName = file.getName();
|
String contentType = Files.probeContentType(file.toPath());
|
byte[] content = Files.readAllBytes(file.toPath());
|
return new MockMultipartFile("file", fileName, contentType, content);
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
return null;
|
}
|
|
/**
|
* 获取正射影像服务信息
|
*
|
* @param dkbh 图斑编号
|
* @param workspaceId 项目id
|
* @return
|
*/
|
@Override
|
public R getOrthoimageInfo(String dkbh, String workspaceId, String jobId) {
|
Double runningProgress = 1.0;
|
// 根据图斑编号 查询导出对应的正射影像信息
|
OdmTaskInfoVO odmTaskInfo = baseMapper.getTbOrthoimageInfo(dkbh, runningProgress, jobId,OdmConstant.OTR_TASK_TYPES);
|
if (null == odmTaskInfo) {
|
return R.data(null);
|
}
|
// 设置 webODM访问token
|
odmTaskInfo.setOdmToken("JWT " + sendRequest.getOdmToken());
|
// 设置图斑中心点坐标,需要访问webodm接口获取,格式化url
|
String url = MessageFormat.format(OdmConstant.GET_RTHOPHOTO_METADATA_API, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
//调用接口获取正射影像数据信息
|
JSONObject jsonObject = sendRequest.sendGetRequest(url, true);
|
// 返回
|
if (null == jsonObject) {
|
return R.data(odmTaskInfo);
|
}
|
// 设置中心点
|
JSONArray center = jsonObject.getJSONArray("center");
|
odmTaskInfo.setOrthoimageLng(center.get(0).toString());
|
odmTaskInfo.setOrthoimageLng(center.get(1).toString());
|
// 返回
|
return R.data(odmTaskInfo);
|
}
|
|
/**
|
* 获取正射影像服务信息
|
*
|
* @param lotInfoId 图斑id
|
* @param workspaceId 项目id
|
* @return
|
*/
|
@Override
|
public R getOrthoimageInfos(String lotInfoId, String workspaceId, String jobId) {
|
Double runningProgress = 1.0;
|
List<OdmTaskInfoVO> odmTaskInfoList = baseMapper.listTbOrthoimageInfoAsc(lotInfoId, runningProgress, jobId,OdmConstant.OTR_TASK_TYPES);
|
|
if (CollectionUtils.isEmpty(odmTaskInfoList)) {
|
return R.data(Collections.emptyList());
|
}
|
|
// --- 优化:将循环外不变的变量提前获取 ---
|
String odmToken = "JWT " + sendRequest.getOdmToken();
|
String activeProfile = SpringContextUtil.getActiveProfile(); // 获取一次环境配置
|
String nowDateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); // 获取一次当前日期字符串
|
|
// 遍历列表,为每个对象填充额外信息
|
for (OdmTaskInfoVO taskInfo : odmTaskInfoList) {
|
// 1. 设置 Token
|
taskInfo.setOdmToken(odmToken);
|
|
// 2. 设置中心点坐标
|
String metaUrl = MessageFormat.format(OdmConstant.GET_RTHOPHOTO_METADATA_API, taskInfo.getProjectId(), taskInfo.getTaskId());
|
JSONObject jsonObject = sendRequest.sendGetRequest(metaUrl, true);
|
if (jsonObject != null) {
|
JSONArray center = jsonObject.getJSONArray("center");
|
if (center != null && center.size() >= 2) {
|
taskInfo.setOrthoimageLng(center.get(0).toString());
|
taskInfo.setOrthoimageLat(center.get(1).toString());
|
}
|
// 获取边界点
|
String bounds = jsonObject.getJSONObject("bounds").getJSONArray("value").toJSONString();
|
log.info("边界点:{}", bounds);
|
taskInfo.setBounds(bounds);
|
}
|
|
// 3. 为当前 taskInfo 生成并设置下载 URL
|
String downloadUrl = generateDownloadUrlForTask(taskInfo, activeProfile, nowDateStr);
|
taskInfo.setDownloadUrl(downloadUrl);
|
}
|
|
return R.data(odmTaskInfoList);
|
}
|
|
/**
|
* 为单个任务信息对象生成下载链接。
|
* 该方法整合了原有的 getOdmTaskDownUrl 和 getMasTifUrl 的逻辑。
|
*
|
* @param taskInfo 任务信息对象
|
* @param activeProfile 当前运行环境
|
* @param nowDateStr 当前日期字符串 (yyyyMMdd)
|
* @return 生成的下载链接,如果失败则返回 null
|
*/
|
private String generateDownloadUrlForTask(OdmTaskInfoVO taskInfo, String activeProfile, String nowDateStr) {
|
// 优先判断是否有裁剪好的图斑面(ortTbPath)
|
if (!Strings.isBlank(taskInfo.getOrtTbPath())) {
|
return odmProperties.getDomainUrl() + taskInfo.getOrtTbPath();
|
}
|
|
// 如果没有 ortTbPath,则执行文件复制并生成新的 URL
|
try {
|
// 确定输出目录
|
String outDir = odmDownTempBasePath + "/" + nowDateStr;
|
File dir = new File(outDir);
|
if (!dir.exists()) {
|
dir.mkdirs();
|
}
|
|
// 定义源文件和目标文件路径
|
String inputPath = taskInfo.getBasePath() + taskInfo.getOrthoimagePath();
|
String outPath = outDir + "/" + taskInfo.getDkbh() + ".tif";
|
|
File newFile = new File(outPath);
|
|
// --- 文件复制逻辑 ---
|
// 使用 try-with-resources 语句,可以自动关闭流,更安全
|
try (FileInputStream fileInputStream = new FileInputStream(inputPath);
|
FileOutputStream os = new FileOutputStream(newFile)) {
|
IOUtils.copy(fileInputStream, os);
|
}
|
|
// 成功复制后,返回可访问的下载地址
|
return odmDownPreFixPath + "/" + nowDateStr + "/" + taskInfo.getDkbh() + ".tif";
|
|
} catch (IOException e) {
|
// 使用日志记录错误,而不是打印到控制台
|
log.error("为地块 {} 生成下载TIF文件失败: {}", taskInfo.getDkbh(), e.getMessage());
|
return null; // 发生异常时返回 null
|
}
|
}
|
|
|
/**
|
* 自定义删除
|
*
|
* @param idList
|
* @return
|
*/
|
@Override
|
public Boolean removeOdmTaskByIds(List<Long> idList) {
|
List<OdmTaskInfo> list = new ArrayList<>();
|
int num1 = 0;
|
int num2 = 0;
|
// 遍历删除
|
for (Long id : idList) {
|
// 查询对应的任务信息
|
OdmTaskInfo taskInfo = getById(id);
|
list.add(taskInfo);
|
// 删除
|
boolean b = removeById(id);
|
if (b) {
|
num1++;
|
}
|
}
|
// 删除webodm任务
|
num2 = removeWebodmTask(list, num2);
|
return num1 == num2;
|
}
|
|
/**
|
* 删除webodm任务
|
*
|
* @param list
|
* @param num2
|
* @return
|
*/
|
private int removeWebodmTask(List<OdmTaskInfo> list, int num2) {
|
// 删除webodm上对应的任务
|
for (OdmTaskInfo odmTaskInfo : list) {
|
// 格式化url
|
String url = MessageFormat.format(OdmConstant.REMOVE_TASK_INFO_API, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
//调用接口删除信息
|
JSONObject jsonObject = sendRequest.sendPostFormData(url, true, null);
|
// 返回
|
if (null != jsonObject) {
|
Boolean success = jsonObject.getBoolean("success");
|
if (success) {
|
num2++;
|
// 异步删除对应的文件
|
asyncService.deleteFileByTaskId(odmTaskInfo.getTaskId());
|
}
|
}
|
}
|
return num2;
|
}
|
|
/**
|
* 删除webodm任务
|
*
|
* @param list
|
* @return
|
*/
|
private boolean updateAndRemoveWebodmTask(List<OdmTaskInfo> list) {
|
int num = 0;
|
// 删除webodm上对应的任务
|
for (OdmTaskInfo odmTaskInfo : list) {
|
// 格式化url
|
String url = MessageFormat.format(OdmConstant.REMOVE_TASK_INFO_API, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
//调用接口删除信息
|
JSONObject jsonObject = sendRequest.sendPostFormData(url, true, null);
|
// 返回
|
if (null != jsonObject) {
|
Boolean success = jsonObject.getBoolean("success");
|
if (success) {
|
// 异步删除对应的文件
|
boolean result = asyncService.deleteFileByTaskId(odmTaskInfo.getTaskId());
|
if (result) {
|
odmTaskInfo.setFileDelFlag(1);
|
// 更新删除状态
|
updateById(odmTaskInfo);
|
num++;
|
}
|
}
|
}
|
}
|
// 返回结果
|
return list.size() == num;
|
}
|
|
/**
|
* 定时任务----异常 odm 任务重试
|
*
|
* @return
|
*/
|
@Override
|
public Boolean runErrorWebOdmTask() {
|
int flag = 0;
|
// 查询异常任务,异常次数3以下的,超过3次可以手动触发,不会自动进行重试 ,外加已取消的任务进行重启,暂定每次只处理一个,每次只查询一个
|
List<OdmTaskInfo> list = baseMapper.getErrorWebOdmTaskList();
|
if (list.size() == 0) {
|
return true;
|
}
|
// 开始处理
|
// 格式化重启任务url
|
for (OdmTaskInfo odmTaskInfo : list) {
|
String url = MessageFormat.format(OdmConstant.RESTART_TASK_API, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
//调用接口重启任务
|
JSONObject jsonObject = sendRequest.sendPostFormData(url, true, null);
|
// 返回
|
if (null != jsonObject) {
|
Boolean success = jsonObject.getBoolean("success");
|
if (success) {
|
flag++;
|
int num = odmTaskInfo.getErrorNum();
|
num++;
|
// 更新odmTaskInfo 信息
|
if (odmTaskInfo.getStatus() == WebOdmStatusEnum.FAIL.getStatus().intValue()) {
|
odmTaskInfo.setErrorNum(num);
|
odmTaskInfo.setLastError("");
|
}
|
odmTaskInfo.setRunningProgress(0.00);
|
odmTaskInfo.setProcessingTime(0L);
|
// 重置更新状态
|
odmTaskInfo.setIsUpdate(0);
|
// 更新
|
baseMapper.updateById(odmTaskInfo);
|
}
|
}
|
}
|
// 返回
|
return flag == list.size();
|
}
|
|
/**
|
* 更新正射影像tif 信息到 附件表
|
*
|
* @return
|
*/
|
@Override
|
public R updateOrthoimageToAttach() {
|
int count = 0;
|
// 查询已完成的任务中没有将tif 信息同步到 附件表的信息
|
List<OdmTaskInfoVO> list = baseMapper.getNoSaveAttachOdmTaskInfoList();
|
// 遍历将tif 信息同步到附件表
|
for (OdmTaskInfoVO odmTaskInfo : list) {
|
Boolean flag = updateOrthoimageAttach(odmTaskInfo);
|
if (flag) {
|
count++;
|
}
|
}
|
return R.status(count == list.size());
|
}
|
|
/**
|
* odm 任务重启
|
*
|
* @param taskId 航线任务id
|
* @return
|
*/
|
@Override
|
public Boolean restartOdmTask(String taskId) {
|
boolean flag = false;
|
// 查询对应odm 信息
|
QueryWrapper<OdmTaskInfo> wrapper = new QueryWrapper<>();
|
wrapper.eq("task_id", taskId);
|
OdmTaskInfo odmTaskInfo = getOne(wrapper);
|
// 先重置
|
resetOdmTask(taskId, odmTaskInfo.getProjectId());
|
// 获取重启任务url
|
String url = MessageFormat.format(OdmConstant.RESTART_TASK_API, odmTaskInfo.getProjectId(), taskId);
|
//调用接口重启任务
|
JSONObject jsonObject = sendRequest.sendPostFormData(url, true, null);
|
// 返回
|
if (null != jsonObject) {
|
Boolean success = jsonObject.getBoolean("success");
|
if (success) {
|
// 更新odmTaskInfo 信息
|
if (odmTaskInfo.getStatus() == WebOdmStatusEnum.FAIL.getStatus().intValue()) {
|
odmTaskInfo.setLastError("");
|
}
|
odmTaskInfo.setRunningProgress(0.00);
|
odmTaskInfo.setProcessingTime(0L);
|
odmTaskInfo.setStatus(0);
|
// 重置更新状态
|
odmTaskInfo.setIsUpdate(0);
|
// 更新
|
int update = baseMapper.updateById(odmTaskInfo);
|
if (update > 0) {
|
flag = true;
|
}
|
}
|
}
|
return flag;
|
}
|
|
/**
|
* 重置信息
|
*
|
* @param taskId
|
* @param projectId
|
*/
|
private boolean resetOdmTask(String taskId, Integer projectId) {
|
// 查询任务信息
|
QueryWrapper<OdmTaskInfo> wrapper = new QueryWrapper<>();
|
wrapper.eq("task_id", taskId);
|
OdmTaskInfo odmTaskInfo = getOne(wrapper);
|
// 查询当前航线任务的类型(过滤掉不参与图片合并的类型),当前只要 2:航测 4:正射举证 5:倾斜摄影 6:视频切斜摄影
|
Integer waylineType = baseMapper.getWaylineType(odmTaskInfo.getWaylineJobId(), OdmConstant.ODM_TASK_OPTION_TYPE);
|
// 如果不存在
|
if (null == waylineType) {
|
return false;
|
}
|
String url = MessageFormat.format(OdmConstant.PATCH_TASK_INFO_API, projectId, taskId);
|
Map<String, Object> map = new HashMap<>(2);
|
// 获取选项参数信息
|
List<Map<String, Object>> options = OdmConstant.ODM_TASK_RESET_OPTIONS.get(waylineType);
|
map.put("options", options);
|
JSONObject jsonObject = sendRequest.sendPatchJsonByMap(url, true, map);
|
if (null != jsonObject) {
|
return true;
|
}
|
return false;
|
}
|
|
/**
|
* 获取odm 任务下载url
|
*
|
* @param dkbh
|
* @param workspaceId
|
* @return
|
*/
|
@Override
|
public String getOdmTaskDownUrl(String dkbh, String workspaceId, String jobId) {
|
Double runningProgress = 1.0;
|
// 根据图斑编号 查询导出对应的正射影像信息
|
OdmTaskInfoVO odmTaskInfo = baseMapper.getTbOrthoimageInfo(dkbh, runningProgress, jobId,OdmConstant.OTR_TASK_TYPES);
|
if (null == odmTaskInfo) {
|
return null;
|
}
|
// 判断是否有画好的图斑面,如果没有则正常取原tif图
|
if (!Strings.isBlank(odmTaskInfo.getOrtTbPath())) {
|
// 拼接 url
|
return odmProperties.getDomainUrl() + odmTaskInfo.getOrtTbPath();
|
}
|
// 获取原tif重命名图
|
return getMasTifUrl(dkbh, odmTaskInfo);
|
}
|
|
/**
|
* 获取原tif重命名图
|
*
|
* @param dkbh
|
* @param odmTaskInfo
|
* @return
|
*/
|
@NotNull
|
private String getMasTifUrl(String dkbh, OdmTaskInfoVO odmTaskInfo) {
|
// 下载tif 图片,重新赋名
|
LocalDateTime currentTime = LocalDateTime.now();
|
// 格式化时间,生成当前时间
|
String nowDateStr = currentTime.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
String outPath = odmDownTempBasePath + "/" + nowDateStr;
|
// 获取路径
|
String inputPath = odmTaskInfo.getBasePath() + odmTaskInfo.getOrthoimagePath();
|
// String inputPath = "E:\\work\\temp\\odm\\odm_orthophoto.tif";
|
//通过上传路径得到上传的文件夹
|
File file = new File(outPath);
|
//若目标文件夹不存在,则创建
|
if (!file.exists()) {
|
file.mkdirs();
|
}
|
//定义重新生成后的目标文件名
|
outPath = outPath + "/" + dkbh + ".tif";
|
File newFile = new File(outPath);
|
// 根据目标文件的新建其输出流对象
|
FileOutputStream os = null;
|
try {
|
FileInputStream fileInputStream = new FileInputStream(inputPath);
|
os = new FileOutputStream(newFile);
|
//完成输入流到输出流的复制
|
IOUtils.copy(fileInputStream, os);
|
//关闭流(先开后关)
|
os.close();
|
fileInputStream.close();
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
// 返回下载地址
|
return odmDownPreFixPath + "/" + nowDateStr + "/" + dkbh + ".tif";
|
}
|
|
/**
|
* 定时删除odm临时文件任务
|
*
|
* @return
|
*/
|
@Override
|
public boolean deletedOdmTempFile() {
|
// 删除下载临时目录
|
String path = odmDownTempBasePath;
|
deletedFile(new File(path));
|
// 删除下载临时zip目录
|
// String zipPath = odmDownTempZipBasePath;
|
// deletedFile(new File(zipPath));
|
return true;
|
}
|
|
|
/**
|
* 定时删除odm拼图任务中失效的文件
|
*
|
* @return
|
*/
|
@Override
|
public boolean deletedOdmTaskInVFile() {
|
// 清理已删除的 odm 任务产生的文件(任务删除后没有删除掉对于的文件)
|
List<Integer> typeList = new ArrayList<>();
|
typeList.add(WebOdmProjectIdEnum.ORT.getProjectId());
|
typeList.add(WebOdmProjectIdEnum.TD.getProjectId());
|
for (Integer type : typeList) {
|
// odm 任务查询 url
|
String url = MessageFormat.format(OdmConstant.QUERY_TASK_API, type);
|
//调用接口重启任务
|
JSONArray jsonArray = sendRequest.sendGetRequestResArr(url, true);
|
if (jsonArray.size() == 0) {
|
log.error("没有查询到任务信息");
|
return false;
|
}
|
List<String> nowList = new ArrayList<>();
|
// 转换成taskId集合
|
for (int i = 0; i < jsonArray.size(); i++) {
|
nowList.add(jsonArray.getJSONObject(i).getString("id"));
|
}
|
// 读取对应文件目录列表
|
String filePath = MessageFormat.format(odmProperties.getWebodmDataBasePath() + OdmConstant.TASK_FILE_PATH, type);
|
File file = new File(filePath);
|
File[] files = file.listFiles();
|
if (null != files) {
|
for (File f : files) {
|
// 如果当前文件名(任务id)不存在已有的任务则删除
|
if (!nowList.contains(f.getName())) {
|
deletedFile(f);
|
}
|
}
|
}
|
}
|
return true;
|
}
|
|
/**
|
* 定时删除odm拼图任务中重复的任务(同一个图斑多个任务,保留最新的)
|
*
|
* @return
|
*/
|
@Override
|
public boolean deletedRepOdmTask() {
|
// 查询同一个图斑多出的任务进行删除
|
List<OdmTaskInfo> list = baseMapper.selectReqOdmList();
|
boolean a = removeOdmTaskByList(list);
|
// 删除掉odm_task_info 表中没有的任务
|
boolean b = removeSuperfluousTask();
|
// 删除并返回
|
return a && b;
|
}
|
|
/**
|
* 删除掉 odm_task_info 表中没有的任务
|
*
|
* @return
|
*/
|
private boolean removeSuperfluousTask() {
|
List<Integer> typeList = new ArrayList<>();
|
typeList.add(WebOdmProjectIdEnum.ORT.getProjectId());
|
typeList.add(WebOdmProjectIdEnum.TD.getProjectId());
|
for (Integer type : typeList) {
|
// 查询出所有odm_task_info 任务
|
List<OdmTaskInfo> list = list();
|
List<String> taskIds = list.stream().map(OdmTaskInfo::getTaskId)
|
.collect(Collectors.toList());
|
// 查询出webodm 中所有的任务
|
// odm 任务查询 url
|
String url = MessageFormat.format(OdmConstant.QUERY_TASK_API, type);
|
//调用接口重启任务
|
JSONArray jsonArray = sendRequest.sendGetRequestResArr(url, true);
|
if (jsonArray.size() == 0) {
|
log.error("没有查询到任务信息");
|
return false;
|
}
|
List<String> nowList = new ArrayList<>();
|
// 转换成taskId集合
|
for (int i = 0; i < jsonArray.size(); i++) {
|
nowList.add(jsonArray.getJSONObject(i).getString("id"));
|
}
|
// 获取 nowList 中但不在 taskIds 中的元素
|
List<String> difference = nowList.stream()
|
.filter(element -> !taskIds.contains(element))
|
.collect(Collectors.toList());
|
// 删除webodm 任务中多出的任务
|
for (String taskId : difference) {
|
//调用接口删除信息
|
JSONObject jsonObject = sendRequest.sendPostFormData(
|
MessageFormat.format(OdmConstant.REMOVE_TASK_INFO_API, type, taskId), true, null
|
);
|
// 返回
|
if (null != jsonObject) {
|
Boolean success = jsonObject.getBoolean("success");
|
if (success) {
|
// 异步删除对应的文件
|
asyncService.deleteFileByTaskId(taskId);
|
}
|
}
|
}
|
}
|
return true;
|
}
|
|
/**
|
* 定时删除odm拼图任务(航线任务已删除的)
|
*
|
* @return
|
*/
|
@Override
|
public boolean delRepOdmTaskByNoWayJob() {
|
// 查询出航线任务已经删除的任务,进行删除对于odm 任务
|
List<OdmTaskInfo> list = baseMapper.selectRemWayJobList();
|
// 删除并返回
|
return removeOdmTaskByList(list);
|
}
|
|
/**
|
* 删除odm任务
|
*
|
* @param list
|
* @return
|
*/
|
private boolean removeOdmTaskByList(List<OdmTaskInfo> list) {
|
// 删除
|
int num1 = 0;
|
int num2 = 0;
|
// 遍历删除
|
for (OdmTaskInfo odmTaskInfo : list) {
|
odmTaskInfo.setFileDelFlag(1);
|
// 数据不删除,修改数据状态
|
boolean b = updateById(odmTaskInfo);
|
if (b) {
|
num1++;
|
}
|
}
|
// 删除webodm任务
|
num2 = removeWebodmTask(list, num2);
|
// 返回
|
return num1 == num2;
|
}
|
|
/**
|
* tif 图片解析画图斑面
|
*
|
* @param dkbh 地块编号
|
* @param flag 是否已完成拼图任务
|
* @param orthoimagePath 正射文件路径
|
* @return
|
*/
|
@Override
|
public String tifParse(String dkbh, Boolean flag, String orthoimagePath) {
|
log.info("tif 图片解析画图斑面,地块编号:{},是否完成拼图任务:{},正射文件路径:{}", dkbh, flag, orthoimagePath);
|
if (null == flag) {
|
flag = false;
|
}
|
if (Strings.isBlank(dkbh)) {
|
log.error("地块编号不能为空");
|
return null;
|
}
|
// 根据图斑编号 查询导出对应的正射影像信息
|
OdmTaskInfoVO odmTaskInfo = baseMapper.getTbOrthoimageInfo(dkbh, null, "",OdmConstant.OTR_TASK_TYPES);
|
if (Strings.isBlank(orthoimagePath)) {
|
orthoimagePath = odmTaskInfo.getOrthoimagePath();
|
}
|
// 解析
|
if (null == odmTaskInfo) {
|
log.error("没有查到对应的任务");
|
return null;
|
}
|
if (!flag && odmTaskInfo.getRunningProgress() < 1.0) {
|
log.error("拼图任务尚未完成");
|
return null;
|
}
|
if (Strings.isBlank(odmTaskInfo.getGeom())) {
|
log.error("当前地块无空间面数据");
|
return null;
|
}
|
// 将面数据转点集合
|
List<Coordinate> coordinates = polygonStringToCoordinates(odmTaskInfo.getGeom());
|
// 下载tif 图片,重新赋名
|
// 格式化正射影像图斑画面路径
|
String outPath = odmProperties.getWebodmDataBasePath() + MessageFormat.format(OdmConstant.ORTHO_TB_IMAGE_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// 获取路径
|
String inputPath = odmTaskInfo.getBasePath() + orthoimagePath;
|
// String inputPath = "C:\\Users\\Administrator\\Desktop\\其他\\无人机\\102.tif";
|
//通过上传路径得到上传的文件夹
|
File file = new File(outPath);
|
//若目标文件夹不存在,则创建
|
if (!file.exists()) {
|
file.mkdirs();
|
}
|
//定义重新生成后的目标文件名
|
outPath = outPath + "/" + dkbh + ".tif";
|
log.info("tif 文件路径:{}", outPath);
|
log.debug("tif 对应面数据:{}", coordinates);
|
// outPath = "C:\\Users\\Administrator\\Desktop\\其他\\无人机\\out.tif";
|
// 将所有点转换成地理坐标
|
List<Point> pointList = ParseTifUtils.getTifPosByMoreLngLat(inputPath, new ArrayList<>(coordinates));
|
log.debug("tif 地理坐标数据:{}", pointList);
|
// 将所有点的地理坐标转换成像素坐标
|
List<MyPoint> pixelByPointList = getPixelByPointList(inputPath, pointList);
|
if (pixelByPointList.size() == 0) {
|
log.error("点位转换失败");
|
return null;
|
}
|
// 画线
|
boolean drawFlag = ParseTifUtils.drawPolygonByPoints(inputPath, outPath, pixelByPointList, "tif");
|
// 失败返回空
|
if (!drawFlag) {
|
log.error("画面失败");
|
return null;
|
}
|
// 返回数据路径
|
return MessageFormat.format(OdmConstant.ORTHO_TB_IMAGE_PATH, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId()) + "/" + dkbh + ".tif";
|
}
|
|
/**
|
* 将所有点的地理坐标转换成像素坐标
|
*
|
* @param inputPath tif 图片路径
|
* @param pointList 地理坐标集合
|
* @return
|
*/
|
@Override
|
public List<MyPoint> getPixelByPointList(String inputPath, List<Point> pointList) {
|
// 转 myPoint
|
List<MyPoint> myPoints = new ArrayList<>();
|
for (Point point : pointList) {
|
myPoints.add(new MyPoint(point.getX(), point.getY()));
|
}
|
String url = pyToolsServiceProperties.getPyToolsService() + TifConstant.PARSE_TIF_API;
|
Map<String, Object> map = new HashMap<>(2);
|
map.put("path", inputPath);
|
map.put("source_points", myPoints);
|
log.info("转换地理坐标数据:{}", map);
|
// 远程调用接口获取结果
|
String result = pyTifSendRequest.sendPostJsonByMap(url, map);
|
JSONArray jsonArray = JSON.parseArray(result);
|
// 清除原有数据进行重新赋值返回
|
myPoints.clear();
|
if (null != jsonArray) {
|
for (int i = 0; i < jsonArray.size(); i++) {
|
MyPoint myPoint = new MyPoint();
|
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
myPoint.setX(jsonObject.getDouble("x"));
|
myPoint.setY(jsonObject.getDouble("y"));
|
myPoints.add(myPoint);
|
}
|
}
|
return myPoints;
|
}
|
|
/**
|
* polygon 数据转点集合数据
|
*
|
* @param polygonString
|
* @return
|
*/
|
public List<Coordinate> polygonStringToCoordinates(String polygonString) {
|
List<Coordinate> points = new ArrayList<>();
|
String[] coordinates = polygonString.replace("POLYGON((", "").replace("))", "").split(",");
|
// 遍历转换
|
for (String coordinate : coordinates) {
|
String[] point = coordinate.trim().split(" ");
|
double x = Double.parseDouble(point[0]);
|
double y = Double.parseDouble(point[1]);
|
points.add(new Coordinate(x, y));
|
}
|
return points;
|
}
|
|
/**
|
* 处理没有 tif 画面的
|
*
|
* @return
|
*/
|
@Override
|
public boolean handleNotParseTif() {
|
// 查询没有处理tif拼图斑面的任务
|
List<OdmTaskInfoVO> odmTaskInfoVOList = baseMapper.getNotParseTifOdmList(OdmConstant.OTR_TASK_TYPES);
|
int size = odmTaskInfoVOList.size();
|
//处理
|
int num = 0;
|
if (size > 0) {
|
for (OdmTaskInfoVO odmTaskInfoVO : odmTaskInfoVOList) {
|
// 更新
|
String ortTbPath = tifParse(odmTaskInfoVO.getDkbh(), true, odmTaskInfoVO.getOrthoimagePath());
|
odmTaskInfoVO.setOrtTbPath(ortTbPath);
|
boolean update = updateById(odmTaskInfoVO);
|
if (update) {
|
// 更新正射影像tif 到 附件表
|
updateOrthoimageAttach(odmTaskInfoVO);
|
num++;
|
}
|
}
|
}
|
if (size == num) {
|
return true;
|
}
|
return false;
|
}
|
|
/**
|
* 定时删除odm拼图任务(保留一个月内的数据) 按天来,动态传参
|
*
|
* @param day
|
* @return
|
*/
|
@Override
|
public boolean delRepOdmTaskByBefOneMon(Integer day) {
|
// 查询一个月之外并且没有删除的任务
|
List<OdmTaskInfo> list = baseMapper.getOneMonLatOdmTask(day);
|
// 删除并返回
|
return updateAndRemoveWebodmTask(list);
|
}
|
|
/**
|
* 历史odm tif 数据处理
|
*
|
* @return
|
*/
|
@Override
|
public Boolean hisOdmTaskTifDataHandler() {
|
int number = 0;
|
// 1. 查询所有历史 odm 任务列表
|
List<OdmTaskInfoVO> list = baseMapper.getHistoryOdmTaskByTifComp(OdmConstant.OTR_TASK_TYPES);
|
// 2. 遍历列表处理历史odm 图片
|
for (OdmTaskInfoVO odmTaskInfo : list) {
|
// 通过路径获取文件
|
String path = odmTaskInfo.getBasePath() + odmTaskInfo.getOrthoimagePath();
|
File file = new File(path);
|
// 检查文件是否存在
|
if (!file.exists()) {
|
log.error("文件 {} 不存在!", path);
|
continue;
|
}
|
String smallTargetName = "_small.jpeg";
|
File smallOutFile = new File(file.getParent(), file.getName().replace(".tif", smallTargetName));
|
// 检查文件是否存在
|
if (smallOutFile.exists()) {
|
log.error("文件已存在!", path);
|
continue;
|
}
|
// 图片压缩
|
log.info("开始压缩tif图片,路径:{}", path);
|
Map<String, Object> map = new HashMap<>(1);
|
map.put("ort_path", path);
|
TifConvertResult tifConvertResult = tifConvertClient.tifConvJpeg(map);
|
log.info("tif 图片压缩结果:{}", tifConvertResult);
|
}
|
return number == list.size();
|
}
|
|
/**
|
* 视频处理成3d 白膜测试
|
*
|
* @param jobId
|
* @return
|
*/
|
@Override
|
public boolean videoToOdm(String jobId) {
|
// 1. 获取视频地址
|
List<String> mediaObjectKeyList = baseMapper.getMediaFileInfoByWaylineJobId(jobId);
|
List<String> picAllList = new ArrayList<>();
|
if (mediaObjectKeyList.size() > 0) {
|
// 判断是否有再处理视频的任务,如果有则不处理,没有才处理,防止cpu,内存使用过多
|
String value = bladeRedis.get(RedisKeyConstant.ODM_VIDEO_TO_PIC_KEY);
|
if (!Strings.isBlank(value)) {
|
log.info("已存在处理的视频,待处理完成后执行,退出当前执行");
|
return false;
|
}
|
// 设置redis,有效时长 60 分钟
|
bladeRedis.setEx(RedisKeyConstant.ODM_VIDEO_TO_PIC_KEY, "1", RedisKeyConstant.ODM_VIDEO_TO_PIC_TIME);
|
// 设置缓存
|
for (String objectKey : mediaObjectKeyList) {
|
// 找到第一个/的位置
|
int firstSlashIndex = objectKey.indexOf('/');
|
if (firstSlashIndex != -1) {
|
// 截取第一个/之后的部分
|
objectKey = "/" + objectKey.substring(firstSlashIndex + 1);
|
}
|
String videoPath = "";
|
// 获取运行环境信息
|
String activeProfile = SpringContextUtil.getActiveProfile();
|
// 拼接 url
|
if (activeProfile.equals("test") || activeProfile.equals("prod")) {
|
videoPath = minioDataConstant.getServerBasePath() + minioDataConstant.getBucket() + objectKey;
|
}
|
// 2. 读取视频,视频切图
|
try {
|
boolean mp4ToJpgFlag = VideoConverterUtils.mp4ToJpgRestriction(videoPath, OdmConstant.FRAME_INTERVAL);
|
boolean mp4ToCreateStrFlag = VideoConverterUtils.mp4ToCreateStr(videoPath);
|
if (mp4ToJpgFlag && mp4ToCreateStrFlag) {
|
// 3. 切图图片经纬度、高度赋值
|
String url = pyToolsServiceProperties.getPyToolsService() + TifConstant.SET_PIC_LOCATION_API;
|
String inputPath = VideoConverterUtils.getOutputPathByInputPath(videoPath);
|
String srtPath = inputPath + "out.srt";
|
String picPath = inputPath + "*.jpg";
|
Map<String, Object> map = new HashMap<>(2);
|
map.put("pic_path", picPath);
|
map.put("srt_path", srtPath);
|
// 远程调用接口获取结果
|
String result = pyTifSendRequest.sendPostJsonByMap(url, map);
|
boolean parseBoolean = Boolean.parseBoolean(result);
|
// 4. 生成 webodm 任务
|
if (parseBoolean) {
|
// 获取图片集合
|
List<String> list = getPicListByPath(inputPath, objectKey);
|
// 合并数据集
|
picAllList.addAll(list);
|
}
|
}
|
} catch (IOException e) {
|
// 清除缓存
|
bladeRedis.del(RedisKeyConstant.ODM_VIDEO_TO_PIC_KEY);
|
e.printStackTrace();
|
} catch (InterruptedException e) {
|
// 清除缓存
|
bladeRedis.del(RedisKeyConstant.ODM_VIDEO_TO_PIC_KEY);
|
e.printStackTrace();
|
}
|
}
|
if (picAllList.size() > 0) {
|
// 先创建任务
|
OdmTaskInfo odmTaskInfo = new OdmTaskInfo();
|
odmTaskInfo.setWaylineJobId(jobId);
|
boolean b = saveOdmTaskInfo(odmTaskInfo);
|
if (b) {
|
createOdmTask(jobId, null, WaylineTypeEnum.ALB_COL.getType().intValue(), picAllList, odmTaskInfo.getProjectId());
|
}
|
// 清除缓存
|
bladeRedis.del(RedisKeyConstant.ODM_VIDEO_TO_PIC_KEY);
|
}
|
}
|
// 下面步骤需要依赖定时任务触发执行
|
// 5. 通过定时任务定时获取已完成的视频任务
|
// 6. 视频拼图任务完成后,处理地表模型数据,地表模型 tif 写入数据库
|
// 7. 将 obj 文件处理成白膜
|
// 8. 将生成的白膜转换成 3dtiles,并生成路径保存到数据库
|
return true;
|
}
|
|
@Override
|
public List<OdmTaskInfo> getTifDataByCoord(double longitude, double latitude) {
|
return baseMapper.getTifDataByCoord(longitude, latitude);
|
}
|
|
/**
|
* 根据经纬度获取10公里内tif数据
|
*
|
* @param longitude 经度
|
* @param latitude 纬度
|
* @return
|
*/
|
@Override
|
public List<OdmTaskInfo> getAllTifDataByCoord(double longitude, double latitude) {
|
return baseMapper.getAllTifDataByCoord(longitude, latitude);
|
}
|
|
|
public boolean saveOdmTaskInfo(OdmTaskInfo odmTaskInfo) {
|
OdmTaskInfo odmTask = getOdmTaskInfo(odmTaskInfo.getWaylineJobId());
|
if (null == odmTask) {
|
OdmTaskInfo jobBaseInfo = baseMapper.selectJobBaseInfo(odmTaskInfo.getWaylineJobId());
|
odmTaskInfo.setType(OdmTypeEnum.ALB.getType());
|
odmTaskInfo.setProjectId(WebOdmProjectIdEnum.TD.getProjectId());
|
odmTaskInfo.setAreaCode(jobBaseInfo.getAreaCode());
|
odmTaskInfo.setCreateUser(jobBaseInfo.getCreateUser());
|
odmTaskInfo.setUpdateUser(jobBaseInfo.getUpdateUser());
|
odmTaskInfo.setCreateDept(jobBaseInfo.getCreateDept());
|
odmTaskInfo.setCreateTime(new Date());
|
odmTaskInfo.setUpdateTime(new Date());
|
boolean save = this.save(odmTaskInfo);
|
return save;
|
}
|
return false;
|
}
|
|
/**
|
* 获取图片路径集合
|
*
|
* @param inputDir 图片完整路径目录
|
* @param object_key 视频文件的 object_key
|
* @return
|
*/
|
private List<String> getPicListByPath(String inputDir, String object_key) throws IOException {
|
List<String> list = new ArrayList<>();
|
int lastSlashIndex = object_key.lastIndexOf('.');
|
if (lastSlashIndex != -1) {
|
// 取 object_key 目录,取文件名作为目录
|
String pathBeforeLastSlash = object_key.substring(0, lastSlashIndex);
|
Path startPath = Paths.get(inputDir);
|
Files.walk(startPath)
|
.filter(Files::isRegularFile)
|
.filter(p -> p.toString().toLowerCase().endsWith(".jpg"))
|
.forEach(p -> list.add(pathBeforeLastSlash + File.separator + p.getFileName().toString()));
|
}
|
return list;
|
}
|
|
/**
|
* 查询地图位置上3d-tiles数据
|
*
|
* @param dto
|
* @return
|
*/
|
@Override
|
public TreeVo getVoxGridTilesList(OdmTaskInfoQueryParam dto) {
|
if (null==dto.getType()){
|
// 默认展示查询白膜
|
dto.setType(AttachResultTypeEnum.ALB.getType());
|
}
|
if (Objects.nonNull(dto.getDateEnum())) {
|
TimeRange timeRange = TimeRangeUtils.getTimeRange(dto.getDateEnum());
|
dto.setStartDate(timeRange.getStartTime());
|
dto.setEndDate(timeRange.getEndTime());
|
}
|
// 设置区域code
|
String areaCode = HeaderUtils.getHeader(CommonConstant.AREA_CODE).orElse(null);
|
dto.setAreaCode(!StringUtils.isEmpty(dto.getAreaCode()) ? dto.getAreaCode() : areaCode);
|
//所有区域数据
|
Object regionResultz = RedisOpsUtils.get(CacheConstant.REGION_INFO + dto.getAreaCode());
|
if (Objects.isNull(regionResultz)) {
|
R<TreeVo> regionResult = sysClient.getRegionTreeByPrentCode(dto.getAreaCode());
|
RedisOpsUtils.setWithExpire(CacheConstant.REGION_INFO + dto.getAreaCode(), regionResult.getData(), TimeUnit.HOURS.toSeconds(1));
|
regionResultz = regionResult.getData();
|
}
|
// 设置区域树数据
|
TreeVo data = regionResultz == null ? null : (TreeVo) regionResultz;
|
// 设置格式化后的区域编号
|
dto.setAreaCode(HeaderUtils.getAreaCode(dto.getAreaCode()));
|
|
//根据条件查询所有区域对应的odm统计数据
|
// List<OdmTaskStatisticsBO> odmTaskStatisticsBOList = baseMapper.getOdmTaskStatisticsByAreaCode(dto);
|
//获取3d-tiles明细数据
|
// List<OdmTaskTilesVo> odmTaskTilesList = baseMapper.getOdmTaskTilesList(dto);
|
// 计算面积
|
// List<AttachTypeStatisticsVO> attachTypeStatisticsVO = attachClient.calculateTheThreeDimensionalArea("", "", "", String.valueOf(dto.getType()), dto.getAreaCode());
|
// 路径处理
|
// tilesPathHandler(odmTaskTilesList);
|
// 分组处理
|
// Map<String, List<AttachTypeStatisticsVO>> odmTaskTilesMap = Streams.groupBy(attachTypeStatisticsVO, AttachTypeStatisticsVO::getAreaCode);
|
//县下面的所有数量 key=区域code,value =数量
|
// Map<String, Double> map = Streams.toMap(attachTypeStatisticsVO, AttachTypeStatisticsVO::getAreaCode, AttachTypeStatisticsVO::getArea);
|
//县区域下的所有3D-TILES数量
|
|
//根据条件查询所有区域对应的odm统计数据
|
List<OdmTaskStatisticsBO> odmTaskStatisticsBOList = baseMapper.getOdmTaskStatisticsByAreaCode(dto);
|
//获取3d-tiles明细数据
|
List<OdmTaskTilesVo> odmTaskTilesList = baseMapper.getOdmTaskTilesList(dto);
|
// 路径处理
|
// tilesPathHandler(odmTaskTilesList);
|
List<AttachTypeStatisticsVO> attachTypeStatisticsVO = attachClient.calculateTheThreeDimensionalArea("", "", "", String.valueOf(dto.getType()), dto.getAreaCode());
|
// 分组处理
|
Map<String, List<OdmTaskTilesVo>> odmTaskTilesMap = Streams.groupBy(odmTaskTilesList, OdmTaskTilesVo::getAreaCode);
|
//县下面的所有数量 key=区域code,value =数量
|
// Map<String, Integer> map = Streams.toMap(odmTaskStatisticsBOList, OdmTaskStatisticsBO::getDictKey, OdmTaskStatisticsBO::getNum);
|
Map<String, Double> map = Streams.toMap(attachTypeStatisticsVO, AttachTypeStatisticsVO::getAreaCode, AttachTypeStatisticsVO::getArea);
|
//县区域下的所有3D-TILES数量
|
|
TreeVo treeVo = TreeUtils.buildTreeVo(data, map, odmTaskTilesMap);
|
// 返回
|
return treeVo;
|
}
|
|
/**
|
* tiles 路径处理
|
*
|
* @param odmTaskTilesList
|
*/
|
private void tilesPathHandler(List<OdmTaskTilesVo> odmTaskTilesList) {
|
if (odmTaskTilesList.size() > 0) {
|
// 获取运行环境信息
|
String activeProfile = SpringContextUtil.getActiveProfile();
|
// 先拼接数据
|
for (OdmTaskTilesVo odmTaskTilesVo : odmTaskTilesList) {
|
if (!Strings.isBlank(odmTaskTilesVo.getTilesPath())) {
|
// 拼接 url
|
odmTaskTilesVo.setTilesPath(odmProperties.getDomainUrl() + odmTaskTilesVo.getTilesPath());
|
}
|
}
|
}
|
}
|
|
/**
|
* 查询地图位置上3d-tiles数据-单机巢
|
*
|
* @param dto
|
* @return
|
*/
|
@Override
|
public List<OdmTaskTilesVo> getVoxGridTilesListBySinglePlayer(OdmTaskInfoQueryParam dto) {
|
List<OdmTaskTilesVo> odmTaskTilesList = new ArrayList<>();
|
if (Strings.isBlank(dto.getDeviceSn())) {
|
return odmTaskTilesList;
|
}
|
if(null==dto.getType()){
|
// 没传默认给3 白膜
|
dto.setType(AttachResultTypeEnum.ALB.getType());
|
}
|
odmTaskTilesList = baseMapper.getOdmTaskTilesList(dto);
|
// 路径处理
|
// tilesPathHandler(odmTaskTilesList);
|
return odmTaskTilesList;
|
}
|
|
/**
|
* 历史 odm 新增相关字段数据补充
|
*
|
* @return
|
*/
|
@Override
|
public int historyOdmTaskDataHandler() {
|
// 获取运行环境信息
|
String activeProfile = SpringContextUtil.getActiveProfile();
|
// 查询没有设备编号的数据,进行补充
|
// List<OdmTaskInfo> list = baseMapper.getNoDeviceOdmTaskList();
|
List<OdmTaskInfoVO> list = baseMapper.getNoGeomOdmTaskList();
|
for (OdmTaskInfoVO odmTaskInfo : list) {
|
odmTaskInfo.setGeom(getGeom(odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId(), activeProfile));
|
// 更新
|
baseMapper.updateOdmTaskGeomInfoById(odmTaskInfo);
|
}
|
return 0;
|
}
|
|
/**
|
* 测试远程调用资源模块
|
*
|
* @return
|
*/
|
@Override
|
public Boolean testToResource(MultipartFile file) {
|
Attach attach = new Attach();
|
attach.setName("/software/data/odm-data/docker/volumes/webodm_appmedia/_data/project/3/task/a34ad90f-bb45-4c93-b022-597c3a86d60c/assets/odm_orthophoto/odm_orthophoto.tif");
|
Boolean aBoolean = attachClient.saveAttachInfo(attach);
|
try {
|
BladeFile test = attachClient.putFile(file, "test");
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
return aBoolean;
|
}
|
|
/**
|
* 更新三维白膜信息到 附件表
|
*
|
* @return
|
*/
|
@Override
|
public R updateVoxGridTilesToAttach() {
|
int count = 0;
|
// 查询已完成的任务中没有将三维白膜 信息同步到 附件表的信息
|
List<OdmTaskInfoVO> list = baseMapper.getNoSaveTilesAttachOdmTaskInfoList();
|
// 遍历将三维白膜 信息同步到附件表
|
for (OdmTaskInfoVO odmTaskInfo : list) {
|
Boolean flag = update3dTilesAttach(odmTaskInfo, odmTaskInfo.getVoxGridTilesPath());
|
if (flag) {
|
count++;
|
}
|
}
|
return R.status(count == list.size());
|
}
|
|
/**
|
* 3d白膜部分生成失败补充
|
*
|
* @return
|
*/
|
@Override
|
public void tilesSup() {
|
OdmTaskInfoVO odmTaskInfoVO = new OdmTaskInfoVO();
|
odmTaskInfoVO.setType(OdmTypeEnum.ALB.getType());
|
handleTiles(odmTaskInfoVO);
|
}
|
|
/**
|
* 实景三维生成失败补充
|
* @return
|
*/
|
@Override
|
public void liveTilesSup() {
|
OdmTaskInfoVO odmTaskInfoVO = new OdmTaskInfoVO();
|
odmTaskInfoVO.setType(OdmTypeEnum.TILT.getType());
|
handleLiveTiles(odmTaskInfoVO);
|
}
|
|
/**
|
* 3d白膜部分生成失败补充
|
*
|
* @param odmTaskInfo
|
* @return
|
*/
|
@Override
|
public void tilesSupByParam(OdmTaskInfoVO odmTaskInfo) {
|
handleTiles(odmTaskInfo);
|
}
|
|
/**
|
* 补充处理白膜
|
*
|
* @param odmTaskInfoVO
|
*/
|
public void handleTiles(OdmTaskInfoVO odmTaskInfoVO) {
|
// 设置地形文件(处理耗时较长)时配置 redis,防止重复调用
|
String value = bladeRedis.get(RedisKeyConstant.ODM_TIELS_KEY);
|
if (!Strings.isBlank(value)) {
|
return;
|
}
|
// 查询状态已经完成已处理过的,然后没有生成出白膜的任务
|
List<OdmTaskInfoVO> list = baseMapper.getNotCreateTilesTask(odmTaskInfoVO);
|
// 获取运行环境信息
|
String activeProfile = SpringContextUtil.getActiveProfile();
|
// 遍历处理
|
for (OdmTaskInfoVO odmTaskInfo : list) {
|
log.info("开始补充处理三维白膜数据,任务id:{}", odmTaskInfo.getTaskId());
|
// 设置缓存
|
bladeRedis.setEx(RedisKeyConstant.ODM_TIELS_KEY, "1", RedisKeyConstant.DEFAULT_TIME);
|
setPointCloudTo3dTilesPath(odmTaskInfo, activeProfile);
|
log.info("补充处理三维白膜数据结束,任务id:{},处理后白膜路径:{}", odmTaskInfo.getTaskId(), odmTaskInfo.getVoxGridTilesPath());
|
if (!Strings.isBlank(odmTaskInfo.getVoxGridTilesPath())) {
|
boolean flag = false;
|
if (Strings.isBlank(odmTaskInfo.getGeom())) {
|
// 更新
|
flag = updateById(odmTaskInfo);
|
} else {
|
// 手动更新
|
int update = baseMapper.updateOdmTaskGeomInfoById(odmTaskInfo);
|
flag = update > 0 ? true : false;
|
}
|
if (flag) {
|
log.info("开始更新三维白膜数据到附件表,任务id:{}", odmTaskInfo.getTaskId());
|
update3dTilesAttach(odmTaskInfo, odmTaskInfo.getVoxGridTilesPath());
|
}
|
}
|
bladeRedis.del(RedisKeyConstant.ODM_TIELS_KEY);
|
}
|
}
|
|
/**
|
* 补充处理实景三维
|
* @param odmTaskInfoVO
|
*/
|
public void handleLiveTiles(OdmTaskInfoVO odmTaskInfoVO) {
|
// 设置地形文件(处理耗时较长)时配置 redis,防止重复调用
|
String value = bladeRedis.get(RedisKeyConstant.ODM_LIVE_TIELS_KEY);
|
if (!Strings.isBlank(value)) {
|
return;
|
}
|
// 查询状态已经完成已处理过的,然后没有生成出白膜的任务
|
List<OdmTaskInfoVO> list = baseMapper.getNotCreateTilesTask(odmTaskInfoVO);
|
// 获取运行环境信息
|
String activeProfile = SpringContextUtil.getActiveProfile();
|
// 遍历处理
|
for (OdmTaskInfoVO odmTaskInfo : list) {
|
log.info("开始补充处理实景倾斜三维数据,任务id:{}", odmTaskInfo.getTaskId());
|
// 设置缓存
|
bladeRedis.setEx(RedisKeyConstant.ODM_LIVE_TIELS_KEY, "1", RedisKeyConstant.DEFAULT_TIME);
|
// 远程调用服务设置三维倾斜路径
|
setObjTo3dTilesPath(odmTaskInfo, activeProfile);
|
log.info("补充处理实景倾斜三维数据结束,任务id:{},处理后实景倾斜三维路径:{}", odmTaskInfo.getTaskId(), odmTaskInfo.getVoxGridTilesPath());
|
if (!Strings.isBlank(odmTaskInfo.getTilesPath())) {
|
boolean flag = false;
|
if (Strings.isBlank(odmTaskInfo.getGeom())) {
|
// 更新
|
flag = updateById(odmTaskInfo);
|
} else {
|
// 手动更新
|
int update = baseMapper.updateOdmTaskGeomInfoById(odmTaskInfo);
|
flag = update > 0 ? true : false;
|
}
|
if (flag) {
|
log.info("开始更新实景倾斜三维数据到附件表,任务id:{}", odmTaskInfo.getTaskId());
|
update3dTilesAttach(odmTaskInfo, odmTaskInfo.getTilesPath());
|
}
|
}
|
bladeRedis.del(RedisKeyConstant.ODM_LIVE_TIELS_KEY);
|
}
|
}
|
|
@Override
|
public TifParseResponse tifParseNew(TifParseRequest request) {
|
String url = pyToolsServiceProperties.getPyToolsService() + TifConstant.ANALYZE_TIF_ELEVATION;
|
Map<String, Object> map = convertTifParseRequestToMap(request);
|
// 远程调用接口获取结果
|
log.info("开始调用 tif 解析接口,参数:{}", map);
|
String result = pyTifSendRequest.sendPostJsonByMap(url, map);
|
log.info("tif 解析接口返回结果:{}", result);
|
TifParseResponse tifParseResponse = JSON.parseObject(result, TifParseResponse.class);
|
|
return tifParseResponse;
|
}
|
|
// 将 TifParseRequest 对象转换为 Map
|
private Map<String, Object> convertTifParseRequestToMap(TifParseRequest request) {
|
Map<String, Object> map = new HashMap<>();
|
map.put("tif_paths", request.getTif_paths());
|
map.put("start_lat", request.getStart_lat());
|
map.put("start_lon", request.getStart_lon());
|
map.put("end_lat", request.getEnd_lat());
|
map.put("end_lon", request.getEnd_lon());
|
map.put("interval", request.getInterval());
|
return map;
|
}
|
|
@Override
|
public List<PointParam> waylineFlightPathPlanning(List<PointParam> param) {
|
String url = pyToolsServiceProperties.getPyToolsService() + TifConstant.WAYLINE_PLAN_API;
|
List<Map<String, Object>> map = convertPointParamsToFlightPathList(param);
|
// 远程调用接口获取结果
|
log.info("开始调用网格航线接口,参数:{}", map);
|
String result = pyTifSendRequest.planFlightPath(url, map);
|
log.info("网格航线接口返回结果:{}", result);
|
|
// 处理返回的JSON字符串,去除外层的引号并解析转义字符
|
if (result != null && !result.isEmpty()) {
|
// 去除首尾的引号
|
if (result.startsWith("\"") && result.endsWith("\"")) {
|
result = result.substring(1, result.length() - 1);
|
}
|
// 处理转义字符
|
result = result.replace("\\\"", "\"");
|
}
|
List<PointParam> pointParams = new ArrayList<>();
|
|
if (result == null || result.isEmpty()) {
|
return pointParams;
|
}
|
|
try {
|
// 解析JSON数组
|
JSONArray jsonArray = JSONArray.parseArray(result);
|
|
for (int i = 0; i < jsonArray.size(); i++) {
|
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
|
// 创建PointParam对象
|
PointParam pointParam = new PointParam();
|
pointParam.setLongitude(jsonObject.getDouble("longitude"));
|
pointParam.setLatitude(jsonObject.getDouble("latitude"));
|
pointParam.setExecuteHeight(jsonObject.getDouble("height"));
|
pointParam.setIsTarget(jsonObject.getInteger("isTarget"));
|
pointParam.setOriginalIndex(jsonObject.getInteger("originalIndex"));
|
|
// 设置默认值
|
pointParam.setWaypointSpeed(10.0); // 默认速度
|
pointParam.setClickTiles(1); // 默认在模型上
|
pointParam.setHoverTime(0.0); // 默认悬停时间
|
|
pointParams.add(pointParam);
|
}
|
} catch (Exception e) {
|
log.error("解析JSON字符串失败:{}", e.getMessage());
|
e.printStackTrace();
|
}
|
|
return pointParams;
|
}
|
|
|
@Override
|
public void generateGridTask() {
|
log.info("开始生成网格任务");
|
String url = pyToolsServiceProperties.getPyToolsService() + TifConstant.GRID_TASK_API;
|
// 远程调用接口获取结果
|
pyTifSendRequest.sendGetByMap(url);
|
log.info("生成网格任务结束");
|
}
|
|
/**
|
* 保存禁飞区面数据
|
*
|
* @param height 高度
|
* @param geoData geo 数据
|
* @return
|
*/
|
@Override
|
public Map<String, Object> saveMultipolygonInfo(Double height, String geoData) {
|
try {
|
String url = pyToolsServiceProperties.getPyToolsService() + TifConstant.SAVE_MULTIPOLYGON_API;
|
// area1 = "[[115.884897,28.61798],[115.881343,28.613184],[115.887823,28.612999],[115.884897,28.61798],[115.884897,28.61798]]";
|
// area2 = "MULTIPOLYGON(((113.0 23.0,113.0 24.0,114.0 24.0,114.0 23.0,113.0 23.0)))";
|
Map<String, Object> map = new HashMap<>();
|
map.put("height", String.valueOf(height));
|
map.put("multipolygon_wkt", geoData);
|
// 远程调用接口获取结果
|
String responseJson = pyTifSendRequest.sendPostJsonByMap(url, map);
|
String idsIfSuccess = getIdsIfSuccess(responseJson);
|
log.info("保存禁飞区面数据成功,返回的IDs为:{}", idsIfSuccess);
|
if (idsIfSuccess == null || idsIfSuccess.isEmpty()) {
|
log.error("保存禁飞区面数据失败");
|
throw new RuntimeException("保存禁飞区面数据失败");
|
}
|
Map<String, Object> resultMap = new HashMap<>();
|
resultMap.put("ids", idsIfSuccess);
|
return resultMap;
|
} catch (Exception e) {
|
log.error("保存禁飞区面数据失败: {}", e);
|
throw new RuntimeException(e);
|
}
|
}
|
|
/**
|
* 判断操作是否成功,如果成功则返回IDs,否则返回空字符串
|
*
|
* @param responseJson 响应的JSON字符串
|
* @return 成功时返回IDs,失败时返回空字符串
|
*/
|
public String getIdsIfSuccess(String responseJson) {
|
try {
|
JSONObject jsonObject = JSON.parseObject(responseJson);
|
String status = jsonObject.getString("status");
|
|
if ("success".equals(status)) {
|
JSONArray insertedGids = jsonObject.getJSONArray("inserted_gids");
|
if (insertedGids != null && !insertedGids.isEmpty()) {
|
// 将数组转换为逗号分隔的字符串
|
StringBuilder idsBuilder = new StringBuilder();
|
for (int i = 0; i < insertedGids.size(); i++) {
|
if (i > 0) {
|
idsBuilder.append(",");
|
}
|
idsBuilder.append(insertedGids.get(i));
|
}
|
return idsBuilder.toString();
|
}
|
}
|
} catch (Exception e) {
|
log.error("解析JSON响应时出错: {}", e.getMessage());
|
}
|
return "";
|
}
|
|
/**
|
* 将PointParam列表按相邻点分组,例如(1,2)、(2,3)、(3,4)等
|
*
|
* @param request PointParam列表
|
* @return 分组后的列表,每组包含两个相邻的点
|
*/
|
private List<List<PointParam>> groupPointParamsInPairs(List<PointParam> request) {
|
List<List<PointParam>> groupedPoints = new ArrayList<>();
|
|
if (request == null || request.size() < 2) {
|
return groupedPoints;
|
}
|
|
for (int i = 0; i < request.size() - 1; i++) {
|
List<PointParam> pair = new ArrayList<>();
|
pair.add(request.get(i));
|
pair.add(request.get(i + 1));
|
groupedPoints.add(pair);
|
}
|
|
return groupedPoints;
|
}
|
|
/**
|
* 将PointParam列表转换为用于飞行路径规划的Map列表
|
*
|
* @param request PointParam列表
|
* @return 转换后的Map列表
|
*/
|
private List<Map<String, Object>> convertPointParamsToFlightPathList(List<PointParam> request) {
|
List<Map<String, Object>> flightPathList = new ArrayList<>();
|
|
// 将相邻点分组
|
List<List<PointParam>> groupedPoints = groupPointParamsInPairs(request);
|
|
// 转换每组点为Map格式
|
for (List<PointParam> pair : groupedPoints) {
|
if (pair.size() == 2) {
|
PointParam startPoint = pair.get(0);
|
PointParam endPoint = pair.get(1);
|
|
Map<String, Object> segmentMap = new HashMap<>();
|
segmentMap.put("start_latitude", startPoint.getLatitude());
|
segmentMap.put("start_longitude", startPoint.getLongitude());
|
segmentMap.put("start_height", startPoint.getExecuteHeight());
|
segmentMap.put("start_index", startPoint.getOriginalIndex());
|
segmentMap.put("end_latitude", endPoint.getLatitude());
|
segmentMap.put("end_longitude", endPoint.getLongitude());
|
segmentMap.put("end_height", endPoint.getExecuteHeight());
|
segmentMap.put("end_index", endPoint.getOriginalIndex());
|
|
flightPathList.add(segmentMap);
|
}
|
}
|
|
return flightPathList;
|
}
|
|
/**
|
* 获取历史 obj 网格数据
|
*
|
* @return
|
*/
|
@Override
|
public int getHisObjGridData() {
|
// 查询已经生成的obj 网格白马数据
|
List<OdmTaskInfoVO> list = baseMapper.getHistoryOdmTaskListByTifComp();
|
String basePath = "D:/obj_grid/";
|
for (OdmTaskInfoVO odmTaskInfo : list) {
|
// 拼接obj 网格路径
|
String objPath = "https://wrj.shuixiongit.com/aisky-webodm-vol";
|
String objBasePath = "/webodm_appmedia/_data/project/{0}/task/{1}/assets/odm_texturing/odm_textured_model_geo_conv_vox_grid.obj";
|
String posiBasePath = "/webodm_appmedia/_data/project/{0}/task/{1}/assets/odm_georeferencing/odm_georeferencing_model_geo.txt";
|
// 写入 obj 网格
|
String objDownUrl = objPath + MessageFormat.format(objBasePath, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
|
log.info("开始下载 obj 网格数据,任务id:{},下载路径:{}", odmTaskInfo.getTaskId(), objDownUrl);
|
downloadFile(objDownUrl, basePath + odmTaskInfo.getTaskId() + "/" + "odm_grid.obj");
|
// 拼接坐标文件路径
|
String posiDownUrl = objPath + MessageFormat.format(posiBasePath, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
// 下载坐标文件
|
log.info("开始下载 obj 坐标文件,任务id:{},下载路径:{}", odmTaskInfo.getTaskId(), objDownUrl);
|
String posiSavePath = basePath + odmTaskInfo.getTaskId() + "/" + "position.txt";
|
downloadFile(posiDownUrl, posiSavePath);
|
// 调用接口转换坐标
|
setTilesPostion(posiSavePath);
|
}
|
return 0;
|
}
|
|
/**
|
* 下载文件并保存到指定路径
|
*/
|
private boolean downloadFile(String fileUrl, String saveFilePath) {
|
HttpsURLConnection connection = null;
|
InputStream in = null;
|
FileOutputStream out = null;
|
|
try {
|
// 确保目标文件的父目录存在
|
File saveFile = new File(saveFilePath);
|
if (saveFile.exists()) {
|
return true;
|
}
|
File parentDir = saveFile.getParentFile();
|
if (parentDir != null && !parentDir.exists()) {
|
// 递归创建所有不存在的父目录
|
boolean dirsCreated = parentDir.mkdirs();
|
if (!dirsCreated) {
|
System.out.println("无法创建目录: " + parentDir.getAbsolutePath());
|
return false;
|
}
|
}
|
|
URL url = new URL(fileUrl);
|
connection = (HttpsURLConnection) url.openConnection();
|
connection.setRequestMethod("GET");
|
connection.setConnectTimeout(5000);
|
connection.setReadTimeout(10000);
|
|
// 检查响应码
|
if (connection.getResponseCode() != HttpsURLConnection.HTTP_OK) {
|
System.out.println("下载失败,响应码: " + connection.getResponseCode());
|
return false;
|
}
|
|
// 读取输入流并写入文件
|
in = connection.getInputStream();
|
out = new FileOutputStream(saveFilePath);
|
|
byte[] buffer = new byte[1024];
|
int bytesRead;
|
while ((bytesRead = in.read(buffer)) != -1) {
|
out.write(buffer, 0, bytesRead);
|
}
|
|
return true;
|
|
} catch (Exception e) {
|
e.printStackTrace();
|
return false;
|
} finally {
|
// 关闭资源
|
try {
|
if (out != null) out.close();
|
if (in != null) in.close();
|
if (connection != null) connection.disconnect();
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
}
|
|
/**
|
* 调用 api 转换白膜中心点坐标
|
*
|
* @param filePath
|
*/
|
private void setTilesPostion(String filePath) {
|
log.info("开始处理倾斜数据");
|
// obj 转体素化网格 3d-tiles
|
String objPositionConvUrl = pyToolsServiceProperties.getPyToolsService() + TifConstant.OBJ_POSITION_CONV_API;
|
Map<String, Object> map = new HashMap<>(2);
|
map.put("file_path", filePath);
|
// 远程调用接口获取结果
|
String result = pyTifSendRequest.sendPostJsonByMap(objPositionConvUrl, map);
|
log.info("结果:{}", result);
|
}
|
|
/**
|
* 处理历史已飞的点云提取建筑生成白膜
|
*
|
* @return
|
*/
|
@Override
|
public Boolean handleHistoryPointCloudTo3dtiles() {
|
// 判断是否有再处理任务,如果有则不处理,没有才处理,防止cpu,内存使用过多
|
String value = bladeRedis.get(RedisKeyConstant.ODM_POINT_CLOUD_KEY);
|
if (!Strings.isBlank(value)) {
|
log.info("已存在处理点云");
|
return false;
|
}
|
// 设置redis,有效时长 60*2 分钟
|
bladeRedis.setEx(RedisKeyConstant.ODM_POINT_CLOUD_KEY, "1", RedisKeyConstant.ODM_POINT_CLOUD_TIME);
|
// 查询已经生成的obj 网格白膜数据
|
List<OdmTaskInfoVO> list = baseMapper.getHistoryOdmTaskListByTifComp();
|
// 获取运行环境信息
|
String activeProfile = SpringContextUtil.getActiveProfile();
|
// 每次取一个处理
|
OdmTaskInfoVO odmTaskInfo = list.get(0);
|
// 调用接口处理
|
setPointCloudTo3dTilesPath(odmTaskInfo, activeProfile);
|
// 通过路径获取文件
|
String path = odmProperties.getWebodmDataBasePath() + odmTaskInfo.getVoxGridTilesPath();
|
File file = new File(path);
|
// 检查文件是否存在
|
if (!file.exists()) {
|
log.error("三维白膜/倾斜数据文件 {} 不存在!", path);
|
return false;
|
}
|
// 处理完成后更新数据
|
Attach attach = new Attach();
|
// 此处查询的id 为 附件表的id
|
attach.setId(odmTaskInfo.getId());
|
// 计算设置文件大小
|
attach.setAttachSize(file.length());
|
// 设置文件路径,链接等
|
attach.setDomainUrl(odmProperties.getDomainUrl());
|
attach.setLink(odmProperties.getDomainUrl() + odmTaskInfo.getVoxGridTilesPath());
|
attach.setName(path);
|
attach.setOriginalName(path);
|
|
// 设置类型-三维白膜
|
// 测试用,临时用,用后需要手动数据库修改
|
attach.setResultType(null);
|
|
log.info("发起远程调用保存三维白膜/倾斜附件信息,tif 路径:{}", path);
|
Boolean isSaveAttach = attachClient.saveAttachInfo(attach);
|
// 结束后删除redis
|
bladeRedis.del(RedisKeyConstant.ODM_POINT_CLOUD_KEY);
|
return isSaveAttach;
|
}
|
|
/**
|
* 获取历史 点云数据
|
*
|
* @return
|
*/
|
@Override
|
public int getHisPointCloudData() {
|
// 查询已经生成的obj 网格白马数据
|
List<OdmTaskInfoVO> list = baseMapper.getHistoryOdmTaskListByTifComp();
|
String basePath = "E:\\temp\\webodm\\pointcloud\\";
|
for (OdmTaskInfoVO odmTaskInfo : list) {
|
// 拼接点云路径
|
String pointCloudPath = "https://wrj.shuixiongit.com/aisky-webodm-vol";
|
String pointCloudBasePath = "/webodm_appmedia/_data/project/{0}/task/{1}/assets/odm_georeferencing/odm_georeferenced_model.laz";
|
// 写入 obj 网格
|
String pointCloudDownUrl = pointCloudPath + MessageFormat.format(pointCloudBasePath, odmTaskInfo.getProjectId(), odmTaskInfo.getTaskId());
|
|
log.info("开始下载点云laz数据,任务id:{},下载路径:{}", odmTaskInfo.getTaskId(), pointCloudDownUrl);
|
downloadFile(pointCloudDownUrl, basePath + odmTaskInfo.getTaskId() + "/" + "odm_georeferencing_model.laz");
|
}
|
return 0;
|
}
|
|
@Override
|
public Boolean deleteGrid(String ids) {
|
// 检查参数是否为空
|
if (StringUtils.isEmpty(ids)) {
|
log.warn("传入的ids参数为空,无法执行删除操作");
|
return false;
|
}
|
try {
|
String url = getTifServiceBaseUrl() + TifConstant.DELETE_DELETE_GRID_API;
|
String[] split = ids.split(",");
|
Map<String, Object> map = new HashMap<>();
|
map.put("gid_array", split);
|
// 远程调用接口获取结果
|
String responseJson = pyTifSendRequest.sendPostJsonByMap(url, map);
|
// 解析响应并安全获取状态值
|
if (StringUtils.isEmpty(responseJson)) {
|
log.warn("从远程接口接收到空响应");
|
return false;
|
}
|
JSONObject jsonObject = JSON.parseObject(responseJson);
|
Boolean status = jsonObject != null ? jsonObject.getBoolean("status") : null;
|
return status != null ? status : false;
|
} catch (Exception e) {
|
log.error("删除网格数据时发生异常: ", e);
|
return false;
|
}
|
}
|
|
/**
|
* 将机场地形高度初始化参数缓存到Redis队列中
|
*
|
* @param centerLon 经度
|
* @param centerLat 纬度
|
* @param dockSn 机场序列号
|
* @return 缓存结果 1-成功 0-失败
|
*/
|
@Override
|
public Integer cacheAirportTerrainHeightParams(String centerLon, String centerLat, String dockSn) {
|
try {
|
// 检查机场参数是否已缓存
|
if (bladeRedis.exists(RedisKeyConstant.AIRPORT + dockSn)) {
|
return 0;
|
}
|
// 设置机场参数缓存有效期 100 分钟
|
bladeRedis.setEx(RedisKeyConstant.AIRPORT + dockSn, dockSn, 100 * 60L);
|
// 构造缓存key
|
String cacheKey = RedisKeyConstant.AIRPORT_TERRAIN_HEIGHT;
|
// 构造缓存数据
|
Map<String, String> cacheData = new HashMap<>();
|
cacheData.put("center_lon", centerLon);
|
cacheData.put("center_lat", centerLat);
|
cacheData.put("dock_sn", dockSn);
|
// 将数据存储到Redis列表中
|
bladeRedis.lPush(cacheKey, JSON.toJSONString(cacheData));
|
log.info("机场地形高度参数已缓存到Redis队列,key: {}", cacheKey);
|
|
return 1;
|
} catch (Exception e) {
|
log.error("缓存机场地形高度参数到Redis时发生异常: ", e);
|
}
|
return 0;
|
}
|
|
/**
|
* 从Redis队列中获取并移除第一个机场地形高度参数
|
*
|
* @return 机场地形高度参数
|
*/
|
@Override
|
public Map<String, String> getAirportTerrainHeightParams() {
|
try {
|
// 获取队列key
|
String cacheKey = RedisKeyConstant.AIRPORT_TERRAIN_HEIGHT;
|
// 从队列左侧获取并移除第一个元素
|
Object dataObj = bladeRedis.lPop(cacheKey);
|
if (dataObj != null) {
|
log.info("成功获取并移除机场地形高度参数,key: {}", JSON.toJSON(dataObj));
|
// 如果是字符串,则尝试解析为JSON
|
String dataStr = JSON.toJSONString(dataObj);
|
JSONArray array = JSON.parseArray(dataStr);
|
String string = array.get(0).toString();
|
// 移除可能的引号和转义字符
|
Map<String, String> data = JSON.parseObject(string, Map.class);
|
log.info("成功获取并移除机场地形高度参数,key: {}", cacheKey);
|
return data;
|
} else {
|
log.info("机场地形高度参数队列为空,key: {}", cacheKey);
|
}
|
} catch (Exception e) {
|
log.error("获取并移除机场地形高度参数时发生异常: ", e);
|
}
|
return new HashMap<>();
|
}
|
|
|
/**
|
* 初始化机场高程
|
*
|
* @param centerLon
|
* @param centerLat
|
* @param dockSn
|
* @return
|
*/
|
@Override
|
public Boolean initAirportTerrainHeight(String centerLon, String centerLat, String dockSn) {
|
try {
|
String url = getTifServiceBaseUrl() + TifConstant.INIT_AIRPORT_TERRAIN_API;
|
String areaCodePrefix = getDistrictCodeByLocation(Double.parseDouble(centerLon), Double.parseDouble(centerLat));
|
log.info("初始化机场高程,区域前缀:{}", areaCodePrefix);
|
String demInfoPath = baseMapper.getDemInfo(areaCodePrefix);
|
if (demInfoPath == null) {
|
log.warn("未找到匹配的DEM信息,请检查DEM信息表");
|
return false;
|
}
|
Map<String, Object> map = new HashMap<>();
|
map.put("center_lon", centerLon);
|
map.put("center_lat", centerLat);
|
map.put("tif_paths", demInfoPath);
|
map.put("dock_sn", dockSn);
|
// 远程调用接口获取结果
|
String responseJson = pyTifSendRequest.sendPostJsonByMap(url, map);
|
// 解析响应并安全获取状态值
|
if (StringUtils.isEmpty(responseJson)) {
|
log.warn("从远程接口接收到空响应");
|
return false;
|
}
|
JSONObject jsonObject = JSON.parseObject(responseJson);
|
Boolean status = jsonObject != null ? jsonObject.getBoolean("status") : null;
|
return status != null ? status : false;
|
} catch (Exception e) {
|
log.error("初始化机场高程发生异常: ", e);
|
return false;
|
}
|
}
|
|
/**
|
* 获取航点最大高度
|
*
|
* @param mapLatLng
|
* @return
|
*/
|
@Override
|
public Integer getWaylineMaxTerrainHeight(List<MapLatLng> mapLatLng) {
|
try {
|
String url = getTifServiceBaseUrl() + TifConstant.GET_AIRLINE_MAX_HEIGHT_API;
|
Map<String, Object> map = new HashMap<>();
|
map.put("mapLatLng", mapLatLng);
|
// 远程调用接口获取结果
|
String responseJson = pyTifSendRequest.sendPostJsonByMap(url, map);
|
// 解析响应并安全获取状态值
|
if (StringUtils.isEmpty(responseJson)) {
|
log.warn("从远程接口接收到空响应");
|
return 0;
|
}
|
JSONObject jsonObject = JSON.parseObject(responseJson);
|
Boolean status = jsonObject != null ? jsonObject.getBoolean("status") : null;
|
return status != null ? jsonObject.getInteger("max_height") : 0;
|
} catch (Exception e) {
|
log.error("获取航点最大高度发生异常: ", e);
|
return 0;
|
}
|
}
|
|
/**
|
* 删除机场地形数据
|
*
|
* @param dockSn
|
* @return
|
*/
|
@Override
|
public Integer deleteAirportTerrainHeight(String dockSn) {
|
try {
|
String url = getTifServiceBaseUrl() + TifConstant.DELETE_AIRPORT_TERRAIN_API;
|
Map<String, Object> map = new HashMap<>();
|
map.put("dock_sn", dockSn);
|
// 远程调用接口获取结果
|
String responseJson = pyTifSendRequest.sendPostJsonByMap(url, map);
|
// 解析响应并安全获取状态值
|
if (StringUtils.isEmpty(responseJson)) {
|
log.warn("从远程接口接收到空响应");
|
return 0;
|
}
|
JSONObject jsonObject = JSON.parseObject(responseJson);
|
Boolean status = jsonObject != null ? jsonObject.getBoolean("status") : null;
|
return status != null ? jsonObject.getInteger("total_count") : 0;
|
} catch (Exception e) {
|
log.error("删除机场地形数据发生异常: ", e);
|
return 0;
|
}
|
}
|
|
/**
|
* 根据环境获取对应的TIF服务基础URL
|
*
|
* @return
|
*/
|
private String getTifServiceBaseUrl() {
|
String baseUrl = pyToolsServiceProperties.getPyToolsService();
|
return baseUrl;
|
}
|
|
/**
|
* 根据经纬度获取行政区划信息
|
*
|
* @param longitude 经度
|
* @param latitude 纬度
|
* @return 行政区划信息
|
*/
|
public Map<String, String> getDistrictInfoByLocation(double longitude, double latitude) {
|
// 使用高德地图工具类获取行政区划信息
|
String location = longitude + "," + latitude;
|
JSONObject result = AmapUtils.searchByLatLng(location);
|
|
Map<String, String> districtInfo = new HashMap<>();
|
if (result != null && "1".equals(result.getString("status"))) {
|
JSONObject regeocode = result.getJSONObject("regeocode");
|
if (regeocode != null) {
|
JSONObject addressComponent = regeocode.getJSONObject("addressComponent");
|
if (addressComponent != null) {
|
districtInfo.put("province", addressComponent.getString("province"));
|
districtInfo.put("city", addressComponent.getString("city"));
|
districtInfo.put("district", addressComponent.getString("district"));
|
districtInfo.put("adcode", addressComponent.getString("adcode"));
|
districtInfo.put("township", addressComponent.getString("township"));
|
districtInfo.put("formatted_address", regeocode.getString("formatted_address"));
|
}
|
}
|
}
|
return districtInfo;
|
}
|
|
/**
|
* 根据经纬度获取行政区划编码
|
*
|
* @param longitude 经度
|
* @param latitude 纬度
|
* @return 行政区划编码
|
*/
|
public String getDistrictCodeByLocation(double longitude, double latitude) {
|
Map<String, String> districtInfo = getDistrictInfoByLocation(longitude, latitude);
|
String adcode = districtInfo.get("adcode");
|
if (adcode != null && adcode.length() >= 3) {
|
return adcode.substring(0, 2);
|
}
|
return adcode;
|
}
|
|
}
|