/*
Copyright © 2025, TP-Link Global Inc. All rights reserved.
*/
package com.tplink.cdd.vms.ai.manager.core.domain.peoplecounting.trajectory;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import com.tplink.cdd.vms.ai.manager.core.domain.faulttrackingpoint.annotation.TrackingPointEnable;
import com.tplink.cdd.vms.ai.manager.core.domain.faulttrackingpoint.aspect.PeopleCountingTrackingPointCount;
import com.tplink.cdd.vms.ai.manager.core.domain.peoplecounting.trajectory.tool.TrajectorRecognitionTool;
import org.apache.skywalking.apm.toolkit.trace.Trace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import com.tplink.cdd.vms.ai.manager.core.domain.AiModule;
import com.tplink.cdd.vms.ai.manager.core.domain.common.dto.Line;
import com.tplink.cdd.vms.ai.manager.core.domain.common.dto.Point;
import com.tplink.cdd.vms.ai.manager.core.domain.common.utils.GeometryUtils;
import com.tplink.cdd.vms.ai.manager.core.domain.device.DeviceInfoContextCacheService;
import com.tplink.cdd.vms.ai.manager.core.domain.passengerstatistics.dto.DeviceConfigDTO;
import com.tplink.cdd.vms.ai.manager.core.domain.passengerstatistics.dto.IntersectionLineDTO;
import com.tplink.cdd.vms.ai.manager.core.domain.passengerstatistics.enums.CrossAreaWayEnum;
import com.tplink.cdd.vms.ai.manager.core.domain.peoplecounting.log.DynamicObjectLogger;
import com.tplink.cdd.vms.ai.manager.core.domain.peoplecounting.trajectory.entity.DeviceEntity;
import com.tplink.cdd.vms.ai.manager.core.domain.peoplecounting.trajectory.entity.DevicePointEntity;
import com.tplink.cdd.vms.ai.manager.core.domain.peoplecounting.trajectory.entity.HistoricalEntity;
import com.tplink.cdd.vms.ai.manager.core.domain.peoplecounting.trajectory.entity.ResultEntity;
import com.tplink.cdd.vms.ai.manager.core.domain.smartdata.SmartDataFrame;
import com.tplink.cdd.vms.ai.manager.core.domain.smartdata.processor.AbstractSmartDataHandler;
import com.tplink.cdd.vms.ai.manager.core.domain.smartdata.processor.AiSmartDataProcessor;
import com.tplink.cdd.vms.ai.manager.core.domain.threadpool.AiCustomThreadPool;
import com.tplink.cdd.vms.ai.manager.core.infrastructure.cache.PeopleCountingCache;
import com.tplink.cdd.vms.ai.manager.core.infrastructure.cache.PeopleCountingResultCache;
import com.tplink.cdd.vms.ai.manager.core.infrastructure.cache.TrajectoryCache;
import com.tplink.cdd.vms.ai.manager.core.infrastructure.cache.entity.PeopleCountingConfigEntity;
import com.tplink.cdd.vms.ai.manager.core.port.config.AiSwitchConfig;
import com.tplink.cdd.vms.ai.manager.core.port.dto.DeviceInfoContext;
import com.tplink.cdd.vms.common.dto.OperationResponse;
import com.tplink.cdd.vms.common.util.CollectionUtil;
import com.tplink.cdd.vms.common.util.CustomPIIMaskUtil;
import com.tplink.cdd.vms.common.util.JsonUtils;
/**
Description of this file
@author hejun
@version 1.0
@since 2025/8/11
*/
public abstract class AbstractPeopleCountingHandler implements AbstractSmartDataHandler {
@Lazy
@Autowired
protected PeopleCountingCache peopleCountingCache;
@Lazy
@Autowired
protected PeopleCountingResultCache peopleCountingResultCache;
@Autowired
private AiSwitchConfig aiSwitchConfig;
@Autowired
private TrajectoryCache trajectoryCache;
@Autowired
private DeviceInfoContextCacheService deviceInfoContextCacheService;
@Autowired
private PeopleCountingTrackingPointCount peopleCountingTrackingPointCount;
@Value(“${peopleCounting.trajectory.algorithm:multi_segments}”)
private String trajectoryAlgorithm = “multi_segments”;
@Value(“${peopleCounting.trajectory.sufficientDistanceTimes:2}”)
private int sufficientDistanceTimes = 2;
protected static final Set NEED_LOG_DEVICE_IDS = new HashSet<>();
private static final DynamicObjectLogger log = new DynamicObjectLogger(
AbstractPeopleCountingHandler.class) {
@Override
public boolean debugEnabled(Long deviceId) {
// todo
return true;
// return NEED_LOG_DEVICE_IDS.contains(deviceId);
}
};
protected abstract AiCustomThreadPool getThreadPool();
public abstract Collection getRealDeviceConfigs(
T deviceEntity);
protected abstract Boolean isBusinessTime(PeopleCountingConfigEntity deviceConfig);
protected abstract Boolean isBusinessTime(HistoricalEntity history);
public static void updateNeedLogDeviceIds(Collection deviceIds) {
NEED_LOG_DEVICE_IDS.clear();
if (CollectionUtil.isNotEmpty(deviceIds)) {
NEED_LOG_DEVICE_IDS.addAll(deviceIds);
}
}
@PostConstruct
public void init() {
AiModule module = getModule();
log.info(1L, “peoples counting handler init, module {}, enabled {}.”,
module, aiSwitchConfig.isAiModuleEnabled(module));
if (aiSwitchConfig.isAiModuleEnabled(module)) {
AiSmartDataProcessor.registerHandler(module, this);
}
}
@Trace
@Override
public void handleSmartData(DeviceInfoContext deviceInfo, SmartDataFrame frame) {
Integer threadPoolIndex = deviceInfo.getPassengerFlowStatisticsThreadPoolIndex();
Long deviceId = deviceInfo.getDeviceId();
if (Objects.isNull(threadPoolIndex)) {
int execute = getThreadPool().execute(deviceInfo.getKey(),
() -> doHandleSmartData(deviceInfo, frame));
log.info(deviceId, “ai thread pool index [passenger flow stat] {}, device id {}, dev id {}, channel {}.”,
execute,
deviceId, CustomPIIMaskUtil.encrypt(deviceInfo.getDevId()), deviceInfo.getChannel());
deviceInfo.setPassengerFlowStatisticsThreadPoolIndex(execute);
} else {
getThreadPool().execute(threadPoolIndex,
() -> doHandleSmartData(deviceInfo, frame));
}
}
@Trace
private void doHandleSmartData(DeviceInfoContext deviceInfo, SmartDataFrame frame) {
log.info(123L, “frame is {}”, frame);
if (!verified(deviceInfo, frame)) {
log.debug(deviceInfo.getDeviceId(), “invalid data, DeviceInfoContext: {}, SmartDataFrame: {}.”, deviceInfo,
frame);
return;
}
Long deviceId = deviceInfo.getDeviceId();
DeviceEntity deviceEntity = DeviceEntity.getDeviceEntity(deviceInfo);
log.debug(deviceId, "deviceEntity is {}", deviceEntity); Collection<PeopleCountingConfigEntity> deviceConfigs = getDeviceConfigs(deviceEntity); if (CollectionUtil.isEmpty(deviceConfigs)) { log.debug(deviceId, "device config is empty"); return; } log.info(deviceId, "deviceConfigs is {}", deviceConfigs); List<DevicePointEntity> points = frame.getObjs().stream().map(DevicePointEntity::getDevicePointEntity) .filter(Objects::nonNull) .collect(Collectors.toList()); if (CollectionUtil.isEmpty(points)) { log.debug(deviceId, "points is empty, frame: {}.", frame); return; } Collection<ResultEntity> results = new HashSet<>(); long timeStamp = System.currentTimeMillis() / TimeUnit.MINUTES.toMillis(1); for (PeopleCountingConfigEntity deviceConfig : deviceConfigs) { // todo 并发处理 ResultEntity resultEntity = trajectoryRecognition(deviceEntity, deviceConfig, points); log.debug(deviceId, "resultEntity is {}.", resultEntity); if (Objects.nonNull(resultEntity)) { resultEntity.setTimeMinute(timeStamp); results.add(resultEntity); } } if (CollectionUtil.isEmpty(results)) { log.debug(deviceId, "results is empty, frame: {}.", frame); return; } peopleCountingResultCache.offers(results);
}
public Optional handleDisappearsData(HistoricalEntity history) {
Long deviceId = history.getDeviceId();
CrossAreaWayEnum crossAreaWay = history.resetInside(sufficientDistanceTimes);
if (Objects.equals(crossAreaWay, CrossAreaWayEnum.USELESS)) {
return Optional.empty();
}
ResultEntity resultEntity = history.generateResultEntity();
resultEntity.setBusinessTime(isBusinessTime(history));
switch (crossAreaWay) {
case PASS_BY_STORE:
resultEntity.setPassBy(1);
return Optional.of(resultEntity);
case ENTER_STORE:
resultEntity.setEnter(1);
return Optional.of(resultEntity);
case LEAVE_STORE:
resultEntity.setCheckOut(1);
return Optional.of(resultEntity);
default:
log.debug(deviceId, “useless cross area way: {}”, crossAreaWay);
}
return Optional.empty();
}
public Optional handleDisappearsData(String key, Line start, Line end) {
Optional resultEntity = PeopleCountingCache.parseKey(key);
if (!resultEntity.isPresent()) {
return Optional.empty();
}
ResultEntity result = resultEntity.get();
Long deviceId = result.getDeviceId();
int passBy = 0;
int enter = 0;
int checkOut = 0;
try { CrossAreaWayEnum crossAreaWay = identifyDisappearsTrajectory(key, start, end); log.debug(deviceId, "cross area way: {}, key: {}, start: {}, end: {}.", crossAreaWay, key, start, end); switch (crossAreaWay) { case PASS_BY_STORE: passBy++; break; case ENTER_STORE: enter++; break; case LEAVE_STORE: checkOut++; break; default: log.debug(deviceId, "useless cross area way: {}", crossAreaWay); } } catch (Exception e) { log.debug(deviceId, "trajectory recognition error, key: {}, start: {}, end: {}.", key, start, end, e); } if (passBy == 0 && enter == 0 && checkOut == 0) { return Optional.empty(); } OperationResponse<DeviceInfoContext> deviceInfoContextByDeviceId = deviceInfoContextCacheService.getDeviceInfoContextByDeviceId( deviceId); if (deviceInfoContextByDeviceId.isError() || Objects.isNull(deviceInfoContextByDeviceId.getResult())) { log.debug(deviceId, "device info context is null, device id: {}.", deviceId); return Optional.empty(); } AiModule module = getModule(); result.setModule(module); result.setDeviceEntity(DeviceEntity.getDeviceEntity(deviceInfoContextByDeviceId.getResult())); Optional<PeopleCountingConfigEntity> deviceConfigOptional = peopleCountingCache.getConfigOptional(module, deviceId, result.getConfigId()); if (!deviceConfigOptional.isPresent()) { return Optional.empty(); } PeopleCountingConfigEntity deviceConfig = deviceConfigOptional.get(); result.setConfigId(deviceConfig.getConfigId()); result.setBusinessTime(isBusinessTime(deviceConfig)); result.setPassBy(passBy); result.setEnter(enter); result.setCheckOut(checkOut); result.setTimeMinute(System.currentTimeMillis() / TimeUnit.MINUTES.toMillis(1)); log.debug(deviceId, "trajectory recognition result: {}.", result); return Optional.of(result);
}
public CrossAreaWayEnum identifyDisappearsTrajectory(String key, Line start, Line end) {
HistoricalEntity historical = peopleCountingCache.getHistorical(getModule(), key);
if (Objects.isNull(historical)) {
return CrossAreaWayEnum.USELESS;
}
List polygon = historical.getCurrentConfig().getPolygon();
Point outside = polygon.get(polygon.size() - 2);
if (Boolean.TRUE.equals(historical.getCurrentConfig().getEnterFromA2B())) {
outside = polygon.get(0);
}
Point enter;
IntersectionLineDTO enterIntersectionLine = historical.getEnterIntersectionLine();
if (Objects.isNull(enterIntersectionLine)) {
enterIntersectionLine = GeometryUtils.findRayIntersection(historical.getCurrentConfig().getPolygon(),
start.reverse());
}
enter = enterIntersectionLine.getIntersection();
IntersectionLineDTO leaveIntersectionLine = historical.getLeaveIntersectionLine(); if (Objects.isNull(leaveIntersectionLine)) { leaveIntersectionLine = GeometryUtils.findRayIntersection( historical.getCurrentConfig().getPolygon(), end); } Point leave = leaveIntersectionLine.getIntersection(); Line cuttingLine = historical.getCurrentConfig().getCuttingLine(); CrossAreaWayEnum crossAreaWay = getCrossAreaWay(enter, leave, cuttingLine, outside); log.debug(12L, "[PassengerFlowStatistics] getCrossAreaWay enter = {}, leave = {}, cuttingLine = {}, outside = {}.", enter, leave, cuttingLine, outside); reset(historical); return crossAreaWay;
}
private boolean verified(DeviceInfoContext deviceInfo, SmartDataFrame frame) {
if (Objects.isNull(deviceInfo) || Objects.isNull(frame)) {
return false;
}
if (Objects.isNull(deviceInfo.getDeviceId())) {
return false;
}
if (CollectionUtil.isEmpty(frame.getObjs())) {
return false;
}
return true;
}
private Collection getDeviceConfigs(DeviceEntity deviceEntity) {
return peopleCountingCache.getDeviceConfigs(deviceEntity, getModule());
}
@Trace
private ResultEntity trajectoryRecognition(DeviceEntity deviceEntity, PeopleCountingConfigEntity deviceConfig,
List points) {
Long deviceId = deviceConfig.getDeviceId();
int passBy = 0;
int enter = 0;
int checkOut = 0;
for (DevicePointEntity point : points) {
HistoricalEntity historical = obtainHistorical(deviceEntity, deviceConfig, point.getObjectId());
log.debug(deviceId, “historical is {}”, historical);
if (Objects.isNull(historical)) {
continue;
}
try {
CrossAreaWayEnum crossAreaWay = identifyTrajectory(historical, point);
log.debug(deviceId, “cross area way: {}, historical: {}, point: {}.”, crossAreaWay, historical, point);
switch (crossAreaWay) {
case PASS_BY_STORE:
passBy++;
break;
case ENTER_STORE:
enter++;
break;
case LEAVE_STORE:
checkOut++;
break;
default:
log.debug(deviceId, “useless cross area way: {}”, crossAreaWay);
}
} catch (Exception e) {
log.debug(deviceId, “trajectory recognition error, historical: {}, point: {}.”, historical, point, e);
}
}
if (passBy == 0 && enter == 0 && checkOut == 0) {
return null;
}
ResultEntity result = new ResultEntity();
result.setModule(getModule());
result.setDeviceEntity(deviceEntity);
result.setConfigId(deviceConfig.getConfigId());
result.setBusinessTime(isBusinessTime(deviceConfig));
result.setPassBy(passBy);
result.setEnter(enter);
result.setCheckOut(checkOut);
log.debug(deviceId, “trajectory recognition result: {}.”, result);
return result;
}
private HistoricalEntity obtainHistorical(DeviceEntity deviceEntity, PeopleCountingConfigEntity deviceConfig,
Integer objectId) {
return peopleCountingCache.getHistorical(deviceEntity, deviceConfig.getConfigId(), objectId,
getModule());
}
private CrossAreaWayEnum identifyTrajectory(HistoricalEntity historical, DevicePointEntity point) {
Long deviceId = historical.getDeviceId();
log.debug(deviceId,
“[PassengerFlowStatistics] Start processing passenger flow statistics,HistoricalEntity = {}, current = {}.”,
JsonUtils.bean2Json(historical), JsonUtils.bean2Json(point));
Point current = point.getPoint(); if (Objects.isNull(current)) { peopleCountingTrackingPointCount.getPeopleCountingTrackingPointData(historical, point, CrossAreaWayEnum.USELESS); return CrossAreaWayEnum.USELESS; } historical.setPrevious(historical.getCurrent()); historical.setPreviousInside(historical.getCurrentInside()); historical.setCurrent(current); historical.setCurrentInside(getPosition(historical.getDeviceConfigs(), current)); CrossAreaWayEnum crossAreaWayEnum = CrossAreaWayEnum.USELESS; log.debug(deviceId, "[PassengerFlowStatistics] The current node is located at {}", historical.getCurrentInside()); if (Objects.nonNull(historical.getCurrentInside())) { DeviceConfigDTO currentDeviceConfigDTO = historical.getDeviceConfigs().get(historical.getCurrentInside()); if (point.getConfidence() + currentDeviceConfigDTO.getSensitivity() < 100) { log.debug(deviceId, "[PassengerFlowStatistics] The node confidence does not meet the requirements, confidence = {}, Sensitivity = historical.currentDeviceConfigDTO.getSensitivity()", historical.getCurrentInside()); // 越不灵敏越不能接受置信度小的数据,如此灵敏度和置信度都很小的数据,丢弃 historical.setCurrent(historical.getPrevious()); historical.setCurrentInside(historical.getPreviousInside()); peopleCountingTrackingPointCount.getPeopleCountingTrackingPointData(historical, point, CrossAreaWayEnum.USELESS); return CrossAreaWayEnum.USELESS; } processTrajectoryCache(historical, point.getObjectId(), current, point.getBoxHeight()); historical.setCurrentConfig(currentDeviceConfigDTO); if (Objects.isNull(historical.getEnterIntersectionLine()) && ( Objects.equals(historical.getCurrentInside(), historical.getPreviousInside()) || Objects.isNull(historical.getPrevious()))) { log.debug(deviceId, "[PassengerFlowStatistics] There is no trajectory entering the area."); //判断是否在边界上,在边界上即为进入线 IntersectionLineDTO enterIntersectionLine = GeometryUtils.findIntersection( currentDeviceConfigDTO.getPolygon(), current); log.debug(deviceId, "[PassengerFlowStatistics] The result of judging whether current node is on the boundary is {}", enterIntersectionLine); if (Objects.isNull(enterIntersectionLine)) { // 没有进入边界的点,凭空出现在了区域内 peopleCountingTrackingPointCount.getPeopleCountingTrackingPointData(historical, point, CrossAreaWayEnum.USELESS); return CrossAreaWayEnum.USELESS; } historical.setEnterIntersectionLine(enterIntersectionLine); } // 位于区域内,那离开的位置就得重新计算 historical.setLeaveIntersectionLine(null); historical.setCompleteLeave(false); } else { // 位于区域外,但是还没完全进入,那就得重新计算进入位置 if (Boolean.FALSE.equals(historical.getCompleteEnter())) { historical.setEnterIntersectionLine(null); historical.setCompleteEnter(false); } crossAreaWayEnum = historical.resetInside(sufficientDistanceTimes); } if (Objects.nonNull(historical.getCurrentInside()) && Objects.isNull(historical.getPreviousInside())) { // 进入区域 if (Objects.isNull(historical.getPrevious())) { peopleCountingTrackingPointCount.getPeopleCountingTrackingPointData(historical, point, CrossAreaWayEnum.USELESS); return CrossAreaWayEnum.USELESS; } historical.setEnterIntersectionLine(GeometryUtils.findIntersection( historical.getCurrentConfig().getPolygon(), new Line(current, historical.getPrevious()))); log.debug(deviceId, "[PassengerFlowStatistics] The target enters the area from {}", historical.getEnterIntersectionLine()); } else if (Objects.isNull(historical.getCurrentInside()) && Objects.nonNull(historical.getPreviousInside())) { if (Objects.isNull(historical.getPrevious())) { peopleCountingTrackingPointCount.getPeopleCountingTrackingPointData(historical, point, CrossAreaWayEnum.USELESS); return CrossAreaWayEnum.USELESS; } historical.setLeaveIntersectionLine(GeometryUtils.findIntersection( historical.getCurrentConfig().getPolygon(), new Line(current, historical.getPrevious()))); log.debug(deviceId, "[PassengerFlowStatistics] The target leaves the area from {}", historical.getLeaveIntersectionLine()); } if (Objects.nonNull(historical.getEnterIntersectionLine()) && !Boolean.TRUE.equals( historical.getCompleteEnter())) { historical.setCompleteEnter( GeometryUtils.distanceToLineSegment(historical.getEnterIntersectionLine().getLine(), current) > point.getHalfWidth()); } if (Objects.nonNull(historical.getLeaveIntersectionLine()) && !Boolean.TRUE.equals( historical.getCompleteLeave())) { historical.setCompleteLeave( GeometryUtils.distanceToLineSegment(historical.getLeaveIntersectionLine().getLine(), current) > point.getHalfWidth()); } log.debug(deviceId, "[PassengerFlowStatistics] completeEnter = {}, completeLeave = {}", historical.getCompleteEnter(), historical.getCompleteLeave()); if (Objects.nonNull(historical.getLeaveIntersectionLine()) && Boolean.TRUE.equals( historical.getCompleteLeave())) { if (Objects.isNull(historical.getEnterIntersectionLine())) { if (Objects.equals(trajectoryAlgorithm, "multi_segments")) { // 未知区域进入,从轨迹方向进行确认 List<Line> segment = trajectoryCache.getSegment(getModule(), deviceId, historical.getConfigId(), point.getObjectId()); if (CollectionUtil.isNotEmpty(segment)) { historical.setEnterIntersectionLine( GeometryUtils.findRayIntersection(historical.getCurrentConfig().getPolygon(), segment.get(0).reverse())); } } else { log.debug(deviceId, "[PassengerFlowStatistics] getCrossAreaWay crossAreaWayEnum = {}.", crossAreaWayEnum); reset(historical, point); peopleCountingTrackingPointCount.getPeopleCountingTrackingPointData(historical, point, crossAreaWayEnum); return crossAreaWayEnum; } } if (Objects.nonNull(historical.getEnterIntersectionLine())) { // 完成了离开 List<Point> polygon = historical.getCurrentConfig().getPolygon(); Point outside = polygon.get(polygon.size() - 2); if (Boolean.TRUE.equals(historical.getCurrentConfig().getEnterFromA2B())) { outside = polygon.get(0); } Point enter = historical.getEnterIntersectionLine().getIntersection(); Point leave = historical.getLeaveIntersectionLine().getIntersection(); Line cuttingLine = historical.getCurrentConfig().getCuttingLine(); CrossAreaWayEnum crossAreaWay = getCrossAreaWay(enter, leave, cuttingLine, outside); log.debug(deviceId, "[PassengerFlowStatistics] getCrossAreaWay enter = {}, leave = {}, cuttingLine = {}, outside = {}.", enter, leave, cuttingLine, outside); reset(historical, point); peopleCountingTrackingPointCount.getPeopleCountingTrackingPointData(historical, point, crossAreaWay); return crossAreaWay; } } peopleCountingTrackingPointCount.getPeopleCountingTrackingPointData(historical, point, CrossAreaWayEnum.USELESS); return CrossAreaWayEnum.USELESS;
}
private void processTrajectoryCache(HistoricalEntity historical, Integer objectId, Point current,
Integer boxHeight) {
Long deviceId = historical.getDeviceId();
String configId = historical.getConfigId();
try {
if (Objects.equals(trajectoryAlgorithm, “multi_segments”)) {
trajectoryCache.addPoint(getModule(), deviceId, configId, objectId, current, boxHeight);
} else {
historical.updateInside(current, boxHeight);
}
} catch (Exception e) {
log.error(deviceId,
“trajectory cache add point error, ai module = {}, deviceId = {}, configId = {}, objectId = {}, current = {}.”,
getModule(), deviceId, configId, objectId, current, e);
}
}
public boolean reset(HistoricalEntity historical, DevicePointEntity point) {
trajectoryCache.reset(getModule(), historical.getDeviceId(), historical.getConfigId(), point.getObjectId());
return reset(historical);
}
public boolean reset(HistoricalEntity historical) {
historical.setPrevious(null);
historical.setPreviousInside(null);
historical.setEnterIntersectionLine(null);
historical.setCompleteEnter(null);
historical.setLeaveIntersectionLine(null);
historical.setCompleteLeave(null);
return true;
}
public static Integer getPosition(
Map<Integer, DeviceConfigDTO> deviceConfigs,
Point point) {
if (CollectionUtil.isEmpty(deviceConfigs) || point == null) {
return null;
}
for (DeviceConfigDTO deviceConfig : deviceConfigs.values()) {
if (GeometryUtils.isPointInPolygon(deviceConfig.getPolygon(), point)) {
// 区域不重复,如果处于一个区域内,那必然不在另一个区域内
return deviceConfig.getId();
}
}
return null;
}
public static CrossAreaWayEnum getCrossAreaWay(Point enter, Point leave, Line cuttingLine, Point outside) {
boolean fromOutside = GeometryUtils.arePointsOnSameSide(cuttingLine, enter, outside);
boolean toOutside = GeometryUtils.arePointsOnSameSide(cuttingLine, leave, outside);
if (fromOutside) { if (toOutside) { return CrossAreaWayEnum.PASS_BY_STORE; } return CrossAreaWayEnum.ENTER_STORE; } if (toOutside) { return CrossAreaWayEnum.LEAVE_STORE; } return CrossAreaWayEnum.USELESS;
}
}
这是我的一个类,我想要对identifyTrajectory方法使用springaop,一个怎么修改