Commit d51c2793 authored by xululu@wanji.net.cn's avatar xululu@wanji.net.cn

交通感知指标计算:拥堵、溢出、失衡、死锁和相位空放

parent dc2df39a
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
<!--<jar.tail>export</jar.tail>--> <!--<jar.tail>export</jar.tail>-->
<!--<jar.tail>export</jar.tail>--> <!--<jar.tail>export</jar.tail>-->
<!--<jar.tail>monitor</jar.tail>--> <!--<jar.tail>monitor</jar.tail>-->
<jar.tail>area</jar.tail> <jar.tail>test</jar.tail>
<!--<jar.tail>event</jar.tail>--> <!--<jar.tail>event</jar.tail>-->
<!--<jar.tail>period</jar.tail>--> <!--<jar.tail>period</jar.tail>-->
</properties> </properties>
...@@ -176,7 +176,12 @@ ...@@ -176,7 +176,12 @@
<version>0.9.0</version> <version>0.9.0</version>
</dependency>--> </dependency>-->
<!-- https://mvnrepository.com/artifact/org.apache.flink/flink-connector-jdbc -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-jdbc_2.11</artifactId>
<version>1.14.3</version>
</dependency>
<dependency> <dependency>
......
...@@ -44,6 +44,9 @@ public class CarTrackModel implements Serializable { ...@@ -44,6 +44,9 @@ public class CarTrackModel implements Serializable {
/*航向角*/ /*航向角*/
private Double courseAngle; private Double courseAngle;
//是否位于指定区域内 默认0:不在指定区域内 1:位于指定区域内
private int isInSpecificArea = 0;
/*******************************************/ /*******************************************/
@Data @Data
...@@ -60,6 +63,7 @@ public class CarTrackModel implements Serializable { ...@@ -60,6 +63,7 @@ public class CarTrackModel implements Serializable {
/*是否在路口范围内*/ /*是否在路口范围内*/
private int inCrossFlag; private int inCrossFlag;
/*车道功能转向*/ /*车道功能转向*/
//车道转向:1左转;2直行;3右转;4掉头;5直左;6直右;7左直右;8左右;9左转掉头;10直行掉头;11右转掉头;12左直掉头;13直右掉头;14左直右掉头;15左右掉头
private Integer turn=0; private Integer turn=0;
/*车道终点坐标:x,y*/ /*车道终点坐标:x,y*/
private String laneEndPoint; private String laneEndPoint;
......
package com.wanji.indicators.task.trajectory;
import com.wanji.indicators.model.FrameModel;
import com.wanji.indicators.task.track.service.func.FrameFlatMap;
import com.wanji.indicators.task.trajectory.func.CarRecordInfoFlatMap;
import com.wanji.indicators.task.trajectory.func.ProcessCarInfo;
import com.wanji.indicators.task.trajectory.pojo.CarRecordInfo;
import com.wanji.indicators.util.PropertiesHelper;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.connector.jdbc.JdbcConnectionOptions;
import org.apache.flink.connector.jdbc.JdbcSink;
import org.apache.flink.connector.jdbc.JdbcStatementBuilder;
import org.apache.flink.connector.kafka.source.KafkaSource;
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer;
import org.apache.flink.connector.kafka.source.reader.deserializer.KafkaRecordDeserializationSchema;
import org.apache.flink.runtime.state.storage.FileSystemCheckpointStorage;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.CheckpointConfig;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.kafka.clients.consumer.OffsetResetStrategy;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/24 15:00
* @Description :
*/
public class CarTrajectoryAnalysisMain {
public static void main(String[] args) {
try {
PropertiesHelper instance = PropertiesHelper.getInstance();
Properties properties = instance.getProperties();
//获取配置文件中的kafka消费topic
String topic = properties.getProperty("consumer.topic");
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//配置检查点机制
env.enableCheckpointing(60*1000);
env.getCheckpointConfig().setTolerableCheckpointFailureNumber(3);
env.getCheckpointConfig().setCheckpointTimeout(10 * 60 * 1000);
env.getCheckpointConfig().setCheckpointStorage(new FileSystemCheckpointStorage(properties.getProperty("check.point.uri")));
env.getCheckpointConfig().setExternalizedCheckpointCleanup(CheckpointConfig.ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION);
KafkaSource<String> source = KafkaSource.<String>builder()
.setProperties(instance.getConsumerProperties())
.setProperty("auto.offset.commit", "true")
.setProperty("auto.commit.interval.ms", "1000")
.setProperty("commit.offsets.on.checkpoint", "true")
.setBootstrapServers(properties.getProperty("bootstrap.servers"))
.setTopics(topic)
.setGroupId(properties.getProperty("consumer.group.id")+"test1")
/* 设置起始偏移量有以下几种情况
1.从指定的位置消费:OffsetsInitializer.offsets(Map<TopicPartition, Long> offsets)
2.从最新位置消费(最后一条处):OffsetsInitializer.latest()
3.从最早位置消费(第一条处):OffsetsInitializer.earliest()
4.从上次提交的位置消费:OffsetsInitializer.committedOffsets()
5.新的组,从来没有提交过,再指定一个消费方式:OffsetsInitializer.committedOffsets(OffsetResetStrategy.LATEST)
*/
.setStartingOffsets(OffsetsInitializer.committedOffsets(OffsetResetStrategy.LATEST))
// 从大于等于此时间戳开始的偏移量开始
//.setStartingOffsets(OffsetsInitializer.timestamp(dateTime.getMillis()))
.setDeserializer(KafkaRecordDeserializationSchema.valueOnly(StringDeserializer.class))
.build();
DataStream<String> stream = env
.fromSource(source, WatermarkStrategy.noWatermarks(), "kafka-car-trajectory-source");
SingleOutputStreamOperator<FrameModel> frameModelStream = stream
.flatMap(new FrameFlatMap())
.setParallelism(1)
.name("轨迹帧数据-JsonToObject");
SingleOutputStreamOperator<CarRecordInfo> filteredStream = frameModelStream.
flatMap(new CarRecordInfoFlatMap())
.setParallelism(1)
.name("筛选中有车牌的车辆");
SingleOutputStreamOperator<CarRecordInfo> sink = filteredStream.keyBy(CarRecordInfo::getCarPlate)
.window(TumblingProcessingTimeWindows.of(Time.seconds(60)))
.process(new ProcessCarInfo())
.setParallelism(2)
.name("统计每分钟内不同车牌信息");
sink.addSink(JdbcSink.sink(
"insert into car_record_info (car_id, car_plate, create_time) values(?,?,?)",
new JdbcStatementBuilder<CarRecordInfo>() {
@Override
public void accept(PreparedStatement preparedStatement, CarRecordInfo carRecordInfo) throws SQLException {
preparedStatement.setInt(1, carRecordInfo.getCarId());
preparedStatement.setString(2, carRecordInfo.getCarPlate());
preparedStatement.setDate(3, new Date(carRecordInfo.getCreateTime().getTime()));
}
},
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withUrl("jdbc:mysql://10.102.1.182:3306/flink_test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8")
.withUsername("root")
.withPassword("Wanji300552")
.withDriverName("com.mysql.cj.jdbc.Driver")
.withConnectionCheckTimeoutSeconds(60)
.build()
)).name("写入mysql");
env.execute("从kafka消费轨迹数据并且写到mysql中");
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.wanji.indicators.task.trajectory;
import java.util.UUID;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/11/2 19:05
* @Description :
*/
public class Test2 {
public static void main(String[] args) {
String eventSerialNumber = UUID.randomUUID().toString().replace("-","");
System.out.println(eventSerialNumber);
}
}
package com.wanji.indicators.task.trajectory.func;
import com.wanji.indicators.constant.VehicleTypeEnum;
import com.wanji.indicators.model.CarTrackModel;
import com.wanji.indicators.model.FrameModel;
import com.wanji.indicators.task.trajectory.CarTrajectoryIndexMain;
import com.wanji.indicators.util.DateUtil;
import com.wanji.indicators.util.GeomsConvertUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.util.Collector;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/25 14:48
* @Description :
*/
public class CarDataValidatorFlatMap implements FlatMapFunction<FrameModel, CarTrackModel> {
@Override
public void flatMap(FrameModel frameModel, Collector<CarTrackModel> collector) throws Exception {
Long globalTimeStamp = frameModel.getGlobalTimeStamp();
String globalId = frameModel.getOrgCode();
List<CarTrackModel> trackList = frameModel.getTrackList();
if(!CollectionUtils.isEmpty(trackList)){
List<CarTrackModel> list = trackList.stream()
.filter(CarDataValidatorFlatMap::isValidated)
.peek(source -> {
source.setTimeStamp(DateUtil.toDateTime(globalTimeStamp, "yyyy-MM-dd HH:mm:ss.SSS"));
source.setGlobalTimeStamp(globalTimeStamp);
source.setOrgCode(globalId);
//检验车流轨迹数据是否位于虚拟路口区域
Map<String, String> virtualCrossRoadArea = CarTrajectoryIndexMain.virtualCrossRoadArea;
for(Map.Entry<String,String> entry: virtualCrossRoadArea.entrySet()){
boolean inPolygon = GeomsConvertUtil.isInPolygon(source.getLongitude(), source.getLatitude(), entry.getValue());
if(inPolygon){
source.getRoadnet().setInCrossFlag(1);
source.getRoadnet().setCrossId(entry.getKey());
break;
}
}
short originalType = source.getOriginalType().shortValue();
int category;
if (VehicleTypeEnum.isMotorVehicles(originalType)) {
category = 1;
} else if (VehicleTypeEnum.isNonMotorVehicles(originalType)) {
category = 2;
} else {
category = 3;
}
source.setCategory(category);
}).collect(Collectors.toList());
list.forEach(collector::collect);
}
}
public static boolean isValidated(CarTrackModel source) {
//首先校验是否存在车牌信息
boolean hasQualifiedPlat = isQualifiedPlate(source.getPicLicense());
if(hasQualifiedPlat){
//校验路网信息是否齐全
CarTrackModel.RoadNet roadNet = source.getRoadnet();
if(Objects.nonNull(roadNet)){
boolean qualifiedCrossId = isQualified(roadNet.getCrossId());
boolean qualifiedRid = isQualified(roadNet.getRid());
boolean qualifiedLaneId = isQualified(roadNet.getLaneId());
boolean qualifiedSegmentId = isQualified(roadNet.getSegmentId());
return qualifiedCrossId && qualifiedRid && qualifiedLaneId && qualifiedSegmentId;
}
}
return false;
}
public static boolean isQualified(String string) {
return StringUtils.isNotEmpty(string) && !Objects.equals("null", string);
}
public static boolean isQualifiedPlate(String plate){
return StringUtils.isNotEmpty(plate) && !Objects.equals("null", plate) && !Objects.equals("默A00000", plate);
}
}
package com.wanji.indicators.task.trajectory.func;
import com.wanji.indicators.model.CarTrackModel;
import com.wanji.indicators.model.FrameModel;
import com.wanji.indicators.task.trajectory.pojo.CarRecordInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.util.Collector;
import org.springframework.util.CollectionUtils;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/24 16:21
* @Description :
*/
public class CarRecordInfoFlatMap implements FlatMapFunction<FrameModel, CarRecordInfo> {
@Override
public void flatMap(FrameModel frameModel, Collector<CarRecordInfo> collector) throws Exception {
List<CarTrackModel> trackList = frameModel.getTrackList();
if(!CollectionUtils.isEmpty(trackList)){
List<CarRecordInfo> filteredList = trackList.stream().filter(carTrackModel -> StringUtils.isNotEmpty(carTrackModel.getPicLicense()) && !Objects.equals("null", carTrackModel.getPicLicense()))
.map(carTrackModel -> {
CarRecordInfo carRecordInfo = new CarRecordInfo();
carRecordInfo.setCarId(carTrackModel.getId());
carRecordInfo.setCarPlate(carTrackModel.getPicLicense());
carRecordInfo.setCreateTime(new Date());
return carRecordInfo;
}).collect(Collectors.toList());
filteredList.forEach(collector::collect);
}
}
}
package com.wanji.indicators.task.trajectory.func;
import com.alibaba.fastjson.JSONArray;
import com.wanji.indicators.model.CrossFrameModel;
import com.wanji.indicators.model.CrossRidTurnLampStatusModel;
import com.wanji.indicators.task.trajectory.pojo.CrossRoadLightStatusModel;
import org.apache.flink.streaming.api.functions.co.CoFlatMapFunction;
import org.apache.flink.util.Collector;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/11/1 13:54
* @Description :
*/
public class CrossRoadLightStatusCoFlatMap implements CoFlatMapFunction<CrossFrameModel, String, CrossFrameModel> {
private static final ConcurrentHashMap<String, CrossRoadLightStatusModel> lightStatusMap = new ConcurrentHashMap<>();
@Override
public void flatMap1(CrossFrameModel crossFrameModel, Collector<CrossFrameModel> collector) throws Exception {
//将路口的每一帧轨迹数据与当前路口的相位灯信息绑定
if(lightStatusMap.containsKey(crossFrameModel.getCrossId())){
CrossRoadLightStatusModel lightStatus = lightStatusMap.get(crossFrameModel.getCrossId());
//灯态上报时间戳
long lightStatusTimeStamp = Long.parseLong(lightStatus.getTimeStamp());
//当前路口车流轨迹数据帧时间戳
long globalTimeStamp = crossFrameModel.getGlobalTimeStamp();
if(globalTimeStamp >= lightStatusTimeStamp){
Integer cycleCountDown = lightStatus.getCycleCountDown();
Integer cyclePhaseCountDown = lightStatus.getCyclePhaseCountDown();
Integer cycleLen = lightStatus.getCycleLen();
//存储当前路口不同进口车道的相位灯信息
List<CrossRidTurnLampStatusModel> ridTurnLampList = new ArrayList<>();
Map<String, Map<String, String>> dirLampGroupMap = lightStatus.getDirLampGroupMap();
for(Map.Entry<String, Map<String, String>> entry: dirLampGroupMap.entrySet()){
//不同转向车道和相位灯状态
Map<String, String> laneTurnAndLampState = entry.getValue();
for(Map.Entry<String, String> laneEntry: laneTurnAndLampState.entrySet()){
CrossRidTurnLampStatusModel ridTurnLampStatusModel = new CrossRidTurnLampStatusModel();
ridTurnLampStatusModel.setCrossId(lightStatus.getCrossId());
ridTurnLampStatusModel.setCycleCountDown(cycleCountDown);
ridTurnLampStatusModel.setCyclePhaseCountDown(cyclePhaseCountDown);
ridTurnLampStatusModel.setCycleLen(cycleLen);
ridTurnLampStatusModel.setDir(Integer.parseInt(entry.getKey()));
ridTurnLampStatusModel.setTurn(laneEntry.getKey());//车道转向
ridTurnLampStatusModel.setLampState(laneEntry.getValue());//车道对应灯态
ridTurnLampList.add(ridTurnLampStatusModel);
}
}
//绑定灯态信息
crossFrameModel.setRidTurnLampList(ridTurnLampList);
collector.collect(crossFrameModel);
}
}
}
@Override
public void flatMap2(String lightStatusJson, Collector<CrossFrameModel> collector) throws Exception {
List<CrossRoadLightStatusModel> list = JSONArray.parseArray(lightStatusJson, CrossRoadLightStatusModel.class);
for(CrossRoadLightStatusModel lightStatus: list){
lightStatusMap.put(lightStatus.getCrossId(), lightStatus);
}
}
}
package com.wanji.indicators.task.trajectory.func;
import com.wanji.indicators.task.trajectory.pojo.CarRecordInfo;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/24 17:04
* @Description :
*/
public class ProcessCarInfo extends ProcessWindowFunction<CarRecordInfo, CarRecordInfo, String, TimeWindow> {
@Override
public void process(String s, ProcessWindowFunction<CarRecordInfo, CarRecordInfo, String, TimeWindow>.Context context, Iterable<CarRecordInfo> iterable, Collector<CarRecordInfo> collector) throws Exception {
Set<Integer> idSets = new HashSet<>();
for (CarRecordInfo carRecordInfo : iterable) {
if (!idSets.contains(carRecordInfo.getCarId())) {
collector.collect(carRecordInfo);
idSets.add(carRecordInfo.getCarId());
}
}
}
}
package com.wanji.indicators.task.trajectory.func;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/25 16:09
* @Description :
*/
public class ProcessCarTrace {
}
package com.wanji.indicators.task.trajectory.func;
import com.wanji.indicators.model.CarTrackModel;
import com.wanji.indicators.task.trajectory.pojo.OverFlowIndexResult;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/27 10:39
* @Description :
*/
public class ProcessOverFlowFunction extends ProcessWindowFunction<CarTrackModel, OverFlowIndexResult, String, TimeWindow> {
private Integer agvSpeed = 5;
private Integer duration = 3;
private static Map<String, Set<String>> crossAndRid;
private static Map<String, Set<String>> crossArea;
public ProcessOverFlowFunction(){
}
public ProcessOverFlowFunction(String agvSpeed, String duration){
super();
this.agvSpeed = Integer.parseInt(agvSpeed);
this.duration = Integer.parseInt(duration);
}
@Override
public void process(String s, ProcessWindowFunction<CarTrackModel, OverFlowIndexResult, String, TimeWindow>.Context context, Iterable<CarTrackModel> iterable, Collector<OverFlowIndexResult> collector) throws Exception {
List<CarTrackModel> carInCrossRoadList = StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
}
}
package com.wanji.indicators.task.trajectory.helper;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/26 22:34
* @Description :
*/
public class FileReadingHelper {
public static List<String> getFileContent(String path) {
InputStream inputStream = null;
try {
inputStream = FileReadingHelper.class.getClassLoader().getResourceAsStream(path);
assert inputStream != null;
return IOUtils.readLines(inputStream, StandardCharsets.UTF_8);
}catch (Exception e){
e.printStackTrace();
return null;
}finally {
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.wanji.indicators.task.trajectory.key;
import com.wanji.indicators.model.CarTrackModel;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.api.java.tuple.Tuple2;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/25 15:35
* @Description :
*/
public class KeySelectorByPlateAndRid implements KeySelector<CarTrackModel, Tuple2<String, String>> {
@Override
public Tuple2<String, String> getKey(CarTrackModel carTrackModel) throws Exception {
return new Tuple2<>(carTrackModel.getPicLicense(), carTrackModel.getRoadnet().getRid());
}
}
package com.wanji.indicators.task.trajectory.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/24 16:14
* @Description :
*/
@Data
@NoArgsConstructor
public class CarRecordInfo {
private Long id;
private Integer carId;
private String carPlate;
private Date createTime;
private Date updateTime;
private String createBy;
private String updateBy;
}
package com.wanji.indicators.task.trajectory.pojo;
import com.wanji.indicators.constant.VehicleTypeEnum;
import com.wanji.indicators.model.CarTrackModel;
import com.wanji.indicators.model.FrameModel;
import com.wanji.indicators.task.trajectory.CarTrajectoryIndexMain;
import com.wanji.indicators.task.trajectory.func.CarDataValidatorFlatMap;
import com.wanji.indicators.util.DateUtil;
import com.wanji.indicators.util.GeomsConvertUtil;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.util.Collector;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/27 9:46
* @Description :
*/
public class CarTrackInCrossRoadFlatMap implements FlatMapFunction<FrameModel, CarTrackModel> {
@Override
public void flatMap(FrameModel frameModel, Collector<CarTrackModel> collector) throws Exception {
Long globalTimeStamp = frameModel.getGlobalTimeStamp();
String globalId = frameModel.getOrgCode();
List<CarTrackModel> trackList = frameModel.getTrackList();
if(!CollectionUtils.isEmpty(trackList)){
List<CarTrackModel> processedList = trackList.stream()
.peek(car -> {
//判断车辆轨迹数据在指定区域内
Map<String, List<CrossExitInfo>> crossExitMap = CarTrajectoryIndexMain.crossExitMap;
for(Map.Entry<String, List<CrossExitInfo>> entry: crossExitMap.entrySet()){
Map<String, String> ridAndAreaCoordinates = entry.getValue().stream().collect(Collectors.toMap(CrossExitInfo::getRid, CrossExitInfo::getCoordinates));
if(ridAndAreaCoordinates.containsKey(car.getRoadnet().getRid())){
String coordinates = ridAndAreaCoordinates.get(car.getRoadnet().getRid());
boolean inPolygon = GeomsConvertUtil.isInPolygon(car.getLongitude(), car.getLatitude(), coordinates);
if(inPolygon){
car.setIsInSpecificArea(1);
car.getRoadnet().setCrossId(entry.getKey());
break;
}
}
}
})
.filter(car -> CarDataValidatorFlatMap.isQualifiedPlate(car.getPicLicense()))
.filter(car -> (Objects.nonNull(car.getRoadnet()) && CarDataValidatorFlatMap.isQualified(car.getRoadnet().getCrossId()) && (car.getRoadnet().getInCrossFlag() == 1)) || car.getIsInSpecificArea() == 1)
.peek(source -> {
source.setTimeStamp(DateUtil.toDateTime(globalTimeStamp, "yyyy-MM-dd HH:mm:ss.SSS"));
source.setGlobalTimeStamp(globalTimeStamp);
source.setOrgCode(globalId);
short originalType = source.getOriginalType().shortValue();
int category;
if (VehicleTypeEnum.isMotorVehicles(originalType)) {
category = 1;
} else if (VehicleTypeEnum.isNonMotorVehicles(originalType)) {
category = 2;
} else {
category = 3;
}
source.setCategory(category);
}).collect(Collectors.toList());
processedList.forEach(collector::collect);
}
}
}
package com.wanji.indicators.task.trajectory.pojo;
import com.wanji.indicators.constant.VehicleTypeEnum;
import com.wanji.indicators.model.CarTrackModel;
import com.wanji.indicators.model.FrameModel;
import com.wanji.indicators.task.trajectory.CarTrajectoryIndexMain;
import com.wanji.indicators.task.trajectory.func.CarDataValidatorFlatMap;
import com.wanji.indicators.util.DateUtil;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.util.Collector;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/30 10:10
* @Description :
*/
public class CarTrackModelInNorthAndSouth implements FlatMapFunction<FrameModel, CarTrackModel> {
@Override
public void flatMap(FrameModel frameModel, Collector<CarTrackModel> collector) throws Exception {
Long globalTimeStamp = frameModel.getGlobalTimeStamp();
String globalId = frameModel.getOrgCode();
List<CarTrackModel> trackList = frameModel.getTrackList();
if(!CollectionUtils.isEmpty(trackList)){
List<CarTrackModel> list = trackList.stream()
.filter(CarDataValidatorFlatMap::isValidated)
.filter(car -> CarTrajectoryIndexMain.northAndSouthMap.containsKey(car.getRoadnet().getRid()))
.peek(source -> {
source.setTimeStamp(DateUtil.toDateTime(globalTimeStamp, "yyyy-MM-dd HH:mm:ss.SSS"));
source.setGlobalTimeStamp(globalTimeStamp);
source.setOrgCode(globalId);
short originalType = source.getOriginalType().shortValue();
int category;
if (VehicleTypeEnum.isMotorVehicles(originalType)) {
category = 1;
} else if (VehicleTypeEnum.isNonMotorVehicles(originalType)) {
category = 2;
} else {
category = 3;
}
source.setCategory(category);
}).collect(Collectors.toList());
list.forEach(collector::collect);
}
}
}
package com.wanji.indicators.task.trajectory.pojo;
import lombok.Data;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/27 11:55
* @Description :
*/
@Data
public class CrossExitInfo {
//出口路段编号
private String rid;
//区域内每个顶点的坐标集
private String coordinates;
}
package com.wanji.indicators.task.trajectory.pojo;
import lombok.Data;
import java.util.Map;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/28 14:08
* @Description :
*/
@Data
public class CrossRoadLightStatusModel {
private String code;
private String crossId;
private String manufacturerCode;
private String runMode;
private String controlMode;
private String phasePlanId;
private String schemeStartTime;
private Integer cycleCountDown;
//绿灯倒计时
private Integer cyclePhaseCountDown;
private Integer cycleLen;
private String phaseId;
private String schemeId;
private Map<String, Map<String, String>> dirLampGroupMap;
// private Map<String, Integer> phaseMap;
private String timeStamp;
}
package com.wanji.indicators.task.trajectory.pojo;
import lombok.Data;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/27 10:06
* @Description :
*/
@Data
public class OverFlowDetail {
//出现溢出的路口出口所在路段名称
private String rid;
//溢出指数
private Double index;
//溢出发生时间戳
private Long timestamp;
//溢出时间 时间格式: yyyy-MM-dd HH:mm:ss
private String datetime;
}
package com.wanji.indicators.task.trajectory.pojo;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/27 10:03
* @Description :
*/
@Data
public class OverFlowIndexResult {
//发生溢出的路口id
private String crossId;
private List<OverFlowDetail> details;
private boolean isDeadLock;
}
package com.wanji.indicators.task.trajectory.pojo;
import lombok.Data;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/26 1:09
* @Description :
*/
@Data
public class RidIndexResultOfEastAndWest {
private String rid;
//该路段的结束路口
private String crossId;
private Double index;
private String indexName;
private String timestamp;
private Long globalTimeStamp;
}
package com.wanji.indicators.task.trajectory.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/25 22:23
* @Description :
*/
@Data
@NoArgsConstructor
public class SingleCarInfo {
private Integer id;
private String plate;
private String rid;
private String crossId;
private String laneId;
//从入口到出口的路段通行时间
private Long transitTime;
private String segmentId;
//旅游轨迹的第一帧数据检测时间
private Long startTime;
//旅游轨迹最后一帧数据检测时间
private Long endTime;
private Long windowStartTime;
private Long windowEndTime;
}
package com.wanji.indicators.task.trajectory.pojo;
import lombok.Data;
/**
* @author : jenny
* @version : 1.0
* @createTime : 2023/10/26 17:12
* @Description :
*/
@Data
public class UnbalanceResult {
//路口id
private String crossId;
//失衡发生时间
private String timestamp;
//失衡指数
private Double index;
}
...@@ -15,10 +15,14 @@ import com.vividsolutions.jts.operation.linemerge.LineMerger; ...@@ -15,10 +15,14 @@ import com.vividsolutions.jts.operation.linemerge.LineMerger;
import com.vividsolutions.jts.util.GeometricShapeFactory; import com.vividsolutions.jts.util.GeometricShapeFactory;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class GeomsConvertUtil { public class GeomsConvertUtil {
private static GeometryFactory geometryFactory = new GeometryFactory(); private static GeometryFactory geometryFactory = new GeometryFactory();
...@@ -1005,6 +1009,62 @@ public class GeomsConvertUtil { ...@@ -1005,6 +1009,62 @@ public class GeomsConvertUtil {
return lineList; return lineList;
} }
//判断一个点是否在多边形区域内
private static boolean isInPolygon(double longitude, double latitude, double[][] points) {
//将要判断的点的经纬度组成一个点
Point2D.Double point = new Point2D.Double(longitude, latitude);
//将区域各个顶点的坐标放到一个点集合里
ArrayList<Point2D.Double> point2DS = new ArrayList<>();
for(int i = 0; i < points.length; i++){
double polygonPoint_x = points[i][0];//经度
double polygonPoint_y = points[i][1];//纬度
Point2D.Double polygonPoint = new Point2D.Double(polygonPoint_x, polygonPoint_y);
point2DS.add(polygonPoint);
}
return check(point, point2DS);
}
//points的数据格式是:lon1,lat1;lon2,lat2
public static boolean isInPolygon(double longitude, double latitude, String points){
String[] pointsInOneDimension = points.trim().split(";");
List<double[]> collect = Arrays.stream(pointsInOneDimension).map(s -> s.trim().split(","))
.map(string -> {
double[] coordinates = new double[2];
coordinates[0] = Double.parseDouble(string[0]);
coordinates[1] = Double.parseDouble(string[1]);
return coordinates;
}).collect(Collectors.toList());
double[][] polygon = new double[collect.size()][2];
for(int i = 0; i < collect.size(); i++){
polygon[i] = collect.get(i);
}
return isInPolygon(longitude, latitude, polygon);
}
private static boolean check(Point2D.Double point, ArrayList<Point2D.Double> polygon) {
GeneralPath generalPath = new GeneralPath();
Point2D.Double firstPoint = polygon.get(0);
generalPath.moveTo(firstPoint.x, firstPoint.y);
polygon.remove(0);
for(Point2D.Double polygonPoint: polygon){
generalPath.lineTo(polygonPoint.x, polygonPoint.y);
}
//将几何多边形封闭
generalPath.lineTo(firstPoint.x, firstPoint.y);
generalPath.closePath();
//测试指定点是否在该区域边界内
return generalPath.contains(point);
}
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
......
...@@ -33,7 +33,7 @@ alarm.feishu.url=https://open.feishu.cn/open-apis/bot/v2/hook/0840f036-299e-4595 ...@@ -33,7 +33,7 @@ alarm.feishu.url=https://open.feishu.cn/open-apis/bot/v2/hook/0840f036-299e-4595
signal.status.url=ws://37.12.182.29:9000/utc/signalStatus/{crossId},stateMonitor signal.status.url=ws://37.12.182.29:9000/utc/signalStatus/{crossId},stateMonitor
#区域路口列表 #区域路口列表
full.area.cross.list=13NI00B5RM0,13NGH0B5RC0,13NF80B5QN0 full.area.cross.list=13NI00B5RM0,13NGH0B5RC0,13NF80B5QN0,13NID0B5RM0
#区域指标分析数据 #区域指标分析数据
area.analysis.data.topic=analysis.area.indicators area.analysis.data.topic=analysis.area.indicators
#路段参与者影响分析 #路段参与者影响分析
...@@ -44,3 +44,31 @@ cross.event.data.topic=analysis.cross.event ...@@ -44,3 +44,31 @@ cross.event.data.topic=analysis.cross.event
plate.prefix= plate.prefix=
#路段默认自由流速度配置值 km/h #路段默认自由流速度配置值 km/h
rid.default.free.speed=80 rid.default.free.speed=80
#路段拥堵指数统计
rid.traffic.index.analysis.topic=rid.traffic.index.analysis
#东西方向路段的rid和方向
east.west.rid.direction.list=13NED0B5Q9013NF80B5QN00:6,13NGH0B5RC013NF80B5QN00:2,13NI00B5RM013NGH0B5RC00:3,13NF80B5QN013NGH0B5RC00:6,13NGH0B5RC013NI00B5RM00:7,13NID0B5RM013NI00B5RM00:3
#南北方向路段的rid和方向
north.south.rid.direction.list=13NG40B5SK013NI00B5RM00:1,13NEH0B5RJ013NGH0B5RC00:1,13NEP0B5QJ013NGH0B5RC00:5,13NDG0B5RI013NF80B5QN00:1,13NDT0B5Q9013NF80B5QN00:4
#路口溢出评价指标
road.overflow.avg.speed=5.0
road.overflow.duration=3
road.overflow.deadlock.index.analysis.topic=crossroad.overflow.deadlock.index.analysis
#路口失衡topic
road.unbalance.index.analysis.topic=road.unbalance.index.analysis
#死锁评价指标
cross.road.deadlock.car.number=10
cross.road.deadlock.avg.speed=5.0
#相位灯的状态数据
light.status.topic=cross_lights_status
#虚拟路口区域
virtual.crossroad.13NED0B5Q90=13NED0B5Q90:117.08503591467242,36.64125732273356;117.08495255127629,36.641426722875224;117.08499878952986,36.641454206982246;117.08508543702352,36.641286700399476
virtual.crossroad.13NH20B5RH0=13NH20B5RH0:117.09669255282627,36.644871615002884;117.09669985552095,36.645055610398025;117.09675474612689,36.64505324101935;117.09674606214631,36.64486930675771
13NID0B5RM0:13NID0B5RM013NH20B5RH00:117.09637002802258,36.64488693335725;117.09637338547009,36.644965413693335;117.096424586545,36.64496331528863;117.09642122909746,36.64488609399537
13NGH0B5RC0:13NGH0B5RC013NF80B5QN00:117.08958898682974,36.643899290074785;117.08955379723122,36.64397154605041;117.08960400105843,36.643990313836284;117.08964012904624,36.64391876165263
13NGH0B5RC0:13NGH0B5RC013NEP0B5QJ00:117.0899131390518,36.6437643837966;117.09002985710713,36.64380773763002;117.09005137896341,36.643758573022545;117.08993645702961,36.64371467989449
13NGH0B5RC0:13NGH0B5RC013NI00B5RM00:117.09029328379803,36.64403689420678;117.0902662941944,36.64411114941236;117.09031650772073,36.64412899638859;117.09034442601454,36.64405636578462
13NGH0B5RC0:13NGH0B5RC013NEH0B5RJ00:117.0900452476105,36.644319696002505;117.08989006966468,36.64426827251168;117.089875426555,36.64431973551543;117.09002407323193,36.64436900205546
13NF80B5QN0:13NF80B5QN013NGH0B5RC00:117.0861592881092,36.64197997974727;117.08609667244684,36.64205197263441;117.08614355857083,36.64208312922003;117.08620587174205,36.64201325377074
13NF80B5QN0:13NF80B5QN013NDG0B5RI00:117.08584437829073,36.64214724722395;117.08578629999519,36.642116393129456;117.08576352430352,36.64216220348786;117.08581851529976,36.6421903522089
13NF80B5QN0:13NF80B5QN013NDT0B5Q900:117.08605990284744,36.64173522729118;117.08601072453179,36.64170088034566;117.08596764994606,36.64173562023835;117.08601707684535,36.64177287323281
13NI00B5RM0:13NI00B5RM013NGH0B5RC00:117.09429965504133,36.64494101925379;117.09428967283428,36.64501573456104;117.09434230628959,36.64502178438349;117.09435410344337,36.6449452541295
13NI00B5RM0:13NI00B5RM013NG40B5SK00:117.09481322833656,36.64521631203853;117.09467287245573,36.64521963944088;117.09467426195232,36.645273677983255;117.09481413580993,36.645270760440575
13NI00B5RM0:13NI00B5RM013NID0B5RM00:117.09495729255127,36.644879713845825;117.0949548726223,36.64496864623584;117.09500798547438,36.644968227032756;117.09500962351547,36.64488122630144
13NF80B5QN0:13NF80B5QN013NED0B5Q900:117.08568429578892,36.641777410599644;117.08564440984892,36.64186426813691;117.0856906481025,36.641891752243936;117.08573299685965,36.641806147256275
13NID0B5RM0:13NID0B5RM013NI00B5RM00:117.09559352885879,36.644983794719344;117.09559110892981,36.64507272710936;117.0956442217819,36.645072307906275;117.09564585982298,36.64498530717496
13NID0B5RM0:13NID0B5RM013NHC0B5R400:117.09611905881948,36.64473039236603;117.0961578793065,36.644764806203234;117.09619669979368,36.64473584821827;117.09615515138046,36.644701434381055
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment