AdmRelCalServiceImpl.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. package com.persagy.proxy.adm.service.impl;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.math.MathUtil;
  4. import cn.hutool.core.util.StrUtil;
  5. import com.fasterxml.jackson.databind.JsonNode;
  6. import com.fasterxml.jackson.databind.ObjectMapper;
  7. import com.fasterxml.jackson.databind.node.ArrayNode;
  8. import com.fasterxml.jackson.databind.node.ObjectNode;
  9. import com.persagy.dmp.basic.dto.RequestData;
  10. import com.persagy.dmp.basic.model.QueryCriteria;
  11. import com.persagy.dmp.basic.utils.QueryCriteriaHelper;
  12. import com.persagy.dmp.common.constant.CommonConstant;
  13. import com.persagy.dmp.common.helper.SpringHelper;
  14. import com.persagy.dmp.common.model.response.CommonResult;
  15. import com.persagy.dmp.digital.client.DigitalObjectFacade;
  16. import com.persagy.dmp.digital.client.DigitalRelationCaclFacade;
  17. import com.persagy.dmp.digital.client.DigitalRelationFacade;
  18. import com.persagy.dmp.digital.entity.ObjectDigital;
  19. import com.persagy.dmp.digital.entity.ObjectRelation;
  20. import com.persagy.proxy.adm.constant.AdmObjectInfoConstant;
  21. import com.persagy.proxy.adm.constant.AdmObjectType;
  22. import com.persagy.proxy.adm.constant.AdmRelationTypeEnum;
  23. import com.persagy.proxy.adm.constant.ObjTypeMapping;
  24. import com.persagy.proxy.adm.dto.TwicePointDTO;
  25. import com.persagy.proxy.adm.request.AdmResponse;
  26. import com.persagy.proxy.adm.service.AdmRelCalService;
  27. import com.persagy.proxy.adm.utils.GeoToolsUtil;
  28. import com.persagy.proxy.common.entity.InstanceUrlParam;
  29. import lombok.Data;
  30. import lombok.RequiredArgsConstructor;
  31. import lombok.extern.slf4j.Slf4j;
  32. import org.springframework.stereotype.Service;
  33. import java.util.*;
  34. @Service
  35. @Slf4j
  36. @RequiredArgsConstructor
  37. public class AdmRelCalServiceImpl implements AdmRelCalService {
  38. /***
  39. * Description: 计算关系
  40. * @param context : 请求参数
  41. * @param admRelationTypeEnums : 关系类型集合
  42. //* @param objectTypeStr : 参与计算的业务空间(为空表示所有业务空间类型)
  43. //* @param relationTypeStr : 关系类型
  44. * return : void
  45. * @author : lijie
  46. * date :2021/9/11 20:35
  47. * Update By lijie 2021/9/11 20:35
  48. */
  49. @Override
  50. public void calcRelation(InstanceUrlParam context, List<AdmRelationTypeEnum> admRelationTypeEnums,String relValue) {
  51. for (AdmRelationTypeEnum admRelationTypeEnum : admRelationTypeEnums) {
  52. RequestData requestData = RequestData.builder()
  53. .graphCode(admRelationTypeEnum.getGraphCode())
  54. .relCode(admRelationTypeEnum.getRelCode())
  55. .calBeforeRelFlag(true)
  56. .build();
  57. if (StrUtil.isNotBlank(relValue)){
  58. requestData.setRelValue(relValue);
  59. }
  60. // 调用中台的计算逻辑
  61. DigitalRelationCaclFacade.calculatingObjRelationPrototype(context.getGroupCode(),context.getProjectId(),
  62. context.getAppId(), CommonConstant.DEFAULT_ID, requestData);
  63. }
  64. }
  65. /***
  66. * Description: 计算空间交通关系
  67. * @param context : 通用参数
  68. * @param buildingId : 建筑id
  69. * @param objectType : 对象类型
  70. * @param type : 类型
  71. * @param relationTypeEnum : 关系枚举类
  72. * @return : void
  73. * @author : lijie
  74. * @date :2021/9/30 14:49
  75. * Update By lijie 2021/9/30 14:49
  76. */
  77. @Override
  78. public AdmResponse calcRelationForArchForArchSpToSp(InstanceUrlParam context, String buildingId, String objectType,
  79. String type, AdmRelationTypeEnum relationTypeEnum) {
  80. if (StrUtil.isBlank(buildingId)){
  81. return AdmResponse.success(new ArrayList<>());
  82. }
  83. RequestData requestData = RequestData
  84. .builder()
  85. .buildingId(buildingId)
  86. .objTypes(CollUtil.newHashSet(AdmObjectType.FLOOR.getIndex()))
  87. .build();
  88. // 1.根据建筑id查询建筑下的所有楼层
  89. CommonResult<List<ObjectDigital>> commonResult = DigitalObjectFacade
  90. .queryObjectListSuperiorIdPrototype(context.getGroupCode(), context.getProjectId(),
  91. context.getAppId(), context.getUserId(), requestData);
  92. if (!"success".equals(commonResult.getResult())){
  93. return AdmResponse.failure(commonResult.getMessage());
  94. }
  95. Long count = commonResult.getCount();
  96. List<ObjectDigital> data = commonResult.getData();
  97. handleFloorRelationCalc(data,context,objectType,relationTypeEnum);
  98. if (data.size() == count){
  99. // 不需要分页查询
  100. return AdmResponse.success();
  101. }
  102. // 有结果集未查完
  103. long page = (count % CommonConstant.QUERY_DEFAULT_PAGE_SIZE) == 0
  104. ? (count / CommonConstant.QUERY_DEFAULT_PAGE_SIZE) : (count / CommonConstant.QUERY_DEFAULT_PAGE_SIZE + 1);
  105. for (long i = 1; i < page; i++) {
  106. requestData.setPage(i);
  107. requestData.setSize(CommonConstant.QUERY_DEFAULT_PAGE_SIZE);
  108. List<ObjectDigital> pageFloors = DigitalObjectFacade
  109. .queryObjectListSuperiorId(context.getGroupCode(),
  110. context.getProjectId(), context.getAppId(), context.getUserId(), requestData);
  111. handleFloorRelationCalc(pageFloors,context,objectType,relationTypeEnum);
  112. }
  113. return AdmResponse.success();
  114. }
  115. /***
  116. * Description: 处理楼层下的关系计算(BDTP平台已经限制查询数据量)
  117. * @param data : 楼层数据
  118. * @param context : 请求参数
  119. * @param classCode : 对象类型code
  120. * @param relationTypeEnum: 图类型对象
  121. * @return : void
  122. * @author : lijie
  123. * @date :2021/9/30 16:46
  124. * Update By lijie 2021/9/30 16:46
  125. */
  126. private void handleFloorRelationCalc(List<ObjectDigital> data, InstanceUrlParam context, String classCode,
  127. AdmRelationTypeEnum relationTypeEnum) {
  128. if (CollUtil.isEmpty(data)){
  129. return;
  130. }
  131. ObjectMapper objectMapper = SpringHelper.getBean(ObjectMapper.class);
  132. List<ObjectNode> relations = new ArrayList<>();
  133. // 2.循环所有楼层
  134. for (ObjectDigital floor : data) {
  135. if (!floor.getInfos().has(AdmObjectInfoConstant.MODEL_ID)
  136. || StrUtil.isBlank(floor.getInfos().get(AdmObjectInfoConstant.MODEL_ID).asText())){
  137. continue;
  138. }
  139. // 2.1 根据楼层的modelId查询所有的门
  140. List<TwicePointDTO> pointDTOS =
  141. queryAllDoorByFloorModelId(floor
  142. .getInfos()
  143. .get(AdmObjectInfoConstant.MODEL_ID)
  144. .asText(),context);
  145. if (CollUtil.isEmpty(pointDTOS)){
  146. continue;
  147. }
  148. // 2.2 根据楼层id和classCode查询所有的空间
  149. List<ObjectDigital> spaces = queryAllSpacesByFloorIdAndClassCode(floor.getId(),
  150. classCode,floor.getProjectId(),context);
  151. if (CollUtil.isEmpty(spaces)){
  152. continue;
  153. }
  154. // 2.3 遍历所有的门,得到门拥有的两组x,y坐标
  155. Map<Integer,List<String>> spaceIndexMap = new HashMap<>();
  156. for (int i = 0; i < pointDTOS.size(); i++) {
  157. TwicePointDTO pointDTO = pointDTOS.get(i);
  158. ObjectNode point1 = objectMapper
  159. .createObjectNode()
  160. .put("x", pointDTO.getX1())
  161. .put("y", pointDTO.getY1())
  162. .put("z", 0.00);
  163. ObjectNode point2 = objectMapper
  164. .createObjectNode()
  165. .put("x", pointDTO.getX2())
  166. .put("y", pointDTO.getY2())
  167. .put("z", 0.00);
  168. // 2.4 遍历坐标组,与空间进行对比,得到所有符合的空间id数组
  169. for (ObjectDigital space : spaces) {
  170. if (null==space.getInfos()){
  171. continue;
  172. }
  173. if (!space.getInfos().has("outline")
  174. || !(space.getInfos().get("outline").size()>0)
  175. || !(space.getInfos().get("outline").get(0).size()>0)){
  176. continue;
  177. }
  178. // 空间的第一个数组
  179. JsonNode jsonNode = space.getInfos().get("outline").get(0).get(0);
  180. if (jsonNode==null || !jsonNode.isArray()){
  181. continue;
  182. }
  183. ArrayNode jsonNodes = (ArrayNode) jsonNode;
  184. List<ObjectNode> temp = new ArrayList<>();
  185. jsonNodes.forEach(node->temp.add((ObjectNode) node));
  186. if (GeoToolsUtil.isPointInPoly(point1,temp) || GeoToolsUtil.isPointInPoly(point2,temp)){
  187. List<String> spaceIds = spaceIndexMap.getOrDefault(i, new ArrayList<>());
  188. spaceIds.add(space.getId());
  189. spaceIndexMap.put(i,spaceIds);
  190. }
  191. }
  192. }
  193. // 2.5 创建空间服务关系的关系数据
  194. Set<Map.Entry<Integer, List<String>>> entries = spaceIndexMap.entrySet();
  195. for (Map.Entry<Integer, List<String>> entry : entries) {
  196. List<String> list = entry.getValue();
  197. if(CollUtil.isEmpty(list) || list.size()<2){
  198. continue;
  199. }
  200. String oneSpaceId = list.get(0);
  201. for (int i = 1; i < list.size(); i++) {
  202. String otherSpaceId = list.get(i);
  203. ObjectNode objectNode = objectMapper.createObjectNode()
  204. .put("graphCode", relationTypeEnum.getGraphCode())
  205. .put("relCode", relationTypeEnum.getRelCode())
  206. .put("graphId", StrUtil.format(CommonConstant.DEFAULT_GRAPH_ID_FORMAT, relationTypeEnum.getGraphCode()))
  207. .put("projectId", floor.getProjectId())
  208. .put("objTo", otherSpaceId)
  209. .put("objFrom", oneSpaceId)
  210. .put("valid", 1)
  211. .put("createApp", context.getAppId())
  212. .put("updateApp", context.getAppId())
  213. .put("groupCode", context.getGroupCode())
  214. .put("creator", context.getUserId())
  215. .put("modifier", context.getUserId());
  216. if (StrUtil.isNotBlank(classCode)){
  217. objectNode.put("relValue",classCode);
  218. }
  219. relations.add(objectNode);
  220. }
  221. }
  222. }
  223. if (CollUtil.isNotEmpty(relations)){
  224. DigitalRelationFacade.create(context.getGroupCode(),context.getProjectId(),context.getAppId(),context.getUserId(),relations);
  225. }
  226. }
  227. /***
  228. * Description: 查询楼层下的所有空间
  229. * @param floorId : 楼层id
  230. * @param classCode : 对象类型code
  231. * @param projectId : 项目id
  232. * @param context: 内容上下文
  233. * @return : java.util.List<com.persagy.dmp.digital.entity.ObjectDigital>
  234. * @author : lijie
  235. * @date :2021/10/8 14:47
  236. * Update By lijie 2021/10/8 14:47
  237. */
  238. private List<ObjectDigital> queryAllSpacesByFloorIdAndClassCode(String floorId, String classCode,
  239. String projectId, InstanceUrlParam context) {
  240. RequestData requestData = RequestData
  241. .builder()
  242. .projectId(projectId)
  243. .floorId(floorId)
  244. .objTypes(CollUtil.newHashSet(AdmObjectType.SPACE.getIndex()))
  245. .classCodes(CollUtil.newHashSet(classCode))
  246. .build();
  247. // 1.查询通用结果集
  248. CommonResult<List<ObjectDigital>> commonResult = DigitalObjectFacade
  249. .queryObjectListSuperiorIdPrototype(context.getGroupCode(),
  250. context.getProjectId(), context.getAppId(), context.getUserId(), requestData);
  251. List<ObjectDigital> spaces = commonResult.getData();
  252. if (CollUtil.isEmpty(spaces)){
  253. return new ArrayList<>();
  254. }
  255. Long count = commonResult.getCount();
  256. if (spaces.size() == count){
  257. // 不需要分页查询
  258. return spaces;
  259. }
  260. // 有结果集未查完
  261. long page = (count % CommonConstant.QUERY_DEFAULT_PAGE_SIZE) == 0
  262. ? (count / CommonConstant.QUERY_DEFAULT_PAGE_SIZE) : (count / CommonConstant.QUERY_DEFAULT_PAGE_SIZE + 1);
  263. for (long i = 1; i < page; i++) {
  264. requestData.setPage(i);
  265. requestData.setSize(CommonConstant.QUERY_DEFAULT_PAGE_SIZE);
  266. List<ObjectDigital> pageSpaces = DigitalObjectFacade
  267. .queryObjectListSuperiorId(context.getGroupCode(),
  268. context.getProjectId(), context.getAppId(), context.getUserId(), requestData);
  269. if (CollUtil.isEmpty(pageSpaces)){
  270. continue;
  271. }
  272. spaces.addAll(pageSpaces);
  273. }
  274. return spaces;
  275. }
  276. /***
  277. * Description: 根据楼层模型id查询所有门数据(BDTP限制了查询条数,需要做分页查询)
  278. * @param floorModelId : 楼层模型id
  279. * @param context: 请求参数上下文
  280. * @return : java.util.List<com.persagy.proxy.adm.dto.TwicePointDTO>
  281. * @author : lijie
  282. * @date :2021/10/8 10:02
  283. * Update By lijie 2021/10/8 10:02
  284. */
  285. private List<TwicePointDTO> queryAllDoorByFloorModelId(String floorModelId, InstanceUrlParam context) {
  286. // 门的classCode:CFBEDR,obj_type为equipment
  287. QueryCriteria queryCriteria = new QueryCriteria();
  288. queryCriteria.getCriteria().put("classCode","CFBEDR")
  289. .put("objType",AdmObjectType.EQUIPMENT.getIndex())
  290. .put("modelId",floorModelId);
  291. CommonResult<List<ObjectNode>> commonResult = DigitalObjectFacade
  292. .queryPrototype(context.getGroupCode(), context.getProjectId(), context.getAppId(),
  293. context.getUserId(), queryCriteria);
  294. List<TwicePointDTO> result = new ArrayList<>();
  295. List<ObjectNode> objectNodes = commonResult.getData();
  296. Long count = commonResult.getCount();
  297. analysisDoorResultData(result,objectNodes);
  298. if (objectNodes.size()==count){
  299. // 查询结果集小于BDTP默认分页条数
  300. return result;
  301. }
  302. // 有结果集未查完
  303. long page = (count % CommonConstant.QUERY_DEFAULT_PAGE_SIZE) == 0
  304. ? (count / CommonConstant.QUERY_DEFAULT_PAGE_SIZE) : (count / CommonConstant.QUERY_DEFAULT_PAGE_SIZE + 1);
  305. for (long i = 1; i < page; i++) {
  306. queryCriteria.setPage(i);
  307. queryCriteria.setSize(CommonConstant.QUERY_DEFAULT_PAGE_SIZE);
  308. analysisDoorResultData(result,DigitalObjectFacade
  309. .query(context.getGroupCode(), context.getProjectId(), context.getAppId(),
  310. context.getUserId(), queryCriteria));
  311. }
  312. return result;
  313. }
  314. /***
  315. * Description: 解析门查询结果集
  316. * @param result : 存储结果集
  317. * @param objectNodes : 对象结果集
  318. * @return : void
  319. * @author : lijie
  320. * @date :2021/10/8 14:26
  321. * Update By lijie 2021/10/8 14:26
  322. */
  323. private void analysisDoorResultData(List<TwicePointDTO> result, List<ObjectNode> objectNodes) {
  324. double distance = 100.00;
  325. if (CollUtil.isEmpty(objectNodes)){
  326. return;
  327. }
  328. for (ObjectNode objectNode : objectNodes) {
  329. // 1.获得outline
  330. if (!objectNode.has("outline")
  331. || !objectNode.get("outline").isArray()
  332. || !(objectNode.get("outline").size()>2)){
  333. continue;
  334. }
  335. if (!objectNode.has("location")
  336. || !objectNode.get("location").isObject()
  337. || !objectNode.get("location").has("points")
  338. || !objectNode.get("location").get("points").isArray()
  339. || !(objectNode.get("location").get("points").size()>=1)){
  340. continue;
  341. }
  342. JsonNode point = objectNode.get("location").get("points").get(0);
  343. ArrayNode points = (ArrayNode) objectNode.get("outline").get(0);
  344. JsonNode firstPoint = points.get(0);
  345. JsonNode secondPoint = points.get(1);
  346. Double k=getPoints(firstPoint.get("x").asDouble(),firstPoint.get("y").asDouble(),
  347. secondPoint.get("x").asDouble(),secondPoint.get("y").asDouble());
  348. if (null==k){
  349. result.add(TwicePointDTO
  350. .builder()
  351. .x1(point.get("x").asDouble())
  352. .y1(point.get("y").asDouble()+distance)
  353. .x2(point.get("x").asDouble())
  354. .y2(point.get("y").asDouble()-distance)
  355. .build());
  356. continue;
  357. }
  358. result.add(getPointByDistance(point.get("x").asDouble(),point.get("y").asDouble(),k,distance*2));
  359. }
  360. }
  361. /***
  362. * Description: 计算两点之间的距离(我不太明白这个算法是怎么算的)
  363. * @param baseX : 坐标点1的x值
  364. * @param baseY : 坐标点1的y值
  365. * @param k : 坐标点2的x值
  366. * @param distance : 坐标点2的y值
  367. * @return : java.lang.Double
  368. * @author : lijie
  369. * @date :2021/10/8 11:31
  370. * Update By lijie 2021/10/8 11:31
  371. */
  372. private TwicePointDTO getPointByDistance(Double baseX,Double baseY,Double k,Double distance){
  373. if (null==k){
  374. return TwicePointDTO.builder().x1(baseX).y1(baseY+distance).x2(baseX).y2(baseY-distance).build();
  375. }
  376. double vectorX1 = Math.sqrt(distance * distance / (1 + k * k));
  377. double vectorX2 = -vectorX1;
  378. double vectorY1 = k*vectorX1;
  379. double vectorY2 = k*vectorX2;
  380. return TwicePointDTO.builder().x1(baseX+vectorX1).y1(baseY+vectorY1).x2(baseX+vectorX2).y2(baseY+vectorY2).build();
  381. }
  382. /***
  383. * Description: 计算两点之间的距离(我不太明白这个算法是怎么算的)
  384. * @param x1 : 坐标点1的x值
  385. * @param y1 : 坐标点1的y值
  386. * @param x2 : 坐标点2的x值
  387. * @param y2 : 坐标点2的y值
  388. * @return : java.lang.Double
  389. * @author : lijie
  390. * @date :2021/10/8 11:31
  391. * Update By lijie 2021/10/8 11:31
  392. */
  393. private Double getPoints(Double x1,Double y1,Double x2,Double y2){
  394. if (x1.equals(x2) && y1.equals(y2)){
  395. return null;
  396. }
  397. Double k=null;
  398. if (!x1.equals(x2)){
  399. k=(y2-y1)/(x2-x1);
  400. }
  401. if (null==k){
  402. return 0.00;
  403. }
  404. if (0.00 == k){
  405. return null;
  406. }
  407. return 1/k;
  408. }
  409. }