22 package org.matsim.analysis;
24 import jakarta.inject.Inject;
25 import org.apache.commons.csv.CSVFormat;
26 import org.apache.commons.csv.CSVPrinter;
27 import org.apache.commons.lang3.ArrayUtils;
28 import org.apache.commons.lang3.StringUtils;
29 import org.apache.logging.log4j.LogManager;
30 import org.apache.logging.log4j.Logger;
49 import java.io.IOException;
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.List;
54 import java.util.stream.Collectors;
62 "dep_time",
"trav_time",
"wait_time",
"traveled_distance",
"euclidean_distance",
63 "main_mode",
"longest_distance_mode",
"modes",
"start_activity_type",
64 "end_activity_type",
"start_facility_id",
"start_link",
65 "start_x",
"start_y",
"end_facility_id",
66 "end_link",
"end_x",
"end_y",
"first_pt_boarding_stop",
"last_pt_egress_stop"};
69 "dep_time",
"trav_time",
"wait_time",
"distance",
"mode",
"network_mode",
"start_link",
70 "start_x",
"start_y",
"end_link",
"end_x",
"end_y",
"access_stop_id",
"egress_stop_id",
"transit_line",
"transit_route",
"vehicle_id"};
85 this.legsWriterExtension = legWriterExtension;
99 CSVFormat.Builder.create().setDelimiter(
getDefaultDelimiter()).setHeader(TRIPSHEADER).build());
101 CSVFormat.Builder.create().setDelimiter(
getDefaultDelimiter()).setHeader(LEGSHEADER).build())
105 tripsCSVprinter.printRecords(tripsAndLegRecords.
getFirst());
106 legsCSVprinter.printRecords(tripsAndLegRecords.
getSecond());
108 }
catch (IOException e) {
114 List<List<String>> tripRecords =
new ArrayList<>();
115 List<List<String>> legRecords =
new ArrayList<>();
119 for (
int i = 0; i < trips.size(); i++) {
121 List<String> tripRecord =
new ArrayList<>();
122 tripRecords.add(tripRecord);
123 tripRecord.add(personId.toString());
124 final String tripNo = Integer.
toString(i + 1);
125 tripRecord.add(tripNo);
126 String tripId = personId +
"_" + tripNo;
127 tripRecord.add(tripId);
128 double distance = 0.0;
129 double departureTime = trip.getOriginActivity().getEndTime().orElse(0);
130 double travelTime = trip.getDestinationActivity().getStartTime().orElse(0) - departureTime;
133 double totalWaitingTime = 0.0;
134 double currentLongestShareDistance = Double.MIN_VALUE;
135 String currentModeWithLongestShare =
"";
136 List<String> modes =
new ArrayList<>();
137 String lastActivityType = trip.getOriginActivity().getType();
138 String nextActivityType = trip.getDestinationActivity().getType();
141 Id<Link> fromLinkId = trip.getOriginActivity().getLinkId();
142 Id<Link> toLinkId = trip.getDestinationActivity().getLinkId();
146 String firstPtBoardingStop = null;
147 String lastPtEgressStop = null;
149 String mainMode =
"";
150 if (mainModeIdentifier != null) {
153 if (mainMode == null)
160 for (
Leg leg : trip.getLegsOnly()) {
161 modes.add(leg.getMode());
162 final double legDist = leg.getRoute().getDistance();
165 if (boardingTime != null) {
166 double waitingTime = boardingTime - leg.getDepartureTime().seconds();
167 totalWaitingTime += waitingTime;
169 if (
StringUtils.isBlank(currentModeWithLongestShare) || legDist > currentLongestShareDistance) {
170 currentLongestShareDistance = legDist;
171 currentModeWithLongestShare = leg.getMode();
175 firstPtBoardingStop = firstPtBoardingStop != null ? firstPtBoardingStop : route.getAccessStopId().toString();
176 lastPtEgressStop = route.getEgressStopId().toString();
181 tripRecord.add(customTimeWriter.
writeTime(departureTime));
182 tripRecord.add(customTimeWriter.
writeTime(travelTime));
183 tripRecord.add(customTimeWriter.
writeTime(totalWaitingTime));
184 tripRecord.add(Integer.toString((
int) Math.round(distance)));
185 tripRecord.add(Integer.toString(euclideanDistance));
186 tripRecord.add(mainMode);
187 tripRecord.add(currentModeWithLongestShare);
188 tripRecord.add(modes.stream().collect(Collectors.joining(
"-")));
189 tripRecord.add(lastActivityType);
190 tripRecord.add(nextActivityType);
191 tripRecord.add(String.valueOf(fromFacilityId));
192 tripRecord.add(String.valueOf(fromLinkId));
193 tripRecord.add(Double.toString(fromCoord.
getX()));
194 tripRecord.add(Double.toString(fromCoord.
getY()));
196 tripRecord.add(String.valueOf(toFacilityId));
197 tripRecord.add(String.valueOf(toLinkId));
198 tripRecord.add(Double.toString(toCoord.getX()));
199 tripRecord.add(Double.toString(toCoord.getY()));
200 tripRecord.add(firstPtBoardingStop != null ? firstPtBoardingStop :
"");
201 tripRecord.add(lastPtEgressStop != null ? lastPtEgressStop :
"");
204 if (TRIPSHEADER.length != tripRecord.size()) {
207 log.error(errorMessage);
213 List<PlanElement> allElements =
new ArrayList<>();
214 allElements.add(trip.getOriginActivity());
215 allElements.addAll(trip.getTripElements());
216 allElements.add(trip.getDestinationActivity());
218 if (pe instanceof
Activity currentAct) {
219 if (prevLeg != null) {
220 List<String> legRecord =
getLegRecord(prevLeg, personId.toString(), tripId, prevAct, currentAct, trip);
221 legRecords.add(legRecord);
223 prevAct = currentAct;
225 }
else if (pe instanceof
Leg) {
235 StringBuilder str =
new StringBuilder();
236 str.append(
"Custom CSV Trip Writer Extension does not provide an identical number of additional values and additional columns. Number of columns is ")
237 .append(TRIPSHEADER.length).append(
", and number of values is ").append(tripRecord.size())
239 .append(
"TripsWriterExtension class was: ").append(tripsWriterExtension.getClass())
240 .append(
". Column name to value pairs supplied were:\n");
241 for (
int j = 0; j < Math.max(TRIPSHEADER.length, tripRecord.size()); j++) {
244 columnNameJ = TRIPSHEADER[j];
245 }
catch (ArrayIndexOutOfBoundsException e) {
246 columnNameJ =
"!COLUMN MISSING!";
250 tripRecordJ = tripRecord.get(j);
251 }
catch (IndexOutOfBoundsException e) {
252 tripRecordJ =
"!VALUE MISSING!";
254 str.append(j +
": " + columnNameJ +
": " + tripRecordJ +
"\n");
260 List<String> record =
new ArrayList<>();
261 record.add(personId);
267 double waitingTime = 0.;
268 if (boardingTime != null) {
271 record.add(customTimeWriter.
writeTime(waitingTime));
285 record.add(Double.toString(startCoord.
getX()));
286 record.add(Double.toString(startCoord.
getY()));
288 record.add(Double.toString(endCoord.
getX()));
289 record.add(Double.toString(endCoord.
getY()));
290 String transitLine =
"";
291 String transitRoute =
"";
292 String ptAccessStop =
"";
293 String ptEgressStop =
"";
295 transitLine = route.getLineId().toString();
296 transitRoute = route.getRouteId().toString();
297 ptAccessStop = route.getAccessStopId().toString();
298 ptEgressStop = route.getEgressStopId().toString();
300 record.add(ptAccessStop);
301 record.add(ptEgressStop);
302 record.add(transitLine);
303 record.add(transitRoute);
304 record.add(vehicleId != null ? vehicleId.toString() :
"");
308 if (LEGSHEADER.length != record.size()) {
311 log.error(errorMessage);
319 StringBuilder str =
new StringBuilder();
320 str.append(
"Custom CSV Leg Writer Extension does not provide an identical number of additional values and additional columns. Number of columns is ")
321 .append(LEGSHEADER.length)
322 .append(
", and number of values is ")
323 .append(record.size())
325 .append(
"LegsWriterExtension class was: ")
326 .append(legsWriterExtension.getClass())
327 .append(
". Column name to value pairs supplied were:\n");
328 for (
int j = 0; j < Math.max(LEGSHEADER.length, record.size()); j++) {
331 columnNameJ = LEGSHEADER[j];
332 }
catch (ArrayIndexOutOfBoundsException e) {
333 columnNameJ =
"!COLUMN MISSING!";
337 recordJ = record.get(j);
338 }
catch (IndexOutOfBoundsException e) {
339 recordJ =
"!VALUE MISSING!";
341 str.append(j).append(
": ").append(columnNameJ).append(
": ").append(recordJ).append(
"\n");
343 return str.toString();
372 String[] getAdditionalLegHeader();
379 String writeTime(
double time);
384 public String writeTime(
double time) {
385 return Long.toString((
long) time);
391 public String writeTime(
double time) {
399 return new String[0];
404 return Collections.EMPTY_LIST;
411 public String[] getAdditionalLegHeader() {
412 return new String[0];
417 return Collections.EMPTY_LIST;
static final String [] TRIPSHEADER_BASE
List< String > getAdditionalLegColumns(TripStructureUtils.Trip experiencedTrip, Leg experiencedLeg)
Map< Id< ActivityFacility >, ? extends ActivityFacility > getFacilities()
Id< Link > getStartLinkId()
Attributes getAttributes()
static final String [] LEGSHEADER_BASE
static double calcEuclideanDistance(Coord coord, Coord other)
Set< Map.Entry< Id< T >, V > > entrySet()
final CustomLegsWriterExtension legsWriterExtension
final String [] LEGSHEADER
String identifyMainMode(List<? extends PlanElement > tripElements)
default List< String > getAdditionalTripColumns(Id< Person > personId, TripStructureUtils.Trip trip)
static final String ENTER_VEHICLE_TIME_ATTRIBUTE_NAME
Coord getCoordFromActivity(Activity activity)
OptionalTime getDepartureTime()
List< String > getLegRecord(Leg leg, String personId, String tripId, Activity previousAct, Activity nextAct, TripStructureUtils.Trip trip)
static BufferedWriter getBufferedWriter(URL url, Charset charset, boolean append)
String [] getAdditionalTripHeader()
StringBuilder getTripErrorMessage(List< String > tripRecord)
void write(IdMap< Person, Plan > experiencedPlans, String tripsFilename, String legsFilename)
String writeTime(double time)
final CustomTripsWriterExtension tripsWriterExtension
final AnalysisMainModeIdentifier mainModeIdentifier
static final String VEHICLE_ID_ATTRIBUTE_NAME
List< String > getAdditionalTripColumns(TripStructureUtils.Trip trip)
Object getAttribute(final String attribute)
final String [] TRIPSHEADER
static List< Trip > getTrips(final Plan plan)
final String getNetworkMode()
String [] getAdditionalLegHeader()
static final String writeTime(final double seconds, final String timeformat)
Map< Id< Link >, ? extends Link > getLinks()
OptionalTime getTravelTime()
Id< ActivityFacility > getFacilityId()
String getLegErrorMessage(List< String > record)
Coord getCoordFromLink(Id< Link > linkId)
Map< Id< Vehicle >, Vehicle > getVehicles()
ActivityFacilities getActivityFacilities()
final CustomTimeWriter customTimeWriter
Id< Link > getEndLinkId()
final GlobalConfigGroup global()
final boolean hasNetworkMode()
char getDefaultDelimiter()
String getDefaultDelimiter()
TripsAndLegsWriter(Scenario scenario, CustomTripsWriterExtension tripsWriterExtension, CustomLegsWriterExtension legWriterExtension, AnalysisMainModeIdentifier mainModeIdentifier, CustomTimeWriter customTimeWriter)
Tuple< Iterable<?>, Iterable<?> > getPlanCSVRecords(Plan experiencedPlan, Id< Person > personId)