Skip to content

Commit

Permalink
Merge pull request #130 from usdot-jpo-ode/add-new-reports
Browse files Browse the repository at this point in the history
Add new reports
  • Loading branch information
John-Wiens authored Jan 6, 2025
2 parents 1eca80b + d7f433d commit 5e8bc7c
Showing 8 changed files with 401 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -91,7 +91,7 @@ public List<IDCount> getSignalStateConflictEventsByDay(int intersectionID, Long
.and(DateOperators.DateToString.dateOf("eventGeneratedAt").toString("%Y-%m-%d")).as("dateStr"),
Aggregation.group("dateStr").count().as("count")
);

AggregationResults<IDCount> result = mongoTemplate.aggregate(aggregation, collectionName, IDCount.class);
List<IDCount> results = result.getMappedResults();

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package us.dot.its.jpo.ode.api.models;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class ConnectionData {
private String connectionID;
private int ingressLaneID;
private int egressLaneID;
private int eventCount;

public ConnectionData(String connectionID, int ingressLaneID, int egressLaneID, int eventCount) {
this.connectionID = connectionID;
this.ingressLaneID = ingressLaneID;
this.egressLaneID = egressLaneID;
this.eventCount = eventCount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package us.dot.its.jpo.ode.api.models;

import java.util.List;
import java.util.Set;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.Arrays;

import us.dot.its.jpo.geojsonconverter.pojos.geojson.LineString;
import us.dot.its.jpo.geojsonconverter.pojos.geojson.connectinglanes.ConnectingLanesFeatureCollection;
import us.dot.its.jpo.geojsonconverter.pojos.geojson.map.ProcessedMap;

public class ConnectionOfTravelData {
private List<ConnectionData> validConnections;
private List<ConnectionData> invalidConnections;

public ConnectionOfTravelData(List<ConnectionData> validConnections, List<ConnectionData> invalidConnections) {
this.validConnections = validConnections;
this.invalidConnections = invalidConnections;
}

public List<ConnectionData> getValidConnections() {
return validConnections;
}

public List<ConnectionData> getInvalidConnections() {
return invalidConnections;
}

public static ConnectionOfTravelData processConnectionOfTravelData(List<LaneConnectionCount> connectionCounts, ProcessedMap<LineString> mostRecentProcessedMap) {
// Get the valid connections from the most recent processed map
ConnectingLanesFeatureCollection<LineString> connectingLanesFeatureCollection = mostRecentProcessedMap.getConnectingLanesFeatureCollection();
Set<String> validConnectionIDs = Arrays.stream(connectingLanesFeatureCollection.getFeatures())
.map(feature -> feature.getId())
.collect(Collectors.toSet());

List<ConnectionData> validConnections = new ArrayList<>();
List<ConnectionData> invalidConnections = new ArrayList<>();

for (LaneConnectionCount count : connectionCounts) {
String key = count.getIngressLaneID() + "-" + count.getEgressLaneID();
ConnectionData connectionData = new ConnectionData(key, count.getIngressLaneID(), count.getEgressLaneID(), count.getCount());

if (validConnectionIDs.contains(key)) {
validConnections.add(connectionData);
} else {
invalidConnections.add(connectionData);
}
}

return new ConnectionOfTravelData(validConnections, invalidConnections);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package us.dot.its.jpo.ode.api.models;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import us.dot.its.jpo.conflictmonitor.monitor.models.assessments.LaneDirectionOfTravelAssessment;
import us.dot.its.jpo.conflictmonitor.monitor.models.assessments.LaneDirectionOfTravelAssessmentGroup;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@Getter
@Setter
@ToString
public class LaneDirectionOfTravelReportData {
private long timestamp;
private int laneID;
private int segmentID;
private double headingDelta;
private double medianCenterlineDistance;

public static List<LaneDirectionOfTravelReportData> processLaneDirectionOfTravelData(List<LaneDirectionOfTravelAssessment> assessments) {
List<LaneDirectionOfTravelReportData> reportDataList = new ArrayList<>();

// Sort data by timestamp
List<LaneDirectionOfTravelAssessment> sortedData = assessments.stream()
.sorted(Comparator.comparing(LaneDirectionOfTravelAssessment::getTimestamp))
.collect(Collectors.toList());

// Group data by lane ID and segment ID
Map<Integer, Map<Integer, List<LaneDirectionOfTravelReportData>>> groupedData = new HashMap<>();
for (LaneDirectionOfTravelAssessment assessment : sortedData) {
long minute = Instant.ofEpochMilli(assessment.getTimestamp()).truncatedTo(ChronoUnit.MINUTES).toEpochMilli();
for (LaneDirectionOfTravelAssessmentGroup group : assessment.getLaneDirectionOfTravelAssessmentGroup()) {

LaneDirectionOfTravelReportData reportData = new LaneDirectionOfTravelReportData();
reportData.setTimestamp(minute);
reportData.setLaneID(group.getLaneID());
reportData.setSegmentID(group.getSegmentID());
reportData.setHeadingDelta(group.getMedianHeading() - group.getExpectedHeading());
reportData.setMedianCenterlineDistance(group.getMedianCenterlineDistance());

groupedData
.computeIfAbsent(group.getLaneID(), k -> new HashMap<>())
.computeIfAbsent(group.getSegmentID(), k -> new ArrayList<>())
.add(reportData);
}
}

// Aggregate data by minute for each lane ID and segment ID
for (Map.Entry<Integer, Map<Integer, List<LaneDirectionOfTravelReportData>>> laneEntry : groupedData.entrySet()) {
int laneID = laneEntry.getKey();
for (Map.Entry<Integer, List<LaneDirectionOfTravelReportData>> segmentEntry : laneEntry.getValue().entrySet()) {
int segmentID = segmentEntry.getKey();
Map<Long, List<LaneDirectionOfTravelReportData>> aggregatedData = segmentEntry.getValue().stream()
.collect(Collectors.groupingBy(LaneDirectionOfTravelReportData::getTimestamp));

for (Map.Entry<Long, List<LaneDirectionOfTravelReportData>> minuteEntry : aggregatedData.entrySet()) {
long minute = minuteEntry.getKey();
List<LaneDirectionOfTravelReportData> minuteData = minuteEntry.getValue();

double averageHeadingDelta = minuteData.stream()
.mapToDouble(LaneDirectionOfTravelReportData::getHeadingDelta)
.average()
.orElse(0.0);

double averageCenterlineDistance = minuteData.stream()
.mapToDouble(LaneDirectionOfTravelReportData::getMedianCenterlineDistance)
.average()
.orElse(0.0);

LaneDirectionOfTravelReportData aggregatedReportData = new LaneDirectionOfTravelReportData();
aggregatedReportData.setTimestamp(minute);
aggregatedReportData.setLaneID(laneID);
aggregatedReportData.setSegmentID(segmentID);
aggregatedReportData.setHeadingDelta(averageHeadingDelta);
aggregatedReportData.setMedianCenterlineDistance(averageCenterlineDistance);

reportDataList.add(aggregatedReportData);
}
}
}

return reportDataList;
}
}
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
import org.springframework.data.annotation.Id;

import lombok.EqualsAndHashCode;
import java.util.List;

@ToString
@Setter
@@ -22,6 +23,26 @@ public class ReportDocument {
private long reportStartTime;
private long reportStopTime;
private byte[] reportContents;

}

private List<IDCount> laneDirectionOfTravelEventCounts;
private List<IDCount> laneDirectionOfTravelMedianDistanceDistribution;
private List<IDCount> laneDirectionOfTravelMedianHeadingDistribution;
private List<LaneDirectionOfTravelReportData> laneDirectionOfTravelReportData;
private double headingTolerance;
private double distanceTolerance;
private List<ConnectionData> validConnectionOfTravelData; // Updated type
private List<ConnectionData> invalidConnectionOfTravelData; // Updated type
private List<IDCount> connectionOfTravelEventCounts;
private List<IDCount> signalStateConflictEventCount;
private List<IDCount> signalStateEventCounts;
private List<IDCount> signalStateStopEventCounts;
private List<IDCount> timeChangeDetailsEventCount;
private List<IDCount> intersectionReferenceAlignmentEventCounts;
private List<IDCount> mapBroadcastRateEventCount;
private List<IDCount> mapMinimumDataEventCount;
private List<IDCount> spatMinimumDataEventCount;
private List<IDCount> spatBroadcastRateEventCount;
private List<String> latestMapMinimumDataEventMissingElements;
private List<String> latestSpatMinimumDataEventMissingElements;
private List<StopLineStopReportData> stopLineStopReportData;
private List<StopLinePassageReportData> stopLinePassageReportData;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package us.dot.its.jpo.ode.api.models;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import us.dot.its.jpo.conflictmonitor.monitor.models.events.StopLinePassageEvent;

@Getter
@Setter
@ToString
public class StopLinePassageReportData {
private int signalGroup;
private int totalEvents;
private int redEvents;
private int yellowEvents;
private int greenEvents;
private int darkEvents;

public static List<StopLinePassageReportData> aggregateSignalStateEvents(List<StopLinePassageEvent> events) {
Map<Integer, StopLinePassageReportData> reportDataMap = new HashMap<>();

for (StopLinePassageEvent event : events) {
int signalGroup = event.getSignalGroup();
StopLinePassageReportData data = reportDataMap.getOrDefault(signalGroup, new StopLinePassageReportData());
data.setSignalGroup(signalGroup);
data.setTotalEvents(data.getTotalEvents() + 1);

switch(event.getEventState()) {
case UNAVAILABLE:
case DARK:
data.setDarkEvents(data.getDarkEvents() + 1);
break;
case STOP_THEN_PROCEED:
case STOP_AND_REMAIN:
data.setRedEvents(data.getRedEvents() + 1);
break;
case PRE_MOVEMENT:
case PERMISSIVE_MOVEMENT_ALLOWED:
case PROTECTED_MOVEMENT_ALLOWED:
data.setGreenEvents(data.getGreenEvents() + 1);
break;
case PERMISSIVE_CLEARANCE:
case PROTECTED_CLEARANCE:
case CAUTION_CONFLICTING_TRAFFIC:
data.setYellowEvents(data.getYellowEvents() + 1);
break;
default:
break;
}

reportDataMap.put(signalGroup, data);
}

return new ArrayList<>(reportDataMap.values());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package us.dot.its.jpo.ode.api.models;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import us.dot.its.jpo.conflictmonitor.monitor.models.events.StopLineStopEvent;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;

@Getter
@Setter
@ToString
public class StopLineStopReportData {
private int signalGroup;
private int numberOfEvents;
private double timeStoppedOnRed;
private double timeStoppedOnYellow;
private double timeStoppedOnGreen;
private double timeStoppedOnDark;

public static List<StopLineStopReportData> aggregateStopLineStopEvents(List<StopLineStopEvent> events) {
Map<Integer, StopLineStopReportData> reportDataMap = new HashMap<>();

for (StopLineStopEvent event : events) {
int signalGroup = event.getSignalGroup();
StopLineStopReportData data = reportDataMap.getOrDefault(signalGroup, new StopLineStopReportData());
data.setSignalGroup(signalGroup);
data.setNumberOfEvents(data.getNumberOfEvents() + 1);
data.setTimeStoppedOnRed(data.getTimeStoppedOnRed() + event.getTimeStoppedDuringRed());
data.setTimeStoppedOnYellow(data.getTimeStoppedOnYellow() + event.getTimeStoppedDuringYellow());
data.setTimeStoppedOnGreen(data.getTimeStoppedOnGreen() + event.getTimeStoppedDuringGreen());
data.setTimeStoppedOnDark(data.getTimeStoppedOnDark() + event.getTimeStoppedDuringDark());
reportDataMap.put(signalGroup, data);
}

return new ArrayList<>(reportDataMap.values());
}
}

Large diffs are not rendered by default.

0 comments on commit 5e8bc7c

Please sign in to comment.