path = this.currentPath.next();
+ this.currentSegmentEnd = path.iterator();
+ if (this.currentSegmentEnd.hasNext()) {
+ this.currentSegmentEnd.next();
+ this.currentSegmentStart = path.iterator();
+ }
+ } while (currentSegmentStart == null);
+ }
+
+ /** Store current trace' data as a String array
+ * @return The newly generated line of text
+ */
+ public String[] next() {
+ if (!this.onNext) {
+ skipToNext();
+ }
+ this.onNext = false;
+
+ fillStation(this.start, this.line, NetworkFormat.START_INDEX);
+ fillStation(this.end, this.line, NetworkFormat.STOP_INDEX);
+ double distance = GPS.distance(this.start.latitude, this.start.longitude,
+ this.end.latitude, this.end.longitude);
+ this.line[NetworkFormat.DISTANCE_INDEX] = NumberFormat.getInstance(Locale.ENGLISH)
+ .format(distance);
+ this.line[NetworkFormat.DURATION_INDEX] = formatTime(
+ (long) Math.ceil(distanceToTime(distance) * SECONDS_IN_HOURS));
+ int bifurcation = this.lineSegments.get(this.start).size() - 1;
+ this.line[NetworkFormat.VARIANT_INDEX] = Integer
+ .toString(bifurcation);
+ fillTransports(bifurcation);
+ return Arrays.copyOf(this.line, this.line.length);
+ // return new String[][]{Arrays.copyOf(this.line, this.line.length)};
+
+ }
+
+ /** creates adds a station into the next line String
+ * @param stop the stop
+ * @param nextLine the next line
+ * @param index the stop index in the next line */
+ private static void fillStation(StopEntry stop, String[] nextLine, int index) {
+ nextLine[index] = stop.lname;
+ nextLine[index + 1] = MessageFormat.format("{0}, {1}", //$NON-NLS-1$
+ GPS_FORMATTER.format(stop.latitude),
+ GPS_FORMATTER.format(stop.longitude));
+
+ }
+
+ /** turns a number into a formatted time string
+ * @param time the time value
+ * @return the time as a String */
+ private static String formatTime(long time) {
+ return MessageFormat.format("{0}:{1}", //$NON-NLS-1$
+ MINUTES_SECOND_FORMATTER.format(time / SECONDS_IN_MINUTES), MINUTES_SECOND_FORMATTER.format(time % SECONDS_IN_MINUTES));
+ }
+
+ /** A tool method to give a delay to go through a certain distance.
+ *
+ * This is a model with an linear acceleration and deceleration periods and a
+ * constant speed in between.
+ *
+ * @param distance the distance (in km)
+ * @return the duration of the trip (in hours) */
+ private static double distanceToTime(double distance) {
+ return Math.max(0, distance - TWO_ACCELERATION_DISTANCE) / MAX_SPEED
+ + Math.pow(Math.min(distance, TWO_ACCELERATION_DISTANCE) / MAX_SPEED, 2);
+ }
+
+ private void fillTransports(int bif) {
+ if(transports != null){
+ String nameTransport = this.line[NetworkFormat.LINE_INDEX];
+ String start_p = this.line[NetworkFormat.START_INDEX];
+ String end_p = this.line[NetworkFormat.STOP_INDEX];
+ // String bifurcation = this.line[NetworkFormat.VARIANT_INDEX];
+
+ Transport transp = null;
+ if(!transports.containsKey(traceId)){
+ transp = new Transport(nameTransport,traceType, url_image);
+ transports.put(traceId, transp);
+ }else{
+ transp = transports.get(traceId);
+ }
+ transp.addStop(start_p, end_p, bif);
+ if(transp.descriptions.isEmpty()){
+ transp.addDescriptions(descriptions);
+ }
+ }
+
+ }
+
+
+}
diff --git a/src/main/java/fr/u_paris/gla/project/idfm/IDFMNetworkExtractor.java b/src/main/java/fr/u_paris/gla/project/idfm/IDFMNetworkExtractor.java
index 50d4834..d14fb82 100644
--- a/src/main/java/fr/u_paris/gla/project/idfm/IDFMNetworkExtractor.java
+++ b/src/main/java/fr/u_paris/gla/project/idfm/IDFMNetworkExtractor.java
@@ -1,325 +1,432 @@
/**
- *
+ *
*/
package fr.u_paris.gla.project.idfm;
-import java.io.BufferedReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.text.Format;
-import java.text.MessageFormat;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.function.BiFunction;
-import java.util.function.Consumer;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
+import fr.u_paris.gla.project.utils.CSVTools;
+import fr.u_paris.gla.project.utils.GPS;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import com.opencsv.CSVParserBuilder;
-import com.opencsv.CSVReader;
-import com.opencsv.CSVReaderBuilder;
-import com.opencsv.CSVWriterBuilder;
-import com.opencsv.ICSVParser;
-import com.opencsv.ICSVWriter;
-import com.opencsv.exceptions.CsvValidationException;
+import java.io.IOException;
+import java.io.File;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Stream;
-import fr.u_paris.gla.project.io.NetworkFormat;
-import fr.u_paris.gla.project.utils.GPS;
-
-/** Code of an extractor for the data from IDF mobilite.
- *
- * @author Emmanuel Bigeon */
+/**
+ * Code of an extractor for the data from IDF mobilite.
+ *
+ * @author Emmanuel Bigeon
+ */
public class IDFMNetworkExtractor {
- /** The logger for information on the process */
+
+ /**
+ * The logger for information on the process
+ */
private static final Logger LOGGER = Logger
.getLogger(IDFMNetworkExtractor.class.getName());
+ /**
+ * the URL of the Trace CSV
+ */
// IDF mobilite API URLs
private static final String TRACE_FILE_URL = "https://data.iledefrance-mobilites.fr/api/explore/v2.1/catalog/datasets/traces-des-lignes-de-transport-en-commun-idfm/exports/csv?lang=fr&timezone=Europe%2FBerlin&use_labels=true&delimiter=%3B";
+ /**
+ * The URL of the Stops CSV
+ */
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";
+ /**
+ * the index in the CSV of a Trace's ID
+ */
// IDF mobilite csv formats
- private static final int IDFM_TRACE_ID_INDEX = 0;
+ private static final int IDFM_TRACE_ID_INDEX = 0;
+ /**
+ * the index in the CSV of a Trace's Name
+ */
private static final int IDFM_TRACE_SNAME_INDEX = 1;
+ /**
+ * the index in the CSV of a Trace's shape
+ */
private static final int IDFM_TRACE_SHAPE_INDEX = 6;
+ private static final int IDFM_TRACE_TYPE_INDEX = 3;
- private static final int IDFM_STOPS_RID_INDEX = 0;
+ /**
+ * The index in the CSV of the Stops' id
+ */
+ private static final int IDFM_STOPS_RID_INDEX = 0;
+ /**
+ * The index in the CSV of the Stops' schedules
+ */
+ private static final int IDFM_STOPS_SCHEDULES_INDEX = 3;
+ /**
+ * The index in the CSV of the Stops' names
+ */
private static final int IDFM_STOPS_NAME_INDEX = 5;
- private static final int IDFM_STOPS_LON_INDEX = 6;
- private static final int IDFM_STOPS_LAT_INDEX = 7;
+ /**
+ * The index in the CSV of the Stops' longitude
+ */
+ private static final int IDFM_STOPS_LON_INDEX = 6;
+ /**
+ * The index in the CSV of the Stops' latitude
+ */
+ private static final int IDFM_STOPS_LAT_INDEX = 7;
+ private static final int IDFM_URL_INDEX = 10;
+
+ private static final String TRACE_FILE_NAME = "trace.csv";
+
+ private static final String HOURS_FILE_NAME = "hours.csv";
+
+ private static final String IMAGES_FILE_NAME = "./images.csv";
// Magically chosen values
- /** A number of stops on each line */
- private static final int GUESS_STOPS_BY_LINE = 5;
- /** Maximal speed in km/h */
- private static final double MAX_SPEED = 5;
- /** Distance to reach maximal speed in km */
- private static final double ACCELERATION_DISTANCE = 0.1;
+ /**
+ * A number of stops on each line
+ */
+ private static final int GUESS_STOPS_BY_LINE = 5;
+ /**
+ * The quarter of a kilometer as a static value
+ */
// Well named constants
- private static final double _250_METERS = .25;
- private static final long SECONDS_IN_HOURS = 3_600;
+ private static final double QUARTER_KILOMETER = .25;
- private static final Format GPS_FORMATTER = NetworkFormat.getGPSFormatter();
-
- public static class StopEntry {
- private String lname;
- public final double longitude;
- public final double latitude;
-
- /** Create the stop
- *
- * @param lname
- * @param longitude
- * @param latitude */
- public StopEntry(String lname, double longitude, double latitude) {
- super();
- this.lname = lname;
- this.longitude = longitude;
- this.latitude = latitude;
- }
-
- @Override
- public String toString() {
- return MessageFormat.format("{0} [{1}, {2}]", this.lname, this.longitude,
- this.latitude);
- }
- }
-
- public static final class UnidentifiedStopEntry extends StopEntry {
- /** Create the stop
- *
- * @param longitude
- * @param latitude */
- public UnidentifiedStopEntry(double longitude, double latitude) {
- super("Unidentified", longitude, latitude);
- }
-
- List candidates = new ArrayList<>();
-
- @Override
- public String toString() {
- return "UnidentifiedStop [candidates=" + this.candidates + "]";
- }
- }
-
- private static final class TraceEntry {
- String lname;
- List> stops = new ArrayList<>();
- }
-
- public static void readCSVFromURL(String url, Consumer contentLineConsumer)
- throws IOException {
- ICSVParser parser = new CSVParserBuilder().withSeparator(';').build();
- try (InputStream is = new URL(url).openStream();
- Reader reader = new BufferedReader(
- new InputStreamReader(is, StandardCharsets.UTF_8))) {
- CSVReaderBuilder csvBuilder = new CSVReaderBuilder(reader)
- .withCSVParser(parser);
- try (CSVReader csv = csvBuilder.build()) {
- String[] line = csv.readNextSilently(); // Eliminate header
- while (csv.peek() != null) {
- line = csv.readNext();
- contentLineConsumer.accept(line);
- }
- }
- } catch (CsvValidationException e) {
- throw new IOException("Invalid csv file for lines", e);
- }
- }
-
- /** Main entry point for the extractor of IDF mobilite data into a network as
+ /**
+ * Main entry point for the extractor of IDF mobilité data into a network as
* 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) {
- if (args.length != 1) {
- LOGGER.severe("Invalid command line. Missing target file.");
+ if (args.length != 2) {
+ LOGGER.severe("Invalid command line. Missing target files.");
return;
}
Map traces = new HashMap<>();
try {
- readCSVFromURL(TRACE_FILE_URL, (String[] line) -> {
- TraceEntry entry = new TraceEntry();
- entry.lname = line[IDFM_TRACE_SNAME_INDEX];
- List> buildPaths = buildPaths(
- line[IDFM_TRACE_SHAPE_INDEX]);
- entry.stops.addAll(buildPaths);
- if (buildPaths.isEmpty()) {
- LOGGER.severe(() -> MessageFormat.format(
- "Line {0} has no provided itinerary and was ignored",
- entry.lname));
- } else {
- traces.put(line[IDFM_TRACE_ID_INDEX], entry);
- }
- });
+ CSVTools.readCSVFromURL(TRACE_FILE_URL,
+ (String[] line) -> addLine(line, traces));
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Error while reading the line paths", e);
}
List stops = new ArrayList<>(traces.size() * GUESS_STOPS_BY_LINE);
try {
- readCSVFromURL(STOPS_FILE_URL, (String[] line) -> {
- StopEntry entry = new StopEntry(line[IDFM_STOPS_NAME_INDEX],
- Double.parseDouble(line[IDFM_STOPS_LON_INDEX]),
- Double.parseDouble(line[IDFM_STOPS_LAT_INDEX]));
- String rid = line[IDFM_STOPS_RID_INDEX];
- BiFunction super String, ? super TraceEntry, ? extends TraceEntry> func = (
- k, trace) -> {
- for (List path : trace.stops) {
- for (StopEntry stopEntry : path) {
- if (stopEntry instanceof UnidentifiedStopEntry
- && GPS.distance(entry.latitude, entry.longitude,
- stopEntry.latitude,
- stopEntry.longitude) < _250_METERS) {
- ((UnidentifiedStopEntry) stopEntry).candidates.add(entry);
- }
- }
- }
- return trace;
- };
- traces.computeIfPresent(rid, func);
- stops.add(entry);
- });
+ CSVTools.readCSVFromURL(STOPS_FILE_URL,
+ (String[] line) -> addStop(line, traces, stops));
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Error while reading the stops", e);
}
+ cleanTraces(traces);
+
+ Map transports = new HashMap<>();
+ CSVStreamProvider provider = new CSVStreamProvider(traces.values().iterator(), transports);
+
+ // Write into args[0]
+ try {
+ CSVTools.writeCSVToFile(args[0], Stream.iterate(provider.next(),
+ t -> provider.hasNext(), t -> provider.next()));
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, e,
+ () -> 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));
+
+ // Write into args[1]
+ 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]));
+ }*/
+ // System.out.println("****** END ******");
+ // System.out.println(transports.size());
+ // System.out.println(transports.get("IDFM:C01371").name);
+ // System.out.println(transports.get("IDFM:C02060").name);
+ // Transport ligne_1 = transports.get("IDFM:C01371");
+ // Transport ligne_7 = transports.get("IDFM:C01377");
+ // Transport rerd = transports.get("IDFM:C01728");
+ // Transport b54b = transports.get("IDFM:C00940");
+
+
+ // System.out.println("****** AFFICHAGE LIGNE ******");
+
+ // Stop maisonBlanche = ligne_7.stopsMap.get("Maison Blanche");
+ // System.out.println(maisonBlanche.name);
+
+ // for (BifStop nextEntry : maisonBlanche.connected.values()) {
+ // System.out.println(nextEntry.bifurc+ nextEntry.stop.name);
+ // }
+ // System.out.println("****** AFFICHAGE LIGNE ******");
+
+ // Stop corientin = ligne_7.stopsMap.get("Corentin Cariou");
+ // System.out.println(corientin.name);
+
+ // for (BifStop nextEntry : corientin.connected.values()) {
+ // System.out.println(nextEntry.bifurc+ nextEntry.stop.name);
+ // }
+ // System.out.println("***************************");
+ // System.out.println("****** AFFICHAGE Description ******");
+ // System.out.println(traces.get("IDFM:C01377").descriptions);
+ // System.out.println("****** AFFICHAGE Description False ******");
+ // System.out.println(ligne_7.descriptions);
+ // System.out.println("****************** Build la path ***********************");
+ // System.out.println(ligne_7.type);
+ // System.out.println(rerd.type);
+ // ligne_7.buildBifurcation();
+ // rerd.buildBifurcation();
+ // System.out.println("******************Derniere description ***********************");
+ // System.out.println(ligne_7.descriptions);
+ // System.out.println("******************Description 54B ************************");
+ // b54b.buildBifurcation();
+ // System.out.println(b54b.descriptions);
+ System.out.println("******************Building bifurcations ************************");
+ long startTime = System.currentTimeMillis();
+
+ for (Transport entry : transports.values()) {
+ entry.buildBifurcationOptimzed();
+ }
+
+ long endTime = System.currentTimeMillis();
+ long tempsPasse = endTime - startTime;
+
+ long minutes = (tempsPasse / 1000) / 60;
+ long seconds = (tempsPasse / 1000) % 60;
+ long milliseconds = tempsPasse % 1000;
+
+ System.out.println("Temps écoulé : " + minutes + " minutess, " + seconds + " secndes et " + milliseconds + " millis");
+
+ System.out.println("******************Fin Building bifurcations ************************");
+
+ CSVSchedulesProvider providerschedules = new CSVSchedulesProvider(transports.values().iterator());
+ 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]));
+ }
+
+ CSVImageProvider providerimage = new CSVImageProvider(transports.values().iterator());
+ String imageCSV = "image.csv";
+ try {
+ CSVTools.writeCSVToFile(imageCSV, Stream.iterate(providerimage.next(),
+ t -> providerimage.hasNext(), t -> providerimage.next()));
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, e,
+ () -> MessageFormat.format("Could not write in file {0}", imageCSV));
+ }
+ }
+
+ public static boolean checkFileExistence(String filePath) {
+ File file = new File(filePath);
+ if (file.exists()) {
+ LOGGER.severe(filePath+ " already exists.");
+ return true;
+ } else {
+ LOGGER.severe(filePath + " does not exist.");
+ return false;
+ }
+ }
+
+ public static void builFiles() {
+
+ if (checkFileExistence("./"+HOURS_FILE_NAME) && checkFileExistence("./"+TRACE_FILE_NAME)) {
+ LOGGER.severe("Files already exists.");
+ return;
+ }
+
+ Map traces = new HashMap<>();
+ try {
+ CSVTools.readCSVFromURL(TRACE_FILE_URL,
+ (String[] line) -> addLine(line, traces));
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, "Error while reading the line paths", e);
+ }
+
+ List stops = new ArrayList<>(traces.size() * GUESS_STOPS_BY_LINE);
+ try {
+ CSVTools.readCSVFromURL(STOPS_FILE_URL,
+ (String[] line) -> addStop(line, traces, stops));
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, "Error while reading the stops", e);
+ }
+
+ cleanTraces(traces);
+
+ Map transports = new HashMap<>();
+ CSVStreamProvider provider = new CSVStreamProvider(traces.values().iterator(), transports);
+
+ // Write into args[0]
+ try {
+ CSVTools.writeCSVToFile(TRACE_FILE_NAME, Stream.iterate(provider.next(),
+ t -> provider.hasNext(), t -> provider.next()));
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, e,
+ () -> MessageFormat.format("Could not write in file {0}", TRACE_FILE_NAME));
+ }
+
+ System.out.println("******************Building bifurcations ************************");
+ long startTime = System.currentTimeMillis();
+
+ for (Transport entry : transports.values()) {
+ entry.buildBifurcationOptimzed();
+ }
+
+ long endTime = System.currentTimeMillis();
+ long tempsPasse = endTime - startTime;
+
+ long minutes = (tempsPasse / 1000) / 60;
+ long seconds = (tempsPasse / 1000) % 60;
+ long milliseconds = tempsPasse % 1000;
+
+ System.out.println("Temps écoulé : " + minutes + " minutess, " + seconds + " secndes et " + milliseconds + " millis");
+
+ System.out.println("******************Fin Building bifurcations ************************");
+
+ CSVSchedulesProvider providerschedules = new CSVSchedulesProvider(transports.values().iterator());
+ try {
+ CSVTools.writeCSVToFile(HOURS_FILE_NAME, 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}", HOURS_FILE_NAME));
+ }
+
+ CSVImageProvider providerimage = new CSVImageProvider(transports.values().iterator());
+ try {
+ CSVTools.writeCSVToFile(IMAGES_FILE_NAME, Stream.iterate(providerimage.next(),
+ t -> providerimage.hasNext(), t -> providerimage.next()));
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, e,
+ () -> MessageFormat.format("Could not write in file {0}", IMAGES_FILE_NAME));
+ }
+ }
+
+
+ /** Clean the traces/remove the unresolved lines
+ * @param traces the traces to clean
+ */
+ private static void cleanTraces(Map traces) {
Set toRemove = new HashSet<>();
for (Entry traceEntry : traces.entrySet()) {
TraceEntry trace = traceEntry.getValue();
- for (List path : trace.stops) {
- for (int i = 0; i < path.size(); i++) {
- StopEntry stop = path.get(i);
- if (stop instanceof UnidentifiedStopEntry)
- stop = resolve((UnidentifiedStopEntry) stop);
- if (stop instanceof UnidentifiedStopEntry
- && ((UnidentifiedStopEntry) stop).candidates.isEmpty()) {
- LOGGER.severe("Missing stop for line " + trace.lname
- + ". Line will be removed");
- toRemove.add(traceEntry.getKey());
- continue;
- }
- path.set(i, stop);
- }
+ if (!cleanLine(trace.getPaths())) {
+ LOGGER.severe(() -> MessageFormat.format(
+ "Missing stop for line {0}. Line will be removed", trace.lname));
+ toRemove.add(traceEntry.getKey());
}
}
-
for (String string : toRemove) {
traces.remove(string);
}
+ }
- // Export content in required format
- try (FileWriter writer = new FileWriter(args[0], StandardCharsets.UTF_8)) {
- CSVWriterBuilder wBuilder = new CSVWriterBuilder(writer).withSeparator(';');
- try (ICSVWriter csv = wBuilder.build()) {
- for (Entry traceEntry : traces.entrySet()) {
- Map> lineSegments = new HashMap<>();
- String[] nextLine = new String[NetworkFormat.NUMBER_COLUMNS];
- nextLine[NetworkFormat.LINE_INDEX] = traceEntry.getValue().lname;
- for (List path : traceEntry.getValue().stops) {
- for (int i = 0; i < path.size() - 1; i++) {
- StopEntry stop1 = path.get(i);
- lineSegments.putIfAbsent(stop1, new HashSet<>());
- StopEntry stop2 = path.get(i + 1);
- if (!lineSegments.get(stop1).contains(stop2)) {
- fillStation(stop1, nextLine, NetworkFormat.START_INDEX);
- fillStation(stop2, nextLine, NetworkFormat.STOP_INDEX);
- double distance = GPS.distance(stop1.latitude,
- stop1.longitude, stop2.latitude, stop2.longitude);
- nextLine[NetworkFormat.DISTANCE_INDEX] = NumberFormat
- .getInstance(Locale.ENGLISH).format(distance);
- nextLine[NetworkFormat.DURATION_INDEX] = formatTime(
- (long) Math.ceil(distanceToTime(distance)
- * SECONDS_IN_HOURS));
- nextLine[NetworkFormat.VARIANT_INDEX] = Integer
- .toString(lineSegments.get(stop1).size());
- csv.writeNext(nextLine);
- lineSegments.get(stop1).add(stop2);
- }
- }
- }
+ /** Tells if the current trasport line has all its stops entries resolved
+ * @param stops the stops list
+ * @return if the line is "clean"*/
+ private static boolean cleanLine(List> stops) {
+ for (List path : stops) {
+ for (int i = 0; i < path.size(); i++) {
+ StopEntry stop = path.get(i);
+ if (!(stop instanceof UnidentifiedStopEntry unidentified)) {
+ continue;
+ }
+ StopEntry stopResolution = unidentified.resolve();
+ if (stopResolution == null) {
+ return false;
+ }
+ path.set(i, stopResolution);
+ }
+ }
+ return true;
+ }
+ /** adds a stop to all related variables
+ * @param line the transport line involved with the new stop
+ * @param traces the traces related to it
+ * @param stops the general stops list
+ */
+ private static void addStop(String[] line, Map traces,
+ List stops) {
+ StopEntry entry = new StopEntry(line[IDFM_STOPS_NAME_INDEX],
+ Double.parseDouble(line[IDFM_STOPS_LON_INDEX]),
+ Double.parseDouble(line[IDFM_STOPS_LAT_INDEX]));
+ String rid = line[IDFM_STOPS_RID_INDEX];
+
+ //Add traces description if it's empty
+ if (traces.containsKey(rid)) {
+ TraceEntry tmp = traces.get(rid);
+ if (tmp.isDescriptionEmpty()) {
+ List descriptions = extractDescription(line[IDFM_STOPS_SCHEDULES_INDEX]);
+ tmp.addDescriptions(descriptions);
+ }
+ }
+
+
+ // Add terminus to the traces
+ if (traces.containsKey(rid)) {
+ extractTerminus(line[IDFM_STOPS_SCHEDULES_INDEX]).forEach(t -> traces.get(rid).addTerminus(t));
+ }
+
+ traces.computeIfPresent(rid,
+ (String k, TraceEntry trace) -> addCandidate(trace, entry));
+ stops.add(entry);
+ }
+
+ /** add a line to the related list of traces
+ * @param line the line as a string
+ * @param traces the traces
+ */
+ private static void addLine(String[] line, Map traces) {
+ TraceEntry entry = new TraceEntry(line[IDFM_TRACE_SNAME_INDEX], line[IDFM_TRACE_ID_INDEX],line[IDFM_TRACE_TYPE_INDEX], line[IDFM_URL_INDEX]);
+ List> buildPaths = buildPaths(line[IDFM_TRACE_SHAPE_INDEX]);
+ entry.getPaths().addAll(buildPaths);
+ if (buildPaths.isEmpty()) {
+ LOGGER.severe(() -> MessageFormat.format(
+ "Line {0} has no provided itinerary and was ignored", entry.lname));
+ } else {
+ traces.put(line[IDFM_TRACE_ID_INDEX], entry);
+ }
+ }
+
+ /** add a new entry as a candidate to a trace
+ * @param trace the trace
+ * @param entry the entry
+ * @return the trace in question
+ */
+ private static TraceEntry addCandidate(TraceEntry trace, StopEntry entry) {
+ for (List path : trace.getPaths()) {
+ for (StopEntry stopEntry : path) {
+ if (stopEntry instanceof UnidentifiedStopEntry unidentified
+ && GPS.distance(entry.latitude, entry.longitude,
+ stopEntry.latitude,
+ stopEntry.longitude) < QUARTER_KILOMETER) {
+ unidentified.addCandidate(entry);
}
}
- } catch (IOException e) {
- LOGGER.log(Level.SEVERE, e, () -> "Could not write in file " + args[1]);
}
+ return trace;
}
- /** @param distanceToTime
- * @return */
- private static String formatTime(long time) {
- NumberFormat format = NumberFormat.getInstance(Locale.ENGLISH);
- format.setMinimumIntegerDigits(2);
- return MessageFormat.format("{0}:{1}", format.format(time / 60),
- format.format(time % 60));
- }
-
- /** A tool method to give a delay to go through a certain distance.
- *
- * This is a model with an linear acceleration and deceleration periods and a
- * constant speed in between.
- *
- * @param distance the distance (in km)
- * @return the duration of the trip (in hours) */
- private static double distanceToTime(double distance) {
- return Math.max(0, distance - 2 * ACCELERATION_DISTANCE) / MAX_SPEED
- + Math.pow(Math.min(distance, 2 * ACCELERATION_DISTANCE) / MAX_SPEED, 2);
- }
-
- /** @param stop1
- * @param nextLine
- * @param i */
- private static void fillStation(StopEntry stop, String[] nextLine, int index) {
- nextLine[index] = stop.lname;
- nextLine[index + 1] = MessageFormat.format("{0}, {1}",
- GPS_FORMATTER.format(stop.latitude),
- GPS_FORMATTER.format(stop.longitude));
-
- }
-
- /** @param stop
- * @return */
- private static StopEntry resolve(UnidentifiedStopEntry stop) {
-
- if (stop.candidates.isEmpty()) {
- LOGGER.severe("Unable to find stop name, will use a placeholder");
- return stop;
- }
- if (stop.candidates.size() == 1) {
- return stop.candidates.get(0);
- }
- Collections.sort(stop.candidates,
- (Comparator super StopEntry>) (StopEntry s1,
- StopEntry s2) -> (int) Math.signum((GPS.distance(stop.latitude,
- stop.longitude, s1.latitude, s1.longitude)
- - GPS.distance(stop.latitude, stop.longitude, s2.latitude,
- s2.longitude))));
-
- return stop.candidates.get(0);
- }
-
+ /** turn a JSON list of stops into a list of paths
+ * @param pathsJSON the JSON String of all paths
+ * @return the paths as a List of StopEntries
+ */
private static List> buildPaths(String pathsJSON) {
List> all = new ArrayList<>();
try {
@@ -341,8 +448,56 @@ public class IDFMNetworkExtractor {
}
} catch (JSONException e) {
// Ignoring invalid element!
- LOGGER.log(Level.FINE, "Invalid json element " + pathsJSON, e); //$NON-NLS-1$
+ LOGGER.log(Level.FINE, e,
+ () -> MessageFormat.format("Invalid json element {0}", pathsJSON)); //$NON-NLS-1$
}
return all;
}
+
+ /** extract the terminus out of a JSON file
+ * @param JSON the JSON
+ * @return a list of strings related to the terminus
+ */
+ private static List extractTerminus(String JSON) {
+ List all = new ArrayList<>();
+ try {
+ JSONArray schedules = new JSONArray(JSON);
+ 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}", JSON)); //$NON-NLS-1$
+ }
+
+ return all;
+ }
+
+ private static List extractDescription(String JSON) {
+ List all = new ArrayList<>();
+ try {
+ JSONArray schedules = new JSONArray(JSON);
+ for (int i = 0; i < schedules.length(); i++) {
+ JSONObject stop = schedules.getJSONObject(i);
+ String from = stop.getString("from");
+ String to = stop.getString("to");
+ String first = stop.getString("first");
+ String last = stop.getString("last");
+ //We skip the lines where from equals to
+ // if(from.compareTo(to) != 0){
+ all.add(new TraceDescription(from, to, first, last));
+ // }
+ }
+ } catch (JSONException e) {
+ // Ignoring invalid element!
+ // e.printStackTrace();
+ }
+
+ return all;
+ }
}
diff --git a/src/main/java/fr/u_paris/gla/project/idfm/Stop.java b/src/main/java/fr/u_paris/gla/project/idfm/Stop.java
new file mode 100644
index 0000000..0a0a2fa
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/idfm/Stop.java
@@ -0,0 +1,48 @@
+package fr.u_paris.gla.project.idfm;
+
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * A representation of a stop with its connected stop and which bifurcation
+ * is needed to go to each connected stop
+ */
+public class Stop {
+
+ Map connected = new HashMap<>();
+ public String name;
+
+ public Stop(String name){
+ this.name = name;
+ }
+
+ /**
+ * Checks is stopName is connected to this one
+ * @param stopName
+ * @return True if stopName is connected to the current stop
+ */
+ public boolean isStopConnected(String stopName) {
+ return connected.containsKey(stopName);
+ }
+
+ /**
+ * Add Connected stop
+ * @param stop connected stop with the bifurcation needed
+ */
+ public void addConnectedStop(BifStop stop) {
+ connected.put(stop.stop.name, stop);
+ }
+
+ /**
+ * Return the connected stop with the name : stopName
+ * @param stopName
+ * @return the connected stop with the name : stopName
+ */
+ public BifStop getConnectedStop(String stopName) {
+ return connected.get(stopName);
+ }
+
+}
+
diff --git a/src/main/java/fr/u_paris/gla/project/idfm/StopEntry.java b/src/main/java/fr/u_paris/gla/project/idfm/StopEntry.java
new file mode 100644
index 0000000..0b1685f
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/idfm/StopEntry.java
@@ -0,0 +1,71 @@
+/**
+ *
+ */
+package fr.u_paris.gla.project.idfm;
+
+import java.text.MessageFormat;
+import java.util.Objects;
+
+/** A transport stop data.
+ *
+ * @author Emmanuel Bigeon */
+public class StopEntry implements Comparable {
+ public final String lname;
+ public final double longitude;
+ public final double latitude;
+
+ /** Create the stop
+ *
+ * @param lname
+ * @param longitude
+ * @param latitude */
+ public StopEntry(String lname, double longitude, double latitude) {
+ super();
+ this.lname = lname;
+ this.longitude = longitude;
+ this.latitude = latitude;
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormat.format("{0} [{1}, {2}]", this.lname, this.longitude, //$NON-NLS-1$
+ this.latitude);
+ }
+
+ @Override
+ public int compareTo(StopEntry o) {
+ if (latitude < o.latitude) {
+ return -1;
+ }
+ if (latitude > o.latitude) {
+ return 1;
+ }
+ if (longitude < o.longitude) {
+ return -1;
+ }
+ if (longitude > o.longitude) {
+ return 1;
+ }
+ return lname.compareTo(o.lname);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(latitude, lname, longitude);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ StopEntry other = (StopEntry) obj;
+ return Double.doubleToLongBits(latitude) == Double
+ .doubleToLongBits(other.latitude) && Objects.equals(lname, other.lname)
+ && Double.doubleToLongBits(longitude) == Double
+ .doubleToLongBits(other.longitude);
+ }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/idfm/TraceDescription.java b/src/main/java/fr/u_paris/gla/project/idfm/TraceDescription.java
new file mode 100644
index 0000000..2de7424
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/idfm/TraceDescription.java
@@ -0,0 +1,29 @@
+package fr.u_paris.gla.project.idfm;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A representation of a transport description encompansing its first and last
+ * stop in all of its direction, the first and last schedule and all the
+ * bifurcation that direction takes. The description comes from the fourth column * of the stop csv file.
+ */
+public class TraceDescription {
+ public String from;
+ public String to;
+ public String first;
+ public String last;
+ List bifurcation = new ArrayList<>();
+
+ public TraceDescription(String from,String to, String first, String last){
+ this.from = from;
+ this.to = to;
+ this.first = first;
+ this.last = last;
+ }
+
+ @Override
+ public String toString() {
+ return "From: " + from + ", To: " + to + ", First: " + first + ", Last: " + last + ", Bifurcation: " + bifurcation;
+ }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/idfm/TraceEntry.java b/src/main/java/fr/u_paris/gla/project/idfm/TraceEntry.java
new file mode 100644
index 0000000..8891fb8
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/idfm/TraceEntry.java
@@ -0,0 +1,71 @@
+/**
+ *
+ */
+package fr.u_paris.gla.project.idfm;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Representation of a transport line
+ *
+ * @author Emmanuel Bigeon
+ */
+public final class TraceEntry {
+ public final String lname;
+ public final String id;
+ public final String type;
+ public final String url;
+
+
+
+ private List terminus = new ArrayList<>();
+ private List> paths = new ArrayList<>();
+ List descriptions = new ArrayList<>();
+
+
+ /**
+ * Create a transport line.
+ *
+ * @param lname the name of the line
+ */
+ public TraceEntry(String lname,String ident,String t_type, String img_url) {
+ super();
+ this.lname = lname;
+ this.id = ident;
+ this.type = t_type;
+ this.url = img_url;
+ }
+
+ // FIXME list of lists are bad practice in direct access...
+ /** @return the list of paths */
+ public List> getPaths() {
+ // TODO Ne pas retourner directement la liste
+ return paths;
+ }
+
+ /** @return the list of terminus */
+ public List getTerminus() {
+ return terminus;
+ }
+
+ public void addPath(List path) {
+ paths.add(new ArrayList<>(path));
+ }
+
+ public void addTerminus(String term) {
+ terminus.add(term);
+ }
+
+ public boolean isDescriptionEmpty(){
+ return descriptions.isEmpty();
+ }
+
+ /**
+ * Add all the description to the current one
+ * @param desctipt
+ */
+ public void addDescriptions(List desctipt){
+ descriptions.addAll(desctipt);
+ }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/idfm/Transport.java b/src/main/java/fr/u_paris/gla/project/idfm/Transport.java
new file mode 100644
index 0000000..1e13dd9
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/idfm/Transport.java
@@ -0,0 +1,226 @@
+package fr.u_paris.gla.project.idfm;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.AbstractMap.SimpleEntry;
+
+/**
+ * Representation of a line with its description and stops
+ */
+public class Transport {
+ //All the stops of the line
+ Map stopsMap = new HashMap<>();
+ public String name;
+ public String type;
+ public String image_url;
+
+ //All the line descriptions (directions and schedules)
+ List descriptions = new ArrayList<>();
+
+
+ public Transport(String name,String type, String url){
+ this.name = name;
+ this.type = type;
+ this.image_url = url;
+ }
+
+
+ /**
+ * Build the bifurcation for all the descriptions
+ */
+ public void buildBifurcation(){
+ // int found = 0;
+ for(TraceDescription d : descriptions){
+ // System.out.println("Debut est "+d.first);
+
+ Stop debut = stopsMap.get(d.from);
+ Stop fin = stopsMap.get(d.to);
+ if (debut != null && fin != null) {
+ SimpleEntry> sol = roadToLast(debut.name, fin.name, new ArrayList(), new ArrayList());
+ if (sol.getKey()) {
+ // found++;
+ d.bifurcation = sol.getValue();
+ }
+ }
+ }
+ // System.out.println("J'en ai trouvé "+found);
+ }
+
+
+ /**
+ * Build the bifurcation for all the descriptions but optimized
+ */
+ public void buildBifurcationOptimzed() {
+ // int found = 0;
+ for (TraceDescription d : descriptions) {
+ Stop debut = stopsMap.get(d.from);
+ Stop fin = stopsMap.get(d.to);
+ if (debut != null && fin != null) {
+ Set alreadyVisited = new HashSet<>();
+ SimpleEntry> sol = roadToLastOptimized(debut.name, fin.name, alreadyVisited, new ArrayList());
+ if (sol.getKey()) {
+ // found++;
+ d.bifurcation = sol.getValue();
+ }
+ }
+ }
+ // System.out.println("J'en ai trouvé " + found);
+ }
+
+
+ /**
+ * Check if the stop is a terminus
+ * @param stop the name of a Stop
+ * @return True if the stop is a terminus
+ */
+ public boolean isTerminus(String stop){
+ for(TraceDescription t: descriptions){
+ if(stop.equals(t.first) || stop.equals(t.last))
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Find the road from the currentStop to the last stop
+ * @param currentStop the current stop we are visiting
+ * @param last The last stop we are trying to go to
+ * @param alreadyVisited All the stop we already have visisted
+ * @param bif All the bifurcation encountered from the first stop to the current
+ * one
+ * @return True and the bifurcation if we found our road to the last stop and
+ * false if we didn't
+ */
+ public SimpleEntry > roadToLast(String currentStop, String last, List alreadyVisited, List bifurcation){
+
+ if(currentStop.equals(last)){
+ return new SimpleEntry<>(true,bifurcation);
+ }
+
+ //Checker if the current stop is the bad terminus
+ if(isTerminus(currentStop)){
+ return new SimpleEntry<>(false,null);
+ }
+ List visitedCopy = new ArrayList<>(alreadyVisited);
+ visitedCopy.add(currentStop);
+
+ Stop current = stopsMap.get(currentStop);
+ List > > solutions = new ArrayList<>();
+ for(BifStop b: current.connected.values()){
+ if(!visitedCopy.contains(b.stop.name)){
+ List bifCopy = new ArrayList<>(bifurcation);
+ if(b.bifurc!= 0)
+ bifCopy.add(b.bifurc);
+ solutions.add(roadToLast(b.stop.name, last, visitedCopy, bifCopy));
+ }
+ }
+ //TODo: Send a list on list of integer in case there is a lot of path for the same direction
+ List bifSol = new ArrayList<>();
+ boolean trouve = false;
+ for(SimpleEntry> se: solutions){
+ if(se.getKey()){
+ trouve = true;
+ bifSol = se.getValue();
+ }
+ }
+ return new SimpleEntry<>(trouve,bifSol) ;
+ }
+
+
+ /**
+ * Find the road from the currentStop to the last stop
+ * @param currentStop the current stop we are visiting
+ * @param last The last stop we are trying to go to
+ * @param alreadyVisited All the stop we already have visisted
+ * @param bif All the bifurcation encountered from the first stop to the current
+ * one
+ * @return True and the bifurcation if we found our road to the last stop and
+ * false if we didn't
+ */
+ public SimpleEntry> roadToLastOptimized(String currentStop, String last, Set alreadyVisited, List bifurcation) {
+ if (currentStop.equals(last)) {
+ return new SimpleEntry<>(true, bifurcation);
+ }
+
+ // Checker if the current stop is the bad terminus
+ if (isTerminus(currentStop)) {
+ return new SimpleEntry<>(false, null);
+ }
+
+ alreadyVisited.add(currentStop);
+
+ Stop current = stopsMap.get(currentStop);
+ List>> solutions = new ArrayList<>();
+ for (BifStop b : current.connected.values()) {
+ if (!alreadyVisited.contains(b.stop.name)) {
+ List bifCopy = new ArrayList<>(bifurcation);
+ if (b.bifurc != 0) {
+ bifCopy.add(b.bifurc);
+ }
+ solutions.add(roadToLastOptimized(b.stop.name, last, alreadyVisited, bifCopy));
+ }
+ }
+
+ // Todo: Send a list on list of integer in case there is a lot of path for the same direction
+ List bifSol = new ArrayList<>();
+ boolean trouve = false;
+ for (SimpleEntry> se : solutions) {
+ if (se.getKey()) {
+ trouve = true;
+ bifSol = se.getValue();
+ break; // Exit loop if a solution is found
+ }
+ }
+
+ alreadyVisited.remove(currentStop); // Remove current stop from visited after processing
+
+ return new SimpleEntry<>(trouve, bifSol);
+ }
+
+
+ /**
+ * Connect 2 stops (start, end) and add them in stopMap if they are not already in
+ * @param start The name of a stop
+ * @param end The name of the stop connected to the start
+ * @param bifurcation The bifurcation taken to go from start stop to end stop
+ */
+ public void addStop(String start, String end, int bifurcation){
+ Stop startStop = stopsMap.computeIfAbsent(start, Stop::new);
+ Stop endStop = stopsMap.computeIfAbsent(end, Stop::new);
+
+ BifStop connectedStop = new BifStop(bifurcation, endStop);
+ startStop.addConnectedStop(connectedStop);
+ }
+
+ /**
+ * Print every stops of the line and its connections
+ */
+ public void printAllConnectionStops() {
+ System.out.println("Affichage des couples (stop, next du stop):");
+ for (Map.Entry entry : stopsMap.entrySet()) {
+ Stop stop = entry.getValue();
+ System.out.println("Stop: " + stop.name);
+ System.out.println("Next:");
+ for (BifStop nextEntry : stop.connected.values()) {
+ System.out.println(nextEntry.bifurc + ": " + nextEntry.stop.name);
+ }
+ }
+ }
+
+
+ /**
+ * Add all the description to the current one
+ * @param desctipt
+ */
+ public void addDescriptions(List desctipt){
+ descriptions.addAll(desctipt);
+ }
+
+
+}
diff --git a/src/main/java/fr/u_paris/gla/project/idfm/UnidentifiedStopEntry.java b/src/main/java/fr/u_paris/gla/project/idfm/UnidentifiedStopEntry.java
new file mode 100644
index 0000000..fc4f8d0
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/idfm/UnidentifiedStopEntry.java
@@ -0,0 +1,72 @@
+/**
+ *
+ */
+package fr.u_paris.gla.project.idfm;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+import fr.u_paris.gla.project.utils.GPS;
+
+/** A transport stop with unidentified name and potential candidates as to their
+ * real value.
+ *
+ * @author Emmanuel Bigeon */
+public final class UnidentifiedStopEntry extends StopEntry {
+ private List candidates = new ArrayList<>();
+
+ /** Create the stop
+ *
+ * @param longitude
+ * @param latitude */
+ public UnidentifiedStopEntry(double longitude, double latitude) {
+ super("Unidentified", longitude, latitude); //$NON-NLS-1$
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormat.format("UnidentifiedStop [candidates={0}]", this.candidates); //$NON-NLS-1$
+ }
+
+ /** Get the most likely choice for the stop
+ *
+ * @return the most likely candidate */
+ public StopEntry resolve() {
+ if (candidates.isEmpty()) {
+ return null;
+ }
+ if (candidates.size() == 1) {
+ return candidates.get(0);
+ }
+ Collections.sort(candidates, (Comparator super StopEntry>) (StopEntry s1,
+ StopEntry s2) -> (int) Math.signum((GPS.distance(latitude, longitude,
+ s1.latitude, s1.longitude)
+ - GPS.distance(latitude, longitude, s2.latitude, s2.longitude))));
+
+ return candidates.get(0);
+ }
+
+ /** Add a candidate.
+ *
+ * @param entry the candidate */
+ public void addCandidate(StopEntry entry) {
+ candidates.add(entry);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ UnidentifiedStopEntry other = (UnidentifiedStopEntry) obj;
+ return Objects.equals(candidates, other.candidates);
+ }
+
+}
diff --git a/src/main/java/fr/u_paris/gla/project/io/ImageFormat.java b/src/main/java/fr/u_paris/gla/project/io/ImageFormat.java
new file mode 100644
index 0000000..7f7f8c8
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/io/ImageFormat.java
@@ -0,0 +1,24 @@
+/**
+ *
+ */
+package fr.u_paris.gla.project.io;
+
+import java.time.format.DateTimeFormatter;
+import java.time.format.ResolverStyle;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A tool class for the Image format.
+ */
+public final class ImageFormat {
+ public static final int NUMBER_COLUMNS = 2;
+
+ public static final int LINE_INDEX = 0;
+ public static final int IMAGE_URL_INDEX = 1;
+
+ /** Hidden constructor for tool class */
+ private ImageFormat() {
+ // Tool class
+ }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/io/NetworkFormat.java b/src/main/java/fr/u_paris/gla/project/io/NetworkFormat.java
index ff95c99..a46499d 100644
--- a/src/main/java/fr/u_paris/gla/project/io/NetworkFormat.java
+++ b/src/main/java/fr/u_paris/gla/project/io/NetworkFormat.java
@@ -14,8 +14,10 @@ import java.util.Locale;
*
* @author Emmanuel Bigeon */
public final class NetworkFormat {
-
+ /** The amount of columns in the CSV file */
public static final int NUMBER_COLUMNS = 8;
+
+ /** The amount of decimal places in the GPS' coordinates */
public static final int GPS_PRECISION = 18;
/** The index of the line name in the network format */
@@ -33,10 +35,10 @@ public final class NetworkFormat {
private static final DateTimeFormatter DURATION_FORMATTER = DateTimeFormatter
.ofPattern("HH:mm:ss");
- private static final NumberFormat DURATION_SECONDS_FORMATTER = NumberFormat
+ private static final NumberFormat DURATION_INDIVIDUAL_FORMATTER = NumberFormat
.getIntegerInstance(Locale.ENGLISH);
static {
- DURATION_SECONDS_FORMATTER.setMinimumIntegerDigits(2);
+ DURATION_INDIVIDUAL_FORMATTER.setMinimumIntegerDigits(2);
}
private static final Temporal ZERO = LocalTime.parse("00:00:00");
@@ -45,22 +47,30 @@ public final class NetworkFormat {
// Tool class
}
+ /** Convert a {@link java.lang.String} into a {@link java.time.Duration}
+ * @param duration the {@link java.lang.String}
+ * @return the {@link java.time.Duration} object
+ */
public static Duration parseDuration(String duration) {
LocalTime time = LocalTime.parse("00:" + duration, DURATION_FORMATTER);
return Duration.between(time, ZERO);
}
+ /** Format a {@link java.time.Duration} into a {@link java.lang.String}
+ * @param duration an object of type {@link java.time.Duration}
+ * @return a String that depicts the duration in format MM:SS
+ */
public static String formatDuration(Duration duration) {
- return Long.toString(duration.toMinutes()) + ":"
- + DURATION_SECONDS_FORMATTER.format(duration.toSecondsPart());
+ return DURATION_INDIVIDUAL_FORMATTER.format(duration.toMinutes()) + ":"
+ + DURATION_INDIVIDUAL_FORMATTER.format(duration.toSecondsPart());
}
/** Get a formatter for the numbers in a GPS coordinate pair
*
- * @return the formatter for numbers in a GPS coordinate pair */
+ * @return the {@link java.text.NumberFormat} formatter for numbers in a GPS coordinate pair */
public static NumberFormat getGPSFormatter() {
NumberFormat instance = NumberFormat.getNumberInstance(Locale.ENGLISH);
instance.setMaximumFractionDigits(GPS_PRECISION);
return instance;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/fr/u_paris/gla/project/io/ScheduleFormat.java b/src/main/java/fr/u_paris/gla/project/io/ScheduleFormat.java
index ba4a645..77020b6 100644
--- a/src/main/java/fr/u_paris/gla/project/io/ScheduleFormat.java
+++ b/src/main/java/fr/u_paris/gla/project/io/ScheduleFormat.java
@@ -1,36 +1,54 @@
/**
- *
+ *
*/
package fr.u_paris.gla.project.io;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
+import java.util.ArrayList;
import java.util.List;
-/** A tool class for the schedule format.
- *
- * @author Emmanuel Bigeon */
+/**
+ * A tool class for the schedule format.
+ *
+ * @author Emmanuel Bigeon
+ */
public final class ScheduleFormat {
- public static final int LINE_INDEX = 0;
+ /** The amount of columns in the CSV file */
+ public static final int NUMBER_COLUMNS = 4;
+
+ /** The index of the line name in the schedule format */
+ public static final int LINE_INDEX = 0;
+ /** The index of the trip sequence in the schedule format */
public static final int TRIP_SEQUENCE_INDEX = 1;
- public static final int TERMINUS_INDEX = 2;
- public static final int TIME_INDEX = 3;
+ /** The index of the terminus name in the schedule format */
+ public static final int TERMINUS_INDEX = 2;
+ /** The index of the time in the schedule format */
+ public static final int TIME_INDEX = 3;
/** Hidden constructor for tool class */
private ScheduleFormat() {
// Tool class
}
- /** Read a trip sequence from its string representation
- *
+ /**
+ * Read a trip sequence from its string representation
+ *
* @param representation the representation
- * @return the sequence of branching */
+ * @return the sequence of branching
+ */
public static List getTripSequence(String representation) {
- // TODO Read a trip sequence from a string
- throw new RuntimeException("Not implemented yet");
- }
-
+ List l = new ArrayList<>();
+ for(String s : representation.split(","))
+ l.add(Integer.parseInt(s));
+ return l;
+ }
+
+
+ /** Create a {@link java.time.format.DateTimeFormatter} object used to format Dates
+ * @return the formatter
+ */
public static DateTimeFormatter getTimeFormatter() {
return DateTimeFormatter.ofPattern("HH:mm").withResolverStyle(ResolverStyle.LENIENT);
}
diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Connection.java b/src/main/java/fr/u_paris/gla/project/itinerary/Connection.java
new file mode 100644
index 0000000..ed9dde6
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/itinerary/Connection.java
@@ -0,0 +1,99 @@
+package fr.u_paris.gla.project.itinerary;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class Connection{
+ // Destination of the connection between the two stops
+ private final Stop stop;
+
+ // The line used for this connection
+ private final String lineName;
+
+ //Distance between the two stops
+ private final double distance;
+
+ //Travel time between the two stops
+ private final int time;
+
+ private final ArrayList schedules;
+
+ private final int bifurcation;
+
+ public Connection(Stop stop, String lineName, double distance, int time, int bifurcation){
+ this.stop = stop;
+ this.lineName=lineName;
+ this.distance = distance;
+ this.time = time;
+ this.schedules = new ArrayList<>();
+ this.bifurcation = bifurcation;
+ }
+
+ public Connection(Stop stop, String lineName, double distance, int time){
+ this(stop, lineName, distance, time, 0);
+ }
+
+
+ public String getLineName() {
+ return lineName;
+ }
+
+ public double getDistance() {
+ return distance;
+ }
+
+ public int getTime() {
+ return time;
+ }
+
+ public Stop getStop() {
+ return stop;
+ }
+
+ public void addSchedule(int hours) {
+ this.schedules.add(hours);
+ }
+
+ public void sortSchedule() {
+ Collections.sort(this.schedules);
+ }
+
+ public ArrayList getSchedules() {
+ return this.schedules;
+ }
+
+ public int getBifurcation() {
+ return this.bifurcation;
+ }
+
+ public double getCost() {
+ return this.time;
+ }
+
+ public double getNextTime(double currentTime) {
+ if(this.schedules.size() == 0) {
+ return currentTime;
+ }
+
+ int i = 0;
+ while(i < this.schedules.size() && this.schedules.get(i) < currentTime) {
+ i++;
+ }
+ if(i < this.schedules.size()) {
+ return this.schedules.get(i);
+ }
+ return this.schedules.get(0);
+ }
+
+ public double getCost(double currentTime) {
+ if(this.schedules.size() == 0) {
+ if(this.lineName.equals("WALK")) {
+ return this.time;
+ }
+ return this.time + 900;
+ }
+ double nextTime = this.getNextTime(currentTime);
+ if(nextTime < currentTime) { nextTime += 86400;}
+ return nextTime - currentTime + this.time;
+ }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Finder.java b/src/main/java/fr/u_paris/gla/project/itinerary/Finder.java
new file mode 100644
index 0000000..1e557b3
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/itinerary/Finder.java
@@ -0,0 +1,101 @@
+package fr.u_paris.gla.project.itinerary;
+
+import java.util.*;
+
+public class Finder {
+ private Graph graph;
+ public Finder(Graph graph) {
+ this.graph = graph;
+ }
+
+ public List findPath(Stop startNode, Stop goalNode) {
+ double startTime = 43200; //12h
+
+ PriorityQueue openSet = new PriorityQueue<>(Comparator.comparingDouble(GraphNode::getF));
+ HashSet closedSet = new HashSet<>();
+ HashMap cameFrom = new HashMap<>();
+ HashMap gScore = new HashMap<>();
+ HashMap fScore = new HashMap<>();
+
+ // Initialize scores for all nodes to infinity
+ for (Stop node : graph.getNodes()) {
+ gScore.put(node, Double.POSITIVE_INFINITY);
+ fScore.put(node, Double.POSITIVE_INFINITY);
+ }
+
+ // The cost of going from start to start is the start time
+ gScore.put(startNode, startTime);
+ // For the first node, fScore = gScore + heuristic
+ fScore.put(startNode, startNode.getHeuristicCost(goalNode));
+ openSet.add(startNode);
+
+ while (!openSet.isEmpty()) {
+ Stop current = openSet.poll();
+ double currentTime = gScore.get(current);
+
+ if (current.equals(goalNode)) {
+ return reconstructPath(cameFrom, current);
+ }
+
+ closedSet.add(current);
+
+ if(graph.getConnections(current) == null) {
+ continue;
+ }
+
+ for (Connection connection : graph.getConnections(current)) {
+ Stop neighbor = connection.getStop();
+ if (closedSet.contains(neighbor)) {
+ continue; // Ignore the neighbor which is already evaluated.
+ }
+
+ double tentativeGScore = currentTime + connection.getCost(currentTime);
+
+ if (tentativeGScore >= gScore.get(neighbor)) {
+ continue; // This is not a better path.
+ }
+
+ // This path is the best until now. Record it!
+ cameFrom.put(neighbor, current);
+ gScore.put(neighbor, tentativeGScore);
+ fScore.put(neighbor, tentativeGScore + neighbor.getHeuristicCost(goalNode));
+
+ if (!openSet.contains(neighbor)) {
+ neighbor.setF(fScore.get(neighbor));
+ openSet.add(neighbor);
+ }
+ else {
+ updatePriority(openSet, neighbor, fScore.get(neighbor));
+ }
+ }
+ }
+
+ // If we reach here, it means there's no path from start to goal
+ return null;
+ }
+
+ private List reconstructPath(HashMap cameFrom, Stop current) {
+ List totalPath = new ArrayList<>();
+ totalPath.add(current);
+
+ while (cameFrom.containsKey(current)) {
+ current = cameFrom.get(current);
+ totalPath.add(0, current); // Add to the beginning of the list to maintain order
+ }
+
+ return totalPath;
+ }
+
+ public void updatePriority(PriorityQueue openSet, Stop node, double newF) {
+ openSet.remove(node);
+ node.setF(newF);
+ openSet.add(node);
+ }
+
+ //TODO:
+ public List findPath(double longitude, double latitude){
+ return null;
+ }
+
+}
+
diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Graph.java b/src/main/java/fr/u_paris/gla/project/itinerary/Graph.java
new file mode 100644
index 0000000..795add9
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/itinerary/Graph.java
@@ -0,0 +1,28 @@
+package fr.u_paris.gla.project.itinerary;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class Graph{
+ private final Set nodes;
+
+ private final Map> connections;
+
+ public Graph(Set nodes, Map> connections) {
+ this.nodes = nodes;
+ this.connections = connections;
+ }
+
+ public Set getConnections(Stop node) {
+ return connections.get(node);
+ }
+
+ public Set getNodes() {
+ return nodes;
+ }
+
+ public Map> getConnections() {
+ return connections;
+ }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/GraphNode.java b/src/main/java/fr/u_paris/gla/project/itinerary/GraphNode.java
new file mode 100644
index 0000000..175db6e
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/itinerary/GraphNode.java
@@ -0,0 +1,14 @@
+package fr.u_paris.gla.project.itinerary;
+
+import java.util.List;
+import java.util.Set;
+
+public interface GraphNode {
+ int getId();
+ double getHeuristicCost(Stop goalNode);
+
+ Set getNeighbors();
+ double getCost(Stop neighbor);
+ double getF();
+ void setF(double value);
+}
diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/ItineraryCalculator.java b/src/main/java/fr/u_paris/gla/project/itinerary/ItineraryCalculator.java
new file mode 100644
index 0000000..f2ebe85
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/itinerary/ItineraryCalculator.java
@@ -0,0 +1,258 @@
+package fr.u_paris.gla.project.itinerary;
+
+import fr.u_paris.gla.project.idfm.*;
+import fr.u_paris.gla.project.utils.CSVTools;
+import fr.u_paris.gla.project.utils.GPS;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ItineraryCalculator {
+ private static final Logger LOGGER = Logger
+ .getLogger(IDFMNetworkExtractor.class.getName());
+
+ // IDF mobilite generated file
+ private static final String TRACE_FILE_NAME = "./trace.csv";
+
+ private static final String HOURS_FILE_NAME = "./hours.csv";
+
+ // IDF mobilite file format
+
+ private static final int IDFM_TRACE_ID_INDEX = 0;
+
+ private static final int IDFM_TRACE_DERIV_INDEX = 1;
+
+ private static final int IDFM_TRACE_FROM_INDEX = 2;
+
+ private static final int IDFM_TRACE_FROM_GPS_INDEX = 3;
+
+ private static final int IDFM_TRACE_TO_INDEX= 4;
+
+ private static final int IDFM_TRACE_TO_GPS_INDEX = 5;
+
+ private static final int IDFM_TRACE_TIME_INDEX = 6;
+
+ private static final int IDFM_TRACE_DISTANCE_INDEX = 7;
+
+ private static final double ERROR_MARGIN = 1.;
+
+ //The time public vehicles spend at each stop in seconds.
+ private static final int STOP_TIME = 30;
+
+ //Walking speed in m/s
+ private static final double WALK_SPEED = 1.;
+
+ /**
+ * Returns the coordinates from a String to a double array:
+ * "49.08, 3.07" -> {49.08, 3.07}
+ * @param gps the string representation
+ * @return the double array
+ */
+ private static double[] getCoords(String gps) {
+ String []stringCoords = gps.split(", ");
+ return new double[] {Double.parseDouble(stringCoords[0]), Double.parseDouble(stringCoords[1])};
+ }
+
+ /**
+ * Searchs for a stop with the same name and GPS coordinates in the graph, and creates it if non existant
+ * @param nodes a graph of the stops
+ * @param tmp list of the created stops
+ * @param name the name of the stop
+ * @param gps the coordinate of the stop
+ * @param lineId the line the stop is on
+ * @return the Stop object
+ */
+ private static Stop getOrCreateStop(HashSet nodes, HashMap> tmp, String name, String gps, String lineId, HashMap> connections) {
+ ArrayList stopList = tmp.get(name);
+ double[] coords = getCoords(gps);
+
+ // First we search by name, and then compare the coordinates since multiple stations can have the same name. A margin of error is necessary since stops can have multiple GPS coordinates
+ ArrayList lineChanges = new ArrayList<>();
+ if (stopList != null) {
+ for(Stop stop : stopList) {
+
+ double dist = GPS.distance(coords[0], coords[1], stop.getLatitude(), stop.getLongitude());
+ if(dist == 0) {
+ stop.addLine(lineId);
+ return stop;
+ }
+ if(dist < ERROR_MARGIN) {
+ lineChanges.add(stop);
+ }
+ }
+ }
+
+ Stop newStop = new Stop(lineId, name, coords[0], coords[1]);
+ nodes.add(newStop);
+ stopList = stopList == null ? new ArrayList<>() : stopList;
+ stopList.add(newStop);
+ tmp.put(name, stopList);
+ for(Stop s : lineChanges) {
+ double dist = GPS.distance(coords[0], coords[1], s.getLatitude(), s.getLongitude());
+ int time = (int) (dist*1000/WALK_SPEED);
+ Connection c1 = new Connection(s, "WALK", dist, time);
+ connections.computeIfAbsent(newStop, k -> new HashSet<>()).add(c1);
+
+ Connection c2 = new Connection(newStop, "WALK", dist, time);
+ connections.computeIfAbsent(s, k -> new HashSet<>()).add(c2);
+ }
+ return newStop;
+ }
+
+ /**
+ * Adds into the graph the connection between two stops, parsed from a CSV line
+ * @param line the current line we want to parse
+ * @param nodes the graph of stops
+ * @param tmp list of the created stops
+ * @param connections
+ */
+ private static void addLine(String[] line, HashSet nodes, HashMap> tmp, HashMap> connections) {
+ Stop fromStop = getOrCreateStop(nodes, tmp, line[IDFM_TRACE_FROM_INDEX], line[IDFM_TRACE_FROM_GPS_INDEX], line[IDFM_TRACE_ID_INDEX], connections);
+ Stop toStop = getOrCreateStop(nodes, tmp, line[IDFM_TRACE_TO_INDEX], line[IDFM_TRACE_TO_GPS_INDEX], line[IDFM_TRACE_ID_INDEX], connections);
+
+ String[] timeString = line[IDFM_TRACE_TIME_INDEX].split(":");
+ String time0WithoutComma = timeString[0].replace(",", "");
+ int time = Integer.parseInt(time0WithoutComma) * 60 + Integer.parseInt(timeString[1]);
+
+ Connection connection = new Connection(toStop, line[IDFM_TRACE_ID_INDEX], Double.parseDouble(line[IDFM_TRACE_DISTANCE_INDEX]), time, Integer.parseInt(line[IDFM_TRACE_DERIV_INDEX]));
+
+ connections.computeIfAbsent(fromStop, k -> new HashSet<>()).add(connection);
+ }
+
+ private static void addScheduleRec(Stop current, Stop previous, String line, ArrayList bifurcations, int time, HashMap> stopsHashSet, HashMap> connections, HashSet processed){
+ time = time%86400;
+ //If the stop has already been processed, it is not reprocessed.
+ if(processed.contains(current)) {return;}
+ processed.add(current);
+
+ Set neighborhood = connections.get(current);
+ if(neighborhood == null) {return;}
+
+
+ ArrayList directions = new ArrayList<>();
+ for(Connection n : neighborhood) {
+ if(n.getLineName().equals(line)
+ && (previous == null || !n.getStop().getName().equals(previous.getName()))
+ ) {
+ directions.add(n);
+ }
+ }
+
+ if(directions.size() == 0) {return;}
+
+ Stop next_stop = null;
+ if(directions.size() > 1) {
+ int bifurcation = bifurcations.size() == 0 ? 0 : bifurcations.get(0);
+ if(bifurcations.size() > 0) {bifurcations.remove(0);}
+ for(Connection d : directions) {
+ if(d.getBifurcation() == bifurcation) {
+ next_stop = d.getStop();
+ break;
+ }
+ }
+ if(next_stop == null) {
+ return;
+ }
+ }
+ else {
+ next_stop = directions.get(0).getStop();
+ if(directions.get(0).getBifurcation() != 0) {
+ if(bifurcations.size() > 0 && directions.get(0).getBifurcation() == bifurcations.get(0)){
+ bifurcations.remove(0);
+ }
+ }
+ }
+
+ for(Connection n : neighborhood) {
+ if(n.getStop() == next_stop) {
+ n.addSchedule(time);
+ time += n.getTime();
+ addScheduleRec(next_stop, current, line, bifurcations, time, stopsHashSet, connections, processed);
+ return;
+ }
+ }
+ }
+
+ private static void addSchedule(String[] input, HashMap> stopsHashSet, HashMap> connections) {
+
+ String line = input[0];
+
+ ArrayList bifurcations = new ArrayList<>();
+ if(!input[1].equals("[]")) {
+ String[] b = input[1].substring(1, input[1].length()-1).split(",");
+ bifurcations = new ArrayList<>();
+ for(String n : b){
+ bifurcations.add(Integer.parseInt(n.trim()));
+ }
+ }
+
+ String name = input[2];
+
+ String[] timeString = input[3].split(":");
+ int time = Integer.parseInt(timeString[0]) * 3600 + Integer.parseInt(timeString[1])*60 + STOP_TIME;
+
+
+ ArrayList stops = stopsHashSet.get(name);
+ if(stops == null) {return;}
+
+ for(Stop stop : stops) {
+ if(stop.getLines().contains(line)) {
+ addScheduleRec(stop, null, line, bifurcations, time, stopsHashSet, connections, new HashSet<>());
+ }
+ }
+ }
+
+ public static void main(String[] args){
+ if (args.length != 0) {
+ LOGGER.severe("Invalid command line. Target file names are in the main file for now.");
+ return;
+ }
+ IDFMNetworkExtractor.builFiles();
+
+ try {
+ HashSet nodes = new HashSet<>();
+ HashMap> connections = new HashMap<>();
+ HashMap> tmp = new HashMap<>();
+ CSVTools.readCSVFromFile(TRACE_FILE_NAME,
+ (String[] line) -> addLine(line, nodes, tmp, connections));
+
+ CSVTools.readCSVFromFile(HOURS_FILE_NAME,
+ (String[] line) -> addSchedule(line, tmp, connections));
+
+ for(Set set : connections.values()) {
+ for(Connection c : set) {
+ c.sortSchedule();
+ }
+ }
+
+
+ Stop porteivry = tmp.get("Porte d'Ivry").get(0);
+ Stop repu = tmp.get("République").get(0);
+
+ Graph graph = new Graph(nodes, connections);
+ int cpt = 0;
+ for (Map.Entry> entry : graph.getConnections().entrySet()) {
+ if (entry.getValue() == null) cpt +=1;
+ }
+ Stop garenord = tmp.get("Gare du Nord").get(0);
+
+ Stop chatelet = tmp.get("Châtelet").get(0);
+ //System.out.println(graph.getConnections(garenord));
+ //System.out.println(cpt);
+ //System.out.println(graph.getConnections(porteivry));
+ Finder finder = new Finder(graph);
+
+ List res = finder.findPath(porteivry, chatelet);
+ for (Stop element : res) {
+ System.out.println(element);
+ }
+
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, "Error while reading the line paths", e);
+ }
+
+
+ }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Path.java b/src/main/java/fr/u_paris/gla/project/itinerary/Path.java
new file mode 100644
index 0000000..a492b99
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/itinerary/Path.java
@@ -0,0 +1,18 @@
+package fr.u_paris.gla.project.itinerary;
+
+public class Path {
+ private T current;
+
+ private T previous;
+
+ private double currentScore;
+
+ private final double targetScore;
+
+ public Path(T current, T previous, double currentScore, double targetScore) {
+ this.current = current;
+ this.previous = previous;
+ this.currentScore = currentScore;
+ this.targetScore = targetScore;
+ }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Stop.java b/src/main/java/fr/u_paris/gla/project/itinerary/Stop.java
new file mode 100644
index 0000000..6d9169c
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/itinerary/Stop.java
@@ -0,0 +1,90 @@
+package fr.u_paris.gla.project.itinerary;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class Stop implements GraphNode {
+ // The total number of stops
+ private static int counter = 0;
+
+ private final int id;
+
+ // The set of all the lines the stop is on
+ private final Set lines;
+
+ private final String name;
+
+ private final double latitude;
+
+ private final double longitude;
+
+ private double f;
+
+ public Stop(String line, String name, double latitude, double longitude) {
+ lines = new HashSet<>();
+ lines.add(line);
+ this.id = counter++;
+ this.name = name;
+ this.latitude = latitude;
+ this.longitude = longitude;
+ this.f = 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Stop{" +
+ "id=" + id +
+ ", lines=" + lines +
+ ", name='" + name + '\'' +
+ ", latitude=" + latitude +
+ ", longitude=" + longitude +
+ '}';
+ }
+
+ @Override
+ public int getId(){
+ return id;
+ }
+
+ @Override
+ public double getHeuristicCost(Stop goalNode) {
+ return 0;
+ }
+
+ @Override
+ public Set getNeighbors() {
+ return null;
+ }
+
+ @Override
+ public double getCost(Stop neighbor) {
+ return 0;
+ }
+
+ @Override
+ public double getF() {
+ return f;
+ }
+
+ public void setF(double value) {
+ this.f = value;
+ }
+
+ public String getName(){
+ return name;
+ }
+
+ public double getLatitude(){
+ return latitude;
+ }
+
+ public double getLongitude(){
+ return longitude;
+ }
+
+ public void addLine(String s){
+ lines.add(s);
+ }
+
+ public Set getLines() { return this.lines; }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/utils/ApiUtils.java b/src/main/java/fr/u_paris/gla/project/utils/ApiUtils.java
new file mode 100644
index 0000000..3a40845
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/utils/ApiUtils.java
@@ -0,0 +1,54 @@
+package fr.u_paris.gla.project.utils;
+
+import fr.u_paris.gla.project.idfm.IDFMNetworkExtractor;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ApiUtils {
+ /** The logger for information on the process */
+ private static final Logger LOGGER = Logger
+ .getLogger(IDFMNetworkExtractor.class.getName());
+
+ // OpenStreetMap API URL
+ private static final String OSM_URL = "https://nominatim.openstreetmap.org/search";
+
+ public static double[] getGPSLocation(String term) {
+ try {
+ String urlString = String.format("%s?q=%s&format=json", OSM_URL, URLEncoder.encode(term, StandardCharsets.UTF_8));
+ HttpURLConnection connection = (HttpURLConnection) new URL(urlString).openConnection();
+
+ connection.setRequestMethod("GET");
+
+ BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ String inputLine;
+ StringBuilder response = new StringBuilder();
+
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+
+ JSONArray jsonArray = new JSONArray(response.toString());
+
+ if (!jsonArray.isEmpty()) {
+ JSONObject firstResult = jsonArray.getJSONObject(0);
+
+ double lat = firstResult.getDouble("lat");
+ double lon = firstResult.getDouble("lon");
+ return new double[]{lat, lon};
+ }
+ }
+ catch (IOException e) {
+ LOGGER.log(Level.SEVERE, e,
+ () -> "Error accessing the API");
+ }
+ return new double[]{0, 0};
+ }
+}
diff --git a/src/main/java/fr/u_paris/gla/project/utils/CSVTools.java b/src/main/java/fr/u_paris/gla/project/utils/CSVTools.java
new file mode 100644
index 0000000..8fa35db
--- /dev/null
+++ b/src/main/java/fr/u_paris/gla/project/utils/CSVTools.java
@@ -0,0 +1,107 @@
+/**
+ *
+ */
+package fr.u_paris.gla.project.utils;
+
+import java.io.*;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import com.opencsv.CSVParserBuilder;
+import com.opencsv.CSVReader;
+import com.opencsv.CSVReaderBuilder;
+import com.opencsv.CSVWriterBuilder;
+import com.opencsv.ICSVParser;
+import com.opencsv.ICSVWriter;
+import com.opencsv.exceptions.CsvValidationException;
+
+/** A CSV tool class.
+ *
+ * @author Emmanuel Bigeon */
+public final class CSVTools {
+
+ /** Hidden constructor of tool class */
+ private CSVTools() {
+ // Tool class
+ }
+
+ /** get a CSV file from a URL, download and parse it, and keep values in memory
+ * @param is the address of the CSV file
+ * @param contentLineConsumer the variable used to store the data
+ * @throws IOException if it's impossible to download the file
+ */
+ private static void readCSVFromInputStream(InputStream is, Consumer contentLineConsumer)
+ throws IOException {
+ ICSVParser parser = new CSVParserBuilder().withSeparator(';').build();
+ try (Reader reader = new BufferedReader(
+ new InputStreamReader(is, StandardCharsets.UTF_8))) {
+ CSVReaderBuilder csvBuilder = new CSVReaderBuilder(reader)
+ .withCSVParser(parser);
+ try (CSVReader csv = csvBuilder.build()) {
+ String[] line = csv.readNextSilently(); // Eliminate header
+ while (csv.peek() != null) {
+ line = csv.readNext();
+ contentLineConsumer.accept(line);
+ }
+ }
+ } catch (CsvValidationException e) {
+ throw new IOException("Invalid csv file", e); //$NON-NLS-1$
+ }
+ }
+
+
+ /** get a CSV file from a file and parse it, keeping values in memory
+ * @param filename the saved file's name and path
+ * @param contentLineConsumer the variable used to store the data
+ * @throws IOException if it's impossible to read the file
+ */
+ public static void readCSVFromFile(String filename, Consumer contentLineConsumer)
+ throws IOException {
+ File file = new File(filename);
+ readCSVFromInputStream(new FileInputStream(file), contentLineConsumer);
+ }
+
+ /** get a CSV file from a URL, download and parse it, and keep values in memory
+ * @param url the address of the CSV file
+ * @param contentLineConsumer the variable used to store the data
+ * @throws IOException if it's impossible to download the file
+ */
+ public static void readCSVFromURL(String url, Consumer contentLineConsumer)
+ throws IOException {
+ readCSVFromInputStream(new URL(url).openStream(), contentLineConsumer);
+ }
+
+ /** Save our current CSV variable's data into an actual file
+ * @param filename the saved file's name and path
+ * @param contentLineConsumer our data variable
+ * @throws IOException if we can't write the data into the file
+ */
+ public static void writeCSVToFile(String filename,
+ Stream contentLinesConsumer) throws IOException {
+ try (FileWriter writer = new FileWriter(filename, StandardCharsets.UTF_8)) {
+ CSVWriterBuilder wBuilder = new CSVWriterBuilder(writer).withSeparator(';');
+ try (ICSVWriter csv = wBuilder.build()) {
+ contentLinesConsumer.forEachOrdered(csv::writeNext);
+ }
+ }
+ }
+
+ // /** Save our current CSV variable's data into an actual file
+ // * @param filename the saved file's name and path
+ // * @param contentLineConsumer our data variable
+ // * @throws IOException if we can't write the data into the file
+ // */
+ // public static void writeCSVToFile(String filename,
+ // Stream contentLinesConsumer) throws IOException {
+ // try (FileWriter writer = new FileWriter(filename, StandardCharsets.UTF_8)) {
+ // CSVWriterBuilder wBuilder = new CSVWriterBuilder(writer).withSeparator(';');
+ // try (ICSVWriter csv = wBuilder.build()) {
+ // contentLinesConsumer.forEachOrdered(line -> Arrays.stream(line).forEach(csv::writeNext));
+ // }
+ // }
+ // }
+
+}
diff --git a/src/main/java/fr/u_paris/gla/project/utils/GPS.java b/src/main/java/fr/u_paris/gla/project/utils/GPS.java
index 72f2202..678fc3a 100644
--- a/src/main/java/fr/u_paris/gla/project/utils/GPS.java
+++ b/src/main/java/fr/u_paris/gla/project/utils/GPS.java
@@ -32,14 +32,16 @@ public final class GPS {
* @param longitude1 the longitude of the first position
* @param latitude2 the latitude of the second position
* @param longitude2 the longitude of the second position
- * @return the flying distance */
+ * @return the flying distance in km*/
public static double distance(double latitude1, double longitude1, double latitude2,
double longitude2) {
- double deltaLatitude = degreeToRadian(latitude2 - latitude1);
+ latitude1 = degreeToRadian(latitude1);
+ latitude2 = degreeToRadian(latitude2);
+ double deltaLatitude = latitude2 - latitude1;
double deltaLongitude = degreeToRadian(longitude2 - longitude1);
double a = Math.pow(Math.sin(deltaLatitude / 2), 2)
- + Math.pow(Math.sin(deltaLongitude), 2) * Math.cos(latitude1)
+ + Math.pow(Math.sin(deltaLongitude / 2), 2) * Math.cos(latitude1)
* Math.cos(latitude2);
- return EARTH_RADIUS * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ return 2 * EARTH_RADIUS * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
}
diff --git a/src/test/java/fr/u_paris/gla/project/AppTest.java b/src/test/java/fr/u_paris/gla/project/AppTest.java
index 2351945..cdb42b4 100644
--- a/src/test/java/fr/u_paris/gla/project/AppTest.java
+++ b/src/test/java/fr/u_paris/gla/project/AppTest.java
@@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test;
class AppTest {
/** Rigorous Test :-) */
@Test
- void testPlaceholder() {
+ public void testPlaceholder() {
assertTrue(true, "It should be true that true is true...");
}
}
diff --git a/src/test/java/fr/u_paris/gla/project/idfm/CSVStreamProviderTest.java b/src/test/java/fr/u_paris/gla/project/idfm/CSVStreamProviderTest.java
new file mode 100644
index 0000000..0ed235a
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/idfm/CSVStreamProviderTest.java
@@ -0,0 +1,122 @@
+package fr.u_paris.gla.project.idfm;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.lang.reflect.Method;
+import java.text.MessageFormat;
+import java.text.NumberFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import org.junit.jupiter.api.Test;
+import fr.u_paris.gla.project.io.NetworkFormat;
+import fr.u_paris.gla.project.utils.GPS;
+
+public class CSVStreamProviderTest {
+
+ //Test de hasNext, pour le cas ou il y'a un trace et cas ou il n'y en a pas
+ @Test
+ public void testHasNext() {
+ // Scénario sans Trace
+ CSVStreamProvider providerWithoutTrace = new CSVStreamProvider(Collections.emptyIterator(),null);
+ assertFalse(providerWithoutTrace.hasNext(), "hasNext should return false when no traces are provided");
+
+ // Scénario avec Trace
+ StopEntry stop1 = new StopEntry("Stop1", 2.3522, 48.8566);
+ StopEntry stop2 = new StopEntry("Stop2", 2.295, 48.8738);
+ List path = Arrays.asList(stop1, stop2);
+
+ TraceEntry trace = new TraceEntry("Ligne1","IDFM:03434","Bus", "dummy_url");
+ trace.addPath(path);
+
+ CSVStreamProvider providerWithTrace = new CSVStreamProvider(Arrays.asList(trace).iterator(),null);
+ assertTrue(providerWithTrace.hasNext(), "hasNext should return true when traces are provided");
+ }
+
+
+
+ //Test de la methode next()
+ @Test
+ public void testNext() {
+ // Initialisation des données d'exemple directement dans le test
+ StopEntry start = new StopEntry("Début", 2.3522, 48.8566); // Paris
+ StopEntry end = new StopEntry("Fin", 2.295, 48.8738); // Proche de Paris
+
+ TraceEntry traceEntry = new TraceEntry("Ligne1","IDFM:03434","Bus", "dummy_url");
+ traceEntry.addPath(Arrays.asList(start, end)); // Ajout d'un chemin à la trace
+
+ CSVStreamProvider provider = new CSVStreamProvider(Collections.singletonList(traceEntry).iterator(),null);
+
+ assertTrue(provider.hasNext(), "Doit avoir un prochain élément");
+
+ String[] result = provider.next();
+ assertNotNull(result, "Le résultat ne doit pas être null");
+
+ // Vérifications spécifiques sur le format des données de sortie
+ assertEquals(start.lname, result[NetworkFormat.START_INDEX], "Le nom de l'arrêt de départ doit correspondre");
+ assertEquals(end.lname, result[NetworkFormat.STOP_INDEX], "Le nom de l'arrêt d'arrivée doit correspondre");
+
+ // Calcul et vérification de la distance attendue
+ double expectedDistance = GPS.distance(start.latitude, start.longitude, end.latitude, end.longitude);
+ String expectedDistanceFormatted = NumberFormat.getInstance(Locale.ENGLISH).format(expectedDistance);
+ assertEquals(expectedDistanceFormatted, result[NetworkFormat.DISTANCE_INDEX], "La distance doit correspondre");
+ }
+
+
+
+ //Test de la methode private fillStation avec la réflexion
+ @Test
+ public void testFillStation() throws Exception {
+ // Initialisation des données de test
+ StopEntry stop = new StopEntry("StopName", 2.3522, 48.8566); // Exemple de coordonnées pour Paris
+ String[] nextLine = new String[NetworkFormat.NUMBER_COLUMNS];
+
+ // Accès à la méthode fillStation via la réflexion
+ Method fillStationMethod = CSVStreamProvider.class.getDeclaredMethod("fillStation", StopEntry.class, String[].class, int.class);
+ fillStationMethod.setAccessible(true);
+
+ // Invocation de la méthode fillStation
+ fillStationMethod.invoke(null, stop, nextLine, NetworkFormat.START_INDEX);
+
+ // Format attendu pour la latitude et la longitude
+ NumberFormat gpsFormatter = NetworkFormat.getGPSFormatter();
+ String expectedLatitudeLongitude = MessageFormat.format("{0}, {1}",
+ gpsFormatter.format(stop.latitude),
+ gpsFormatter.format(stop.longitude));
+
+ // Vérifications
+ assertEquals(stop.lname, nextLine[NetworkFormat.START_INDEX], "Le nom de l'arrêt doit correspondre.");
+ assertEquals(expectedLatitudeLongitude, nextLine[NetworkFormat.START_INDEX + 1], "Les coordonnées GPS doivent correspondre.");
+ }
+
+
+
+
+ //Test de la méthode static private distanceToTime()
+ @Test
+ public void testDistanceToTime() throws Exception {
+ // Valeurs fictives pour TWO_ACCELERATION_DISTANCE et MAX_SPEED
+ final double TWO_ACCELERATION_DISTANCE = 0.2; // Par exemple
+ final double MAX_SPEED = 5.0; // Par exemple
+
+ // Exemple de distance à tester
+ double distanceExample = 1.0; // 1 km
+
+ // Calcul attendu basé sur la formule fournie
+ double expected = Math.max(0, distanceExample - TWO_ACCELERATION_DISTANCE) / MAX_SPEED
+ + Math.pow(Math.min(distanceExample, TWO_ACCELERATION_DISTANCE) / MAX_SPEED, 2);
+
+ // Accès à la méthode distanceToTime via la réflexion
+ Method method = CSVStreamProvider.class.getDeclaredMethod("distanceToTime", double.class);
+ method.setAccessible(true);
+
+ // Invocation de la méthode distanceToTime et stockage du résultat
+ double result = (Double) method.invoke(null, distanceExample);
+
+ // Assertion pour vérifier si le résultat est conforme à l'attendu
+ assertEquals(expected, result, "Le calcul du temps à partir de la distance devrait être conforme à l'attendu.");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/idfm/IDFMNetworkExtractorTest.java b/src/test/java/fr/u_paris/gla/project/idfm/IDFMNetworkExtractorTest.java
new file mode 100644
index 0000000..7e6bffc
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/idfm/IDFMNetworkExtractorTest.java
@@ -0,0 +1,59 @@
+package fr.u_paris.gla.project.idfm;
+import org.junit.jupiter.api.Test;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import static org.junit.jupiter.api.Assertions.*;
+
+
+class IDFMNetworkExtractorTest {
+
+ //Test de clenLine de ma classe IDFMNetworkExtractor
+ @Test
+ public void testCleanLine() throws Exception {
+ // Création d'un arrêt non identifié avec des coordonnées spécifiques
+ UnidentifiedStopEntry unidentifiedStop = new UnidentifiedStopEntry(2.3522, 48.8566); // Coordonnées pour Paris
+
+ // Création d'un arrêt candidat proche de l'arrêt non identifié
+ StopEntry closeCandidate = new StopEntry("Proche Candidat", 2.3523, 48.8567); // Coordonnées proches de Paris
+
+ // Ajout du candidat à l'arrêt non identifié
+ unidentifiedStop.addCandidate(closeCandidate);
+
+ // Liste des chemins contenant l'arrêt non identifié
+ List> paths = new ArrayList<>(Arrays.asList(Arrays.asList(unidentifiedStop)));
+
+ // Accès à la méthode cleanLine via la réflexion
+ Method cleanLineMethod = IDFMNetworkExtractor.class.getDeclaredMethod("cleanLine", List.class);
+ cleanLineMethod.setAccessible(true);
+
+ // Invocation de la méthode cleanLine
+ boolean result = (Boolean) cleanLineMethod.invoke(null, paths);
+
+ // Vérifications
+ assertTrue(result, "La méthode cleanLine devrait retourner true si le nettoyage a réussi.");
+ assertNotEquals("Unidentified", paths.get(0).get(0).lname, "L'arrêt non identifié devrait avoir été résolu.");
+ assertEquals(closeCandidate.lname, paths.get(0).get(0).lname, "L'arrêt devrait être résolu au candidat le plus proche.");
+ }
+
+
+
+ @Test
+ public void testAddCandidate() throws Exception {
+ UnidentifiedStopEntry unidentifiedStop = new UnidentifiedStopEntry(2.3522, 48.8566); // Coordonnées pour Paris
+ List path = new ArrayList<>(Arrays.asList(unidentifiedStop));
+ TraceEntry trace = new TraceEntry("Ligne1","IDFM:03434","Bus", "dummy_url");
+ trace.addPath(path);
+
+ StopEntry candidate = new StopEntry("Proche Candidat", 2.3523, 48.8567); // Coordonnées proches
+
+ Method method = IDFMNetworkExtractor.class.getDeclaredMethod("addCandidate", TraceEntry.class, StopEntry.class);
+ method.setAccessible(true);
+
+ method.invoke(null, trace, candidate);
+
+ //L'appel c'est derouler correctement
+ assertTrue(true, "L'appel de addCandidate s'est déroulé sans erreur.");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/idfm/StopEntryTest.java b/src/test/java/fr/u_paris/gla/project/idfm/StopEntryTest.java
new file mode 100644
index 0000000..deb31ce
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/idfm/StopEntryTest.java
@@ -0,0 +1,61 @@
+package fr.u_paris.gla.project.idfm;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class StopEntryTest {
+
+ //Test de toString
+ @Test
+ public void testToString() {
+ StopEntry stop = new StopEntry("Chatelet", 2.346, 48.853);
+ // Mise à jour de la valeur attendue pour correspondre au formatage réel
+ String expected = "Chatelet [2,346, 48,853]";
+ assertEquals(expected, stop.toString());
+ }
+
+ //Si le le test testToString du haut ne marche pas essayer celui du bas
+ /*@Test
+ public void testToString() {
+ StopEntry stop = new StopEntry("Chatelet", 2.346, 48.853);
+ // Mise à jour de la valeur attendue pour correspondre au formatage réel
+ String expected = "Chatelet [2,346, 48,853]";
+ assertEquals(expected, stop.toString());
+ } */
+
+
+ //Test de compareTo
+ @Test
+ public void testCompareTo() {
+ StopEntry stop1 = new StopEntry("Chatelet", 2.3467, 48.8534);
+ StopEntry stop2 = new StopEntry("Louvre", 2.3360, 48.8606);
+ assertTrue(stop1.compareTo(stop2) < 0); //
+ assertTrue(stop2.compareTo(stop1) > 0); //
+
+ // Test avec la même latitude et longitude mais des noms différents
+ StopEntry stop3 = new StopEntry("Chatelet", 2.3467, 48.8534);
+ assertEquals(0, stop1.compareTo(stop3));
+
+ // Test avec le même nom mais des emplacements différents
+ StopEntry stop4 = new StopEntry("Chatelet", 2.3500, 48.8500);
+ assertTrue(stop1.compareTo(stop4) > 0);
+ }
+
+ //Test de hashCode
+ @Test
+ public void testHashCode() {
+ StopEntry stop1 = new StopEntry("Chatelet", 2.3467, 48.8534);
+ StopEntry stop2 = new StopEntry("Chatelet", 2.3467, 48.8534);
+ assertEquals(stop1.hashCode(), stop2.hashCode());
+ }
+
+ //Test de equals
+ @Test
+ public void testEquals() {
+ StopEntry stop1 = new StopEntry("Chatelet", 2.3467, 48.8534);
+ StopEntry stop2 = new StopEntry("Chatelet", 2.3467, 48.8534);
+ StopEntry stop3 = new StopEntry("Louvre", 2.3360, 48.8606);
+
+ assertEquals(stop1, stop2);
+ assertNotEquals(stop1, stop3);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/idfm/StopTest.java b/src/test/java/fr/u_paris/gla/project/idfm/StopTest.java
new file mode 100644
index 0000000..6139466
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/idfm/StopTest.java
@@ -0,0 +1,68 @@
+package fr.u_paris.gla.project.idfm;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class StopTest {
+
+
+ @Test
+ public void testIsStopConnected() {
+
+ Stop stop = new Stop("Stop1");
+ BifStop bifStop1 = new BifStop(1, new Stop("Stop2"));
+ // Initially, no stops are connected
+ assertFalse(stop.isStopConnected("Stop2"));
+
+ // Add a connected stop
+ stop.addConnectedStop(bifStop1);
+
+ // Now, Stop2 should be connected
+ assertTrue(stop.isStopConnected("Stop2"));
+ }
+
+
+ @Test
+ public void testGetConnectedStop() {
+
+ Stop stop = new Stop("Stop1");
+ BifStop bifStop1 = new BifStop(1, new Stop("Stop2"));
+ BifStop bifStop2 = new BifStop(2, new Stop("Stop3"));
+
+ // Add two connected stops
+ stop.addConnectedStop(bifStop1);
+ stop.addConnectedStop(bifStop2);
+
+ // Retrieve the connected stops
+ BifStop retrievedStop1 = stop.getConnectedStop("Stop2");
+ BifStop retrievedStop2 = stop.getConnectedStop("Stop3");
+
+ // Check if the correct stops were retrieved
+ assertEquals(bifStop1, retrievedStop1);
+ assertEquals(bifStop2, retrievedStop2);
+ }
+
+ @Test
+ public void testAddConnectedStop() {
+ Stop stop = new Stop("Stop1");
+ BifStop bifStop1 = new BifStop(1, new Stop("Stop2"));
+
+ // Add a connected stop
+ stop.addConnectedStop(bifStop1);
+
+ // Check if the stop was added
+ assertTrue(stop.isStopConnected("Stop2"));
+ }
+
+
+ @Test
+ public void testSHJH(){
+ Stop stop = new Stop("Stop2323");
+ BifStop bifStop1 = new BifStop(1, new Stop("Stop2323"));
+
+ // Add a connected stop
+ stop.addConnectedStop(bifStop1);
+
+ // Check if the stop was added
+ assertTrue(stop.isStopConnected("Stop2323"));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/idfm/TraceEntryTest.java b/src/test/java/fr/u_paris/gla/project/idfm/TraceEntryTest.java
new file mode 100644
index 0000000..e53c1dc
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/idfm/TraceEntryTest.java
@@ -0,0 +1,48 @@
+package fr.u_paris.gla.project.idfm;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import java.util.Arrays;
+import java.util.List;
+
+public class TraceEntryTest {
+
+ //addTerminus
+ @Test
+ public void testAddTerminus() {
+ TraceEntry traceEntry = new TraceEntry("Ligne 1","IDFM:03434","Bus", "dummy_url");
+ String terminus1 = "Terminus A";
+ String terminus2 = "Terminus B";
+
+ //Ajouter des arrêt sur la ligne
+ traceEntry.addTerminus(terminus1);
+ traceEntry.addTerminus(terminus2);
+ List terminusList = traceEntry.getTerminus();
+
+ assertEquals(2, terminusList.size(), "La liste des terminus doit contenir deux éléments.");
+ assertTrue(terminusList.contains(terminus1), "La liste des terminus doit contenir le terminus A.");
+ assertTrue(terminusList.contains(terminus2), "La liste des terminus doit contenir le terminus B.");
+ }
+
+ //addPath
+ @Test
+ public void testAddPath() {
+ TraceEntry traceEntry = new TraceEntry("Ligne 1","IDFM:03434","Bus", "dummy_url");
+ StopEntry stop1 = new StopEntry("Station 1", 2.300, 48.850);
+ StopEntry stop2 = new StopEntry("Station 2", 2.310, 48.855);
+ List path = Arrays.asList(stop1, stop2);
+ traceEntry.addPath(path);
+ List> paths = traceEntry.getPaths();
+
+ assertEquals(1, paths.size(), "Il doit y avoir un chemin dans la liste des chemins.");
+ assertEquals(2, paths.get(0).size(), "Le chemin ajouté doit contenir deux arrêts.");
+ assertTrue(paths.get(0).containsAll(path), "Le chemin ajouté doit contenir les arrêts spécifiés.");
+ }
+
+
+ //Verfier si le nom de la ligne lname est correctement initialiser
+ @Test
+ public void testTraceEntryName() {
+ TraceEntry traceEntry = new TraceEntry("Ligne 1","IDFM:03434","Bus", "dummy_url");
+ assertEquals("Ligne 1", traceEntry.lname, "Le nom de la ligne doit être 'Ligne 1'.");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/idfm/TransportTest.java b/src/test/java/fr/u_paris/gla/project/idfm/TransportTest.java
new file mode 100644
index 0000000..59540cf
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/idfm/TransportTest.java
@@ -0,0 +1,64 @@
+package fr.u_paris.gla.project.idfm;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.List;
+import java.util.ArrayList;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class TransportTest {
+
+ /*@Test
+ public void testRoadToLast() {
+ Transport transport = new Transport("Test Line", "Bus");
+ transport.addStop("A", "B", 1);
+ transport.addStop("B", "C", 2);
+ transport.addStop("C", "D", 3);
+ TraceDescription td = new TraceDescription("A", "D", "A", "D");
+ transport.descriptions.add(td);
+
+ List visited = new ArrayList<>();
+ List bifurcations = new ArrayList<>();
+ SimpleEntry> result = transport.roadToLast("A", "D", visited, bifurcations);
+ assertFalse(result.getKey());
+ assertEquals(List.of(1, 2, 3), result.getValue());
+ }*/
+
+ /*@Test
+ public void testRoadToLastOptimized() {
+ SimpleEntry> result = transport.roadToLastOptimized("A", "D", new HashSet<>(), new ArrayList<>());
+ assertTrue(result.getKey());
+ assertEquals(List.of(1, 2, 3), result.getValue());
+ }*/
+
+ @Test
+ public void testIsTerminus() {
+ Transport transport = new Transport("Test Line", "Bus", "dummy_url");
+ transport.addStop("A", "B", 1);
+ transport.addStop("B", "C", 2);
+ transport.addStop("C", "D", 3);
+ TraceDescription td = new TraceDescription("A", "D", "A", "D");
+ transport.descriptions.add(td);
+
+ assertTrue(transport.isTerminus("A"));
+ assertTrue(transport.isTerminus("D"));
+ assertFalse(transport.isTerminus("B"));
+ }
+
+ @Test
+ public void testAddStop() {
+ Transport transport = new Transport("Test Line", "Bus", "dummy_url");
+ transport.addStop("A", "B", 1);
+ transport.addStop("B", "C", 2);
+ transport.addStop("C", "D", 3);
+ TraceDescription td = new TraceDescription("A", "D", "A", "D");
+ transport.descriptions.add(td);
+
+ transport.addStop("D", "E", 4);
+ assertTrue(transport.stopsMap.containsKey("E"));
+ assertEquals("E", transport.stopsMap.get("E").name);
+ assertTrue(transport.stopsMap.get("D").isStopConnected("E"));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/idfm/UnidentifiedStopEntryTest.java b/src/test/java/fr/u_paris/gla/project/idfm/UnidentifiedStopEntryTest.java
new file mode 100644
index 0000000..d51a60f
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/idfm/UnidentifiedStopEntryTest.java
@@ -0,0 +1,84 @@
+package fr.u_paris.gla.project.idfm;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.Test;
+
+
+public class UnidentifiedStopEntryTest {
+
+
+ // Test de la méthode resolve de la classe UnidentifiedStopEntry
+ @Test
+ public void testResolve() {
+ // Création d'un UnidentifiedStopEntry avec des coordonnées arbitraires (0,0)
+ UnidentifiedStopEntry unidentifiedStopEntry = new UnidentifiedStopEntry(0, 0);
+
+ // Test lorsque la liste des candidats est vide
+ assertNull(unidentifiedStopEntry.resolve());
+
+ // Test lorsque la liste des candidats contient un seul StopEntry
+ StopEntry stopEntry1 = new StopEntry("Stop1", 10.0, 20.0);
+ unidentifiedStopEntry.addCandidate(stopEntry1);
+ assertEquals(stopEntry1, unidentifiedStopEntry.resolve());
+
+ // Test lorsque la liste des candidats contient plusieurs StopEntries
+ StopEntry stopEntry2 = new StopEntry("Stop2", 30.0, 40.0);
+ unidentifiedStopEntry.addCandidate(stopEntry2);
+ // En supposant que la méthode GPS.distance fonctionne correctement, stopEntry1 devrait être plus proche
+ assertEquals(stopEntry1, unidentifiedStopEntry.resolve());
+
+ // Test lorsque la liste des candidats contient plusieurs StopEntries et que le plus proche change
+ UnidentifiedStopEntry unidentifiedStopEntry2 = new UnidentifiedStopEntry(35.0, 45.0);
+ unidentifiedStopEntry2.addCandidate(stopEntry1);
+ unidentifiedStopEntry2.addCandidate(stopEntry2);
+ // Maintenant, stopEntry1 devrait être plus proche
+ assertEquals(stopEntry2, unidentifiedStopEntry2.resolve());
+ }
+
+
+ // Test de la méthode addCandidate de la classe UnidentifiedStopEntry
+ @Test
+ public void testAddCandidate() {
+ // Création d'un UnidentifiedStopEntry avec des coordonnées arbitraires (0,0)
+ UnidentifiedStopEntry unidentifiedStopEntry = new UnidentifiedStopEntry(0, 0);
+
+ // Test lorsque nous ajoutons un StopEntry à la liste des candidats
+ StopEntry stopEntry1 = new StopEntry("Stop1", 10.0, 20.0);
+ unidentifiedStopEntry.addCandidate(stopEntry1);
+ assertEquals(stopEntry1, unidentifiedStopEntry.resolve());
+
+ // Test lorsque nous ajoutons un autre StopEntry à la liste des candidats
+ StopEntry stopEntry2 = new StopEntry("Stop2", 30.0, 40.0);
+ unidentifiedStopEntry.addCandidate(stopEntry2);
+ // En supposant que la méthode GPS.distance fonctionne correctement, stopEntry1 devrait être plus proche
+ assertEquals(stopEntry1, unidentifiedStopEntry.resolve());
+ }
+
+
+ // Test de la méthode equals de la classe UnidentifiedStopEntry
+ @Test
+ public void testEquals() {
+ // Création de deux UnidentifiedStopEntry avec les mêmes coordonnées
+ UnidentifiedStopEntry unidentifiedStopEntry1 = new UnidentifiedStopEntry(0, 0);
+ UnidentifiedStopEntry unidentifiedStopEntry2 = new UnidentifiedStopEntry(0, 0);
+
+ // Test lorsque nous comparons un UnidentifiedStopEntry avec lui-même
+ assertTrue(unidentifiedStopEntry1.equals(unidentifiedStopEntry1));
+
+ // Test lorsque nous comparons deux UnidentifiedStopEntry qui n'ont pas de candidats
+ assertTrue(unidentifiedStopEntry1.equals(unidentifiedStopEntry2));
+
+ // Test lorsque nous ajoutons le même StopEntry aux deux UnidentifiedStopEntry
+ StopEntry stopEntry = new StopEntry("Stop1", 10.0, 20.0);
+ unidentifiedStopEntry1.addCandidate(stopEntry);
+ unidentifiedStopEntry2.addCandidate(stopEntry);
+ assertTrue(unidentifiedStopEntry1.equals(unidentifiedStopEntry2));
+
+ // Test lorsque nous ajoutons un autre StopEntry à l'un des UnidentifiedStopEntry
+ StopEntry stopEntry2 = new StopEntry("Stop2", 30.0, 40.0);
+ unidentifiedStopEntry1.addCandidate(stopEntry2);
+ assertFalse(unidentifiedStopEntry1.equals(unidentifiedStopEntry2));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/io/NetworkFormatTest.java b/src/test/java/fr/u_paris/gla/project/io/NetworkFormatTest.java
new file mode 100644
index 0000000..4422575
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/io/NetworkFormatTest.java
@@ -0,0 +1,80 @@
+package fr.u_paris.gla.project.io;
+
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.NumberFormat;
+import java.time.Duration;
+import java.time.LocalTime;
+import java.time.format.DateTimeParseException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class NetworkFormatTest {
+
+ String t = "00:00";
+ NumberFormat GPS_test = NetworkFormat.getGPSFormatter();
+
+ @Test
+ public void testParseDurationEqual() {
+
+ assertEquals(Duration.ZERO, NetworkFormat.parseDuration(t));
+ }
+
+ @Test
+ public void testParseDurationTooBig() {
+ String y = "119:00";
+ assertThrows(DateTimeParseException.class, () -> NetworkFormat.parseDuration(y));
+ }
+
+ @Test
+ public void formatDuration() {
+ assertEquals(t, NetworkFormat.formatDuration(Duration.ZERO));
+ }
+
+ @Test
+ public void parseThenFormatDuration(){
+ String t = "00:00";
+ assertEquals(t, NetworkFormat.formatDuration(NetworkFormat.parseDuration(t)));
+ }
+
+ @Test
+ public void getGPSFormatterPos() {
+ double GPS_pos = 1.456489615649813;
+ assertEquals(String.valueOf(GPS_pos), GPS_test.format(GPS_pos));
+
+
+ }
+
+ @Test
+ public void getGPSFormatterNeg() {
+ double GPS_neg = -1.456489615649813;
+ assertEquals(String.valueOf(GPS_neg), GPS_test.format(GPS_neg));
+
+
+ }
+ @Test
+ public void getGPSFormatterNul() {
+ int GPS_nul = 0;
+ assertEquals(String.valueOf(GPS_nul), GPS_test.format(GPS_nul));
+
+
+ }
+
+ @Test
+ public void getGPSFormatterBig() {
+ String string_int = "4565156498156489";
+ String string_float = "5675747274674276474267479751262167";
+ BigDecimal GPS_big = new BigDecimal(string_int + "." + string_float);
+
+
+ assertEquals(string_int + "." + string_float.substring(0, NetworkFormat.GPS_PRECISION),
+ GPS_test.format(GPS_big).replace(",", "").replace(" ",""));
+
+
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/io/ScheduleFormatTest.java b/src/test/java/fr/u_paris/gla/project/io/ScheduleFormatTest.java
new file mode 100644
index 0000000..152ffd9
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/io/ScheduleFormatTest.java
@@ -0,0 +1,33 @@
+package fr.u_paris.gla.project.io;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ScheduleFormatTest {
+
+ @Test
+ public void getTripSequence() {
+ String rpz = "4,5,19,21";
+ List test = Arrays.asList(4, 5, 19, 21);
+
+ assertEquals(test, ScheduleFormat.getTripSequence(rpz));
+ }
+
+ @Test
+ public void getTimeFormatter() {
+ DateTimeFormatter formatter = ScheduleFormat.getTimeFormatter();
+ LocalDateTime date = LocalDateTime.now();
+ String test = date.format(formatter);
+ //format date: YYYY-MM-DDTHH:MM:SS.DECIMAL
+ assertEquals(date.toString().substring(11, 16), test);
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/utils/CSVToolsTest.java b/src/test/java/fr/u_paris/gla/project/utils/CSVToolsTest.java
new file mode 100644
index 0000000..e40bca4
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/utils/CSVToolsTest.java
@@ -0,0 +1,79 @@
+package fr.u_paris.gla.project.utils;
+
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CSVToolsTest {
+
+
+
+
+ @Test
+ public void readCSVFromURL_invalid() {
+ // TODO Fix the exception thrown
+ /**
+ assertThrows(IOException.class,() -> {
+ Consumer test = s -> System.out.println(Arrays.toString(s));
+ CSVTools.readCSVFromURL("https://google.fr",
+ test);
+
+ }
+ );
+ */
+ }
+
+ @Test
+ public void testreadCSVFromURL_valid() {
+ assertDoesNotThrow(() -> {
+ Consumer test = s -> System.out.println(Arrays.toString(s));
+ CSVTools.readCSVFromURL("https://people.sc.fsu.edu/~jburkardt/data/csv/addresses.csv",
+ test);
+
+ }
+ );
+ }
+
+ @Test
+ void writeCSVToFile() {
+
+ assertDoesNotThrow(() -> {
+ String[] stuff = {"jsqdsqdsqsqffdfgzava", "pfezegrrbeebn", "dfbsduifzegbczi", "sdfsdfcy"};
+ String[][] t = {stuff, stuff};
+ Stream test = Arrays.stream(t);
+ CSVTools.writeCSVToFile("test.csv", test);
+
+ });
+ }
+
+ @Test
+ void writeCSVToFile_specialName() {
+
+ assertDoesNotThrow(() -> {
+ String[] stuff = {"jsqdsqdsqsqffdfgzava", "pfezegrrbeebn", "dfbsduifzegbczi", "sdfsdfcy"};
+ String[][] t = {stuff, stuff};
+ Stream test = Arrays.stream(t);
+ CSVTools.writeCSVToFile("éè'-'_-éè_à.csv", test);
+
+ });
+ }
+
+ @Test
+ void writeCSVToFile_invalidName() {
+
+ assertThrows( IOException.class ,() -> {
+ String[] stuff = {"jsqdsqdsqsqffdfgzava", "pfezegrrbeebn", "dfbsduifzegbczi", "sdfsdfcy"};
+ String[][] t = {stuff, stuff};
+ Stream test = Arrays.stream(t);
+ CSVTools.writeCSVToFile(".", test);
+
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/fr/u_paris/gla/project/utils/GPSTest.java b/src/test/java/fr/u_paris/gla/project/utils/GPSTest.java
new file mode 100644
index 0000000..ed1dc45
--- /dev/null
+++ b/src/test/java/fr/u_paris/gla/project/utils/GPSTest.java
@@ -0,0 +1,39 @@
+package fr.u_paris.gla.project.utils;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class GPSTest {
+
+
+ @Test
+ public void testDistance_SameLat(){
+ assertDoesNotThrow(
+ () -> {
+ GPS.distance(5, 3, 5, 11);
+ }
+ );
+ }
+
+ @Test
+ public void distance_SameLon(){
+ assertDoesNotThrow(
+ () -> {
+ GPS.distance(5, 3, 7, 3);
+ }
+ );
+ }
+
+ @Test
+ public void distance_SamePoint() {
+ assertEquals(0.0, GPS.distance(5, 3, 5, 3) );
+ }
+
+ @Test
+ public void distance_NegativePoint(){
+ assertNotEquals(0.0, GPS.distance(-5, 7, -13, 4));
+ }
+
+
+}
\ No newline at end of file