Add departures
Co-authored-by: Ange Herman KOUE-HEMAZRO <kankoe-ange-herman.koue-hemazro@etu.u-paris.fr>
This commit is contained in:
parent
5727710944
commit
2f0114dac8
4 changed files with 237 additions and 33 deletions
|
@ -0,0 +1,140 @@
|
||||||
|
package fr.u_paris.gla.project.idfm;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import fr.u_paris.gla.project.io.ScheduleFormat;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import fr.u_paris.gla.project.utils.GPS;
|
||||||
|
|
||||||
|
public class CSVStreamSchedulesProvider {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* private static final NumberFormat GPS_FORMATTER = ScheduleFormat
|
||||||
|
* .getGPSFormatter();
|
||||||
|
*/
|
||||||
|
private static final NumberFormat MINUTES_SECOND_FORMATTER = NumberFormat
|
||||||
|
.getInstance(Locale.ENGLISH);
|
||||||
|
static {
|
||||||
|
MINUTES_SECOND_FORMATTER.setMinimumIntegerDigits(2);
|
||||||
|
}
|
||||||
|
/** Number of seconds in a minute. */
|
||||||
|
private static final int SECONDS_IN_MINUTES = 60;
|
||||||
|
private static final long SECONDS_IN_HOURS = 3_600;
|
||||||
|
// Magically chosen values
|
||||||
|
/** Maximal speed in km/h */
|
||||||
|
private static final double MAX_SPEED = 5;
|
||||||
|
/** Distance to reach maximal speed in km */
|
||||||
|
private static final double TWO_ACCELERATION_DISTANCE = 0.2;
|
||||||
|
|
||||||
|
private String[] line = new String[ScheduleFormat.NUMBER_COLUMNS];
|
||||||
|
|
||||||
|
private Iterator<TraceEntry> currentTrace;
|
||||||
|
private Iterator<List<StopEntry>> currentPath = Collections.emptyIterator();
|
||||||
|
private Iterator<StopEntry> currentSegmentStart = Collections.emptyIterator();
|
||||||
|
private Iterator<StopEntry> currentSegmentEnd = Collections.emptyIterator();
|
||||||
|
Map<StopEntry, Set<StopEntry>> lineSegments = new HashMap<>();
|
||||||
|
|
||||||
|
private StopEntry start = null;
|
||||||
|
private StopEntry end = null;
|
||||||
|
|
||||||
|
private boolean hasNext = false;
|
||||||
|
private boolean onNext = false;
|
||||||
|
|
||||||
|
/** Create the stream provider */
|
||||||
|
public CSVStreamSchedulesProvider(Iterator<TraceEntry> traces) {
|
||||||
|
this.currentTrace = traces;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (!this.onNext) {
|
||||||
|
skipToNext();
|
||||||
|
}
|
||||||
|
return this.hasNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipToNext() {
|
||||||
|
if (this.onNext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (!this.onNext) {
|
||||||
|
if (!this.currentSegmentEnd.hasNext()) {
|
||||||
|
skipToNextCandidatePath();
|
||||||
|
}
|
||||||
|
if (this.onNext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skipToNextNewSegment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipToNextNewSegment() {
|
||||||
|
do {
|
||||||
|
this.start = this.currentSegmentStart.next();
|
||||||
|
this.lineSegments.putIfAbsent(this.start, new HashSet<>());
|
||||||
|
this.end = this.currentSegmentEnd.next();
|
||||||
|
} while (this.lineSegments.get(this.start).contains(this.end)
|
||||||
|
&& this.currentSegmentEnd.hasNext());
|
||||||
|
if (!this.lineSegments.get(this.start).contains(this.end)) {
|
||||||
|
this.lineSegments.get(this.start).add(this.end);
|
||||||
|
this.onNext = true;
|
||||||
|
this.hasNext = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the reading head of path to the next one that has at least two
|
||||||
|
* elements
|
||||||
|
*/
|
||||||
|
private void skipToNextCandidatePath() {
|
||||||
|
currentSegmentStart = null;
|
||||||
|
do {
|
||||||
|
while (!this.currentPath.hasNext()) {
|
||||||
|
if (!this.currentTrace.hasNext()) {
|
||||||
|
this.hasNext = false;
|
||||||
|
this.onNext = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TraceEntry trace = this.currentTrace.next();
|
||||||
|
this.currentPath = trace.getPaths().iterator();
|
||||||
|
this.line[ScheduleFormat.LINE_INDEX] = trace.lname;
|
||||||
|
this.line[ScheduleFormat.TERMINUS_INDEX] = trace.getTerminus().get(0);
|
||||||
|
this.lineSegments.clear();
|
||||||
|
}
|
||||||
|
List<StopEntry> path = this.currentPath.next();
|
||||||
|
this.currentSegmentEnd = path.iterator();
|
||||||
|
if (this.currentSegmentEnd.hasNext()) {
|
||||||
|
this.currentSegmentEnd.next();
|
||||||
|
this.currentSegmentStart = path.iterator();
|
||||||
|
}
|
||||||
|
} while (currentSegmentStart == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] next() {
|
||||||
|
if (!this.onNext) {
|
||||||
|
skipToNext();
|
||||||
|
}
|
||||||
|
this.onNext = false;
|
||||||
|
|
||||||
|
this.line[ScheduleFormat.TIME_INDEX] = null;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this.line[ScheduleFormat.DISTANCE_INDEX] =
|
||||||
|
* NumberFormat.getInstance(Locale.ENGLISH)
|
||||||
|
* .format(distance);
|
||||||
|
* this.line[ScheduleFormat.VARIANT_INDEX] = Integer
|
||||||
|
* .toString(this.lineSegments.get(this.start).size() - 1);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return Arrays.copyOf(this.line, this.line.length);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package fr.u_paris.gla.project.idfm;
|
package fr.u_paris.gla.project.idfm;
|
||||||
|
|
||||||
|
@ -23,9 +23,11 @@ import org.json.JSONObject;
|
||||||
import fr.u_paris.gla.project.utils.CSVTools;
|
import fr.u_paris.gla.project.utils.CSVTools;
|
||||||
import fr.u_paris.gla.project.utils.GPS;
|
import fr.u_paris.gla.project.utils.GPS;
|
||||||
|
|
||||||
/** Code of an extractor for the data from IDF mobilite.
|
/**
|
||||||
*
|
* Code of an extractor for the data from IDF mobilite.
|
||||||
* @author Emmanuel Bigeon */
|
*
|
||||||
|
* @author Emmanuel Bigeon
|
||||||
|
*/
|
||||||
public class IDFMNetworkExtractor {
|
public class IDFMNetworkExtractor {
|
||||||
|
|
||||||
/** The logger for information on the process */
|
/** The logger for information on the process */
|
||||||
|
@ -37,14 +39,15 @@ public class IDFMNetworkExtractor {
|
||||||
private static final String STOPS_FILE_URL = "https://data.iledefrance-mobilites.fr/api/explore/v2.1/catalog/datasets/arrets-lignes/exports/csv?lang=fr&timezone=Europe%2FBerlin&use_labels=true&delimiter=%3B";
|
private static final String STOPS_FILE_URL = "https://data.iledefrance-mobilites.fr/api/explore/v2.1/catalog/datasets/arrets-lignes/exports/csv?lang=fr&timezone=Europe%2FBerlin&use_labels=true&delimiter=%3B";
|
||||||
|
|
||||||
// IDF mobilite csv formats
|
// IDF mobilite csv formats
|
||||||
private static final int IDFM_TRACE_ID_INDEX = 0;
|
private static final int IDFM_TRACE_ID_INDEX = 0;
|
||||||
private static final int IDFM_TRACE_SNAME_INDEX = 1;
|
private static final int IDFM_TRACE_SNAME_INDEX = 1;
|
||||||
private static final int IDFM_TRACE_SHAPE_INDEX = 6;
|
private static final int IDFM_TRACE_SHAPE_INDEX = 6;
|
||||||
|
|
||||||
private static final int IDFM_STOPS_RID_INDEX = 0;
|
private static final int IDFM_STOPS_RID_INDEX = 0;
|
||||||
|
private static final int IDFM_STOPS_SCHEDULES_INDEX = 3;
|
||||||
private static final int IDFM_STOPS_NAME_INDEX = 5;
|
private static final int IDFM_STOPS_NAME_INDEX = 5;
|
||||||
private static final int IDFM_STOPS_LON_INDEX = 6;
|
private static final int IDFM_STOPS_LON_INDEX = 6;
|
||||||
private static final int IDFM_STOPS_LAT_INDEX = 7;
|
private static final int IDFM_STOPS_LAT_INDEX = 7;
|
||||||
|
|
||||||
// Magically chosen values
|
// Magically chosen values
|
||||||
/** A number of stops on each line */
|
/** A number of stops on each line */
|
||||||
|
@ -53,14 +56,16 @@ public class IDFMNetworkExtractor {
|
||||||
// Well named constants
|
// Well named constants
|
||||||
private static final double QUARTER_KILOMETER = .25;
|
private static final double QUARTER_KILOMETER = .25;
|
||||||
|
|
||||||
/** Main entry point for the extractor of IDF mobilite data into a network as
|
/**
|
||||||
|
* Main entry point for the extractor of IDF mobilite data into a network as
|
||||||
* defined by this application.
|
* defined by this application.
|
||||||
*
|
*
|
||||||
* @param args the arguments (expected one for the destination file) */
|
* @param args the arguments (expected one for the destination file)
|
||||||
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
if (args.length != 1) {
|
if (args.length != 2) {
|
||||||
LOGGER.severe("Invalid command line. Missing target file.");
|
LOGGER.severe("Invalid command line. Missing target files.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +96,20 @@ public class IDFMNetworkExtractor {
|
||||||
LOGGER.log(Level.SEVERE, e,
|
LOGGER.log(Level.SEVERE, e,
|
||||||
() -> MessageFormat.format("Could not write in file {0}", args[0]));
|
() -> MessageFormat.format("Could not write in file {0}", args[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSVStreamSchedulesProvider providerschedules = new CSVStreamSchedulesProvider(traces.values().iterator());
|
||||||
|
|
||||||
|
TraceEntry tmp = traces.values().iterator().next();
|
||||||
|
tmp.getTerminus()
|
||||||
|
.forEach(m -> LOGGER.log(Level.INFO, m));
|
||||||
|
|
||||||
|
try {
|
||||||
|
CSVTools.writeCSVToFile(args[1], Stream.iterate(providerschedules.next(),
|
||||||
|
t -> providerschedules.hasNext(), t -> providerschedules.next()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, e,
|
||||||
|
() -> MessageFormat.format("Could not write in file {0}", args[1]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void cleanTraces(Map<String, TraceEntry> traces) {
|
private static void cleanTraces(Map<String, TraceEntry> traces) {
|
||||||
|
@ -134,6 +153,10 @@ public class IDFMNetworkExtractor {
|
||||||
Double.parseDouble(line[IDFM_STOPS_LON_INDEX]),
|
Double.parseDouble(line[IDFM_STOPS_LON_INDEX]),
|
||||||
Double.parseDouble(line[IDFM_STOPS_LAT_INDEX]));
|
Double.parseDouble(line[IDFM_STOPS_LAT_INDEX]));
|
||||||
String rid = line[IDFM_STOPS_RID_INDEX];
|
String rid = line[IDFM_STOPS_RID_INDEX];
|
||||||
|
if (traces.keySet().contains(rid)) {
|
||||||
|
extractTerminus(line[IDFM_STOPS_SCHEDULES_INDEX]).forEach(t -> traces.get(rid).addTerminus(t));
|
||||||
|
}
|
||||||
|
|
||||||
traces.computeIfPresent(rid,
|
traces.computeIfPresent(rid,
|
||||||
(String k, TraceEntry trace) -> addCandidate(trace, entry));
|
(String k, TraceEntry trace) -> addCandidate(trace, entry));
|
||||||
stops.add(entry);
|
stops.add(entry);
|
||||||
|
@ -191,4 +214,25 @@ public class IDFMNetworkExtractor {
|
||||||
}
|
}
|
||||||
return all;
|
return all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<String> extractTerminus(String pathsJSON) {
|
||||||
|
List<String> all = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
JSONArray schedules = new JSONArray(pathsJSON);
|
||||||
|
for (int i = 0; i < schedules.length(); i++) {
|
||||||
|
JSONObject stop = schedules.getJSONObject(i);
|
||||||
|
String terminus = stop.getString("from");
|
||||||
|
|
||||||
|
all.add(terminus);
|
||||||
|
}
|
||||||
|
} catch (
|
||||||
|
|
||||||
|
JSONException e) {
|
||||||
|
// Ignoring invalid element!
|
||||||
|
LOGGER.log(Level.FINE, e,
|
||||||
|
() -> MessageFormat.format("Invalid json element {0}", pathsJSON)); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
return all;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,27 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package fr.u_paris.gla.project.idfm;
|
package fr.u_paris.gla.project.idfm;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Representation of a transport line
|
/**
|
||||||
*
|
* Representation of a transport line
|
||||||
* @author Emmanuel Bigeon */
|
*
|
||||||
|
* @author Emmanuel Bigeon
|
||||||
|
*/
|
||||||
public final class TraceEntry {
|
public final class TraceEntry {
|
||||||
public final String lname;
|
public final String lname;
|
||||||
|
|
||||||
|
private List<String> terminus = new ArrayList<>();
|
||||||
private List<List<StopEntry>> paths = new ArrayList<>();
|
private List<List<StopEntry>> paths = new ArrayList<>();
|
||||||
|
|
||||||
/** Create a transport line.
|
/**
|
||||||
*
|
* Create a transport line.
|
||||||
* @param lname the name of the line */
|
*
|
||||||
|
* @param lname the name of the line
|
||||||
|
*/
|
||||||
public TraceEntry(String lname) {
|
public TraceEntry(String lname) {
|
||||||
super();
|
super();
|
||||||
this.lname = lname;
|
this.lname = lname;
|
||||||
|
@ -29,7 +34,16 @@ public final class TraceEntry {
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return the list of terminus */
|
||||||
|
public List<String> getTerminus() {
|
||||||
|
return terminus;
|
||||||
|
}
|
||||||
|
|
||||||
public void addPath(List<StopEntry> path) {
|
public void addPath(List<StopEntry> path) {
|
||||||
paths.add(new ArrayList<>(path));
|
paths.add(new ArrayList<>(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addTerminus(String term) {
|
||||||
|
terminus.add(term);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package fr.u_paris.gla.project.io;
|
package fr.u_paris.gla.project.io;
|
||||||
|
|
||||||
|
@ -7,30 +7,36 @@ import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.ResolverStyle;
|
import java.time.format.ResolverStyle;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** A tool class for the schedule format.
|
/**
|
||||||
*
|
* A tool class for the schedule format.
|
||||||
* @author Emmanuel Bigeon */
|
*
|
||||||
|
* @author Emmanuel Bigeon
|
||||||
|
*/
|
||||||
public final class ScheduleFormat {
|
public final class ScheduleFormat {
|
||||||
public static final int LINE_INDEX = 0;
|
public static final int NUMBER_COLUMNS = 4;
|
||||||
|
|
||||||
|
public static final int LINE_INDEX = 0;
|
||||||
public static final int TRIP_SEQUENCE_INDEX = 1;
|
public static final int TRIP_SEQUENCE_INDEX = 1;
|
||||||
public static final int TERMINUS_INDEX = 2;
|
public static final int TERMINUS_INDEX = 2;
|
||||||
public static final int TIME_INDEX = 3;
|
public static final int TIME_INDEX = 3;
|
||||||
|
|
||||||
/** Hidden constructor for tool class */
|
/** Hidden constructor for tool class */
|
||||||
private ScheduleFormat() {
|
private ScheduleFormat() {
|
||||||
// Tool class
|
// Tool class
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read a trip sequence from its string representation
|
/**
|
||||||
*
|
* Read a trip sequence from its string representation
|
||||||
|
*
|
||||||
* @param representation the representation
|
* @param representation the representation
|
||||||
* @return the sequence of branching */
|
* @return the sequence of branching
|
||||||
|
*/
|
||||||
public static List<Integer> getTripSequence(String representation) {
|
public static List<Integer> getTripSequence(String representation) {
|
||||||
// TODO Read a trip sequence from a string
|
// TODO Read a trip sequence from a string
|
||||||
|
|
||||||
throw new RuntimeException("Not implemented yet");
|
throw new RuntimeException("Not implemented yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DateTimeFormatter getTimeFormatter() {
|
public static DateTimeFormatter getTimeFormatter() {
|
||||||
return DateTimeFormatter.ofPattern("HH:mm").withResolverStyle(ResolverStyle.LENIENT);
|
return DateTimeFormatter.ofPattern("HH:mm").withResolverStyle(ResolverStyle.LENIENT);
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue