Merge branch 'dev' into 'main'
Dev sur main pour release 0.0.0.1 See merge request gla-groupe-3/projet!18
This commit is contained in:
commit
df2eac5076
46 changed files with 79424 additions and 21 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,7 +1,15 @@
|
||||||
# Maven
|
# Maven
|
||||||
target/
|
target/
|
||||||
|
|
||||||
|
# Files
|
||||||
|
.csv
|
||||||
|
|
||||||
|
# IDEs
|
||||||
# Eclipse
|
# Eclipse
|
||||||
.settings/
|
.settings/
|
||||||
.project
|
.project
|
||||||
.classpath
|
.classpath
|
||||||
|
# VSCode/VSCodium
|
||||||
|
.vscode/
|
||||||
|
# IntelliJ
|
||||||
|
.idea/
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
## Etudiant
|
## Etudiant
|
||||||
|
|
||||||
Vous ne devez pas contribuer directement à ce projet mais devez en effectuer un fork. Une fois cela effectué vous devez:
|
Vous ne devez pas contribuer directement à ce projet mais devez en effectuer un fork. Une fois cela effectué vous devez:
|
||||||
- [ ] Ajouter votre identifiant de groupe au champs `groupId` du fichier [pom.xml](pom.xml) sous la forme de `fr.u-paris.gla.votreequipe`
|
- [X] Ajouter votre identifiant de groupe au champs `groupId` du fichier [pom.xml](pom.xml) sous la forme de `fr.u-paris.gla.votreequipe`
|
||||||
- [ ] Modifier le package principal afin de refleter le nouveau nom de groupe.
|
- [ ] Modifier le package principal afin de refleter le nouveau nom de groupe.
|
||||||
- [ ] Adapter le fichier [README](README.md) au contenu de votre projet specifique
|
- [ ] Adapter le fichier [README](README.md) au contenu de votre projet specifique
|
||||||
- [ ] Adapter ce fichier (CONTRIBUTING.md) à vos propres instructions de contribution, notamment:
|
- [ ] Adapter ce fichier (CONTRIBUTING.md) à vos propres instructions de contribution, notamment:
|
||||||
- [ ] Convention de style de codage
|
- [ ] Convention de style de codage
|
||||||
- [ ] Convention d'utilisation de git
|
- [ ] Convention d'utilisation de git
|
||||||
- [ ] Lien avec d'autres projets et d'autres dépôts.
|
- [ ] Lien avec d'autres projets et d'autres dépôts.
|
||||||
- [ ] Modifier le fichier `application.properties` au besoin.
|
- [X] Modifier le fichier `application.properties` au besoin.
|
||||||
|
|
||||||
|
|
||||||
## Enseignant
|
## Enseignant
|
||||||
|
|
33
README.md
33
README.md
|
@ -1,27 +1,48 @@
|
||||||
# Projet de GLA
|
# Projet de GLA
|
||||||
|
|
||||||
Version 2024
|
Version 2024
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
Ceci est l'archetype de projet de Génie Logiciel Avancé (GLA).
|
|
||||||
|
|
||||||
Il s'agit d'un projet Java. Ce dépôt définit un système de build et une application simple. Il est nécéssaire de consulter le fichier [CONTRIBUTING.md](CONTRIBUTING.md) pour utiliser ce dépôt.
|
Ceci est l'archétype de projet de Génie Logiciel Avancé (GLA).
|
||||||
|
|
||||||
|
Il s'agit d'un projet Java. Ce dépôt définit un système de build et une application simple. Il est nécessaire de consulter le fichier [CONTRIBUTING.md](CONTRIBUTING.md) pour utiliser ce dépôt.
|
||||||
|
|
||||||
## Lancement du programme
|
## Lancement du programme
|
||||||
Ce projet utilise [maven](https://maven.apache.org/) de Apache pour la gestion de construction.
|
|
||||||
|
|
||||||
Afin de compiler et lancer les tests, éxecutez simplement
|
Ce projet utilise [maven](https://maven.apache.org/) d'Apache pour la gestion de construction.
|
||||||
```
|
|
||||||
|
Afin de compiler et lancer les tests, exécutez simplement
|
||||||
|
|
||||||
|
```bash
|
||||||
mvn verify
|
mvn verify
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Afin de vérifier les tests via JaCoCo.
|
||||||
|
Les résultats du test sont dans `target/site/jacoco/index.html`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean jacoco:prepare-agent install jacoco:report
|
||||||
|
```
|
||||||
|
|
||||||
|
Par la suite, `mvn jacoco:report` suffit.
|
||||||
|
|
||||||
Dans sa version initiale, le programme fournit est un simple code qui se lance en terminal ou en application graphique.
|
Dans sa version initiale, le programme fournit est un simple code qui se lance en terminal ou en application graphique.
|
||||||
|
|
||||||
Une fois le programme compilé, vous trouverez un jar executable dans le dossier target. Au nom de jar près (version changeante), vous pourrez l'exécuter avec:
|
Une fois le programme compilé, vous trouverez un jar executable dans le dossier target. Au nom de jar près (version changeante), vous pourrez l'exécuter avec:
|
||||||
|
|
||||||
```
|
```
|
||||||
java -jar project-2024.1.0.0-SNAPSHOT.jar --info
|
java -jar target/project-2024.1.0.0-SNAPSHOT.jar --info
|
||||||
```
|
```
|
||||||
|
|
||||||
L'option de lancement `--info` causera l'affichage dans la console d'informations de l'application.
|
L'option de lancement `--info` causera l'affichage dans la console d'informations de l'application.
|
||||||
|
|
||||||
L'option de lancement `--gui` causera l'ouverture d'une fenêtre affichant le logo de l'Université de Paris.
|
L'option de lancement `--gui` causera l'ouverture d'une fenêtre affichant le logo de l'Université de Paris.
|
||||||
|
|
||||||
|
## Tests JaCoCo
|
||||||
|
|
||||||
|
```
|
||||||
|
mvn clean jacoco:prepare-agent install jacoco:report
|
||||||
|
```
|
||||||
|
|
||||||
|
Puis ouvrir le fichier `./target/site/jacoco/index.html`.
|
||||||
|
|
71869
arret_idfm.csv
Normal file
71869
arret_idfm.csv
Normal file
File diff suppressed because it is too large
Load diff
49
pom.xml
49
pom.xml
|
@ -5,11 +5,11 @@
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>fr.u-paris.gla</groupId>
|
<groupId>fr.u-paris.gla.pathfinder</groupId>
|
||||||
<artifactId>project</artifactId>
|
<artifactId>project</artifactId>
|
||||||
<version>2024.1.0.0-SNAPSHOT</version>
|
<version>2024.1.0.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>Project Base</name>
|
<name>Pathfinder</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
@ -23,10 +23,36 @@
|
||||||
<artifactId>junit-jupiter</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
<version>5.9.1</version>
|
<version>5.9.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.opencsv</groupId>
|
||||||
|
<artifactId>opencsv</artifactId>
|
||||||
|
<version>5.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.json</groupId>
|
||||||
|
<artifactId>json</artifactId>
|
||||||
|
<version>20231013</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-dependencies</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/${project.build.finalName}.lib</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
@ -35,11 +61,30 @@
|
||||||
<archive>
|
<archive>
|
||||||
<manifest>
|
<manifest>
|
||||||
<addClasspath>true</addClasspath>
|
<addClasspath>true</addClasspath>
|
||||||
|
<classpathPrefix>${project.build.finalName}.lib/</classpathPrefix>
|
||||||
<mainClass>fr.u_paris.gla.project.App</mainClass>
|
<mainClass>fr.u_paris.gla.project.App</mainClass>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.2.5</version>
|
||||||
|
<configuration>
|
||||||
|
<includes>
|
||||||
|
<include>**/*Test.java</include>
|
||||||
|
</includes>
|
||||||
|
<testFailureIgnore>true</testFailureIgnore>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<version>0.8.11</version>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
<resource>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
@ -15,6 +16,11 @@ import javax.swing.JFrame;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.WindowConstants;
|
import javax.swing.WindowConstants;
|
||||||
|
|
||||||
|
import fr.u_paris.gla.project.itinerary.Finder;
|
||||||
|
import fr.u_paris.gla.project.itinerary.Parse;
|
||||||
|
import fr.u_paris.gla.project.itinerary.Path;
|
||||||
|
import fr.u_paris.gla.project.itinerary.Stop;
|
||||||
|
|
||||||
/** Simple application model.
|
/** Simple application model.
|
||||||
*
|
*
|
||||||
* @author Emmanuel Bigeon */
|
* @author Emmanuel Bigeon */
|
||||||
|
@ -60,31 +66,53 @@ public class App {
|
||||||
showLogo();
|
showLogo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
testRelease();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param out */
|
public static void testRelease(){
|
||||||
public static void printAppInfos(PrintStream out) {
|
Parse parse = new Parse();
|
||||||
Properties props = new Properties();
|
parse.parseFiles();
|
||||||
try (InputStream is = App.class.getResourceAsStream("application.properties")) { //$NON-NLS-1$
|
Stop source = parse.getTmp().get("Porte d'Ivry").get(0);
|
||||||
props.load(is);
|
Stop destination = parse.getTmp().get("Châtelet").get(0);
|
||||||
} catch (IOException e) {
|
System.out.println("Itinéraire de Porte d'Ivry à Châtelet");
|
||||||
throw new RuntimeException("Unable to read application informations", e); //$NON-NLS-1$
|
List<Path> result = parse.getItinerary(source, destination, 43200);
|
||||||
|
for(Path element : result){
|
||||||
|
System.out.println(element.getCurrentStop());
|
||||||
}
|
}
|
||||||
|
System.out.println("°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°");
|
||||||
|
System.out.println("Itinéraire de Porte d'Ivry à Châtelet");
|
||||||
|
source = parse.getTmp().get("Saint-Jacques").get(0);
|
||||||
|
destination = parse.getTmp().get("Porte d'Ivry").get(0);
|
||||||
|
result = parse.getItinerary(source, destination, 43200);
|
||||||
|
for(Path element : result){
|
||||||
|
System.out.println(element.getCurrentStop());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/** @param out the output stream */
|
||||||
|
public static void printAppInfos(PrintStream out) {
|
||||||
|
Properties props = readApplicationProperties();
|
||||||
|
|
||||||
out.println("Application: " + props.getProperty("app.name", UNSPECIFIED)); //$NON-NLS-1$ //$NON-NLS-2$
|
out.println("Application: " + props.getProperty("app.name", UNSPECIFIED)); //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
out.println("Version: " + props.getProperty("app.version", UNSPECIFIED)); //$NON-NLS-1$ //$NON-NLS-2$
|
out.println("Version: " + props.getProperty("app.version", UNSPECIFIED)); //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
out.println("By: " + props.getProperty("app.team", UNSPECIFIED)); //$NON-NLS-1$ //$NON-NLS-2$
|
out.println("By: " + props.getProperty("app.team", UNSPECIFIED)); //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shows the logo in an image. */
|
private static Properties readApplicationProperties() {
|
||||||
public static void showLogo() {
|
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
try (InputStream is = App.class.getResourceAsStream("application.properties")) { //$NON-NLS-1$
|
try (InputStream is = App.class.getResourceAsStream("application.properties")) { //$NON-NLS-1$
|
||||||
props.load(is);
|
props.load(is);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Unable to read application informations", e); //$NON-NLS-1$
|
throw new RuntimeException("Unable to read application informations", e); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Shows the logo in an image. */
|
||||||
|
public static void showLogo() {
|
||||||
|
Properties props = readApplicationProperties();
|
||||||
|
|
||||||
JFrame frame = new JFrame(props.getProperty("app.name")); //$NON-NLS-1$
|
JFrame frame = new JFrame(props.getProperty("app.name")); //$NON-NLS-1$
|
||||||
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||||
|
|
363
src/main/java/fr/u_paris/gla/project/gui/View.java
Normal file
363
src/main/java/fr/u_paris/gla/project/gui/View.java
Normal file
|
@ -0,0 +1,363 @@
|
||||||
|
package fr.u_paris.gla.project.gui;
|
||||||
|
|
||||||
|
import fr.u_paris.gla.project.itinerary.Stop;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.table.DefaultTableModel;
|
||||||
|
import javax.swing.table.TableModel;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class View extends JFrame {
|
||||||
|
private JPanel CardPanel;
|
||||||
|
private JMenuItem Home;
|
||||||
|
private JMenuItem Network;
|
||||||
|
private JMenuItem Favorites;
|
||||||
|
private JPanel NetworkPanel;
|
||||||
|
|
||||||
|
private JTextField TextLocation;
|
||||||
|
private JButton ButtonLocation;
|
||||||
|
private JPanel HomePanel;
|
||||||
|
private JPanel MainPanel;
|
||||||
|
private DefaultTableModel modelStops;
|
||||||
|
|
||||||
|
private JTable tableStops;
|
||||||
|
|
||||||
|
private JTable tableItinerary;
|
||||||
|
|
||||||
|
private DefaultTableModel modelItinerary;
|
||||||
|
|
||||||
|
private JScrollPane paneStops;
|
||||||
|
|
||||||
|
private JScrollPane paneItinerary;
|
||||||
|
private JPanel ItineraryPanel;
|
||||||
|
private JMenuItem Itinerary;
|
||||||
|
private JPanel stationsPanel;
|
||||||
|
private JLabel departText;
|
||||||
|
private JLabel arrText;
|
||||||
|
private JTextField TextCoord;
|
||||||
|
private JButton ButtonCoord;
|
||||||
|
private JPanel SearchCoordPanel;
|
||||||
|
private JPanel SearchLocationPanel;
|
||||||
|
private JLabel NetworkLabel;
|
||||||
|
private JLabel FavLabel;
|
||||||
|
private JMenuBar ButtonBar;
|
||||||
|
|
||||||
|
private ArrayList<Stop> StopList;
|
||||||
|
|
||||||
|
private String departureCur;
|
||||||
|
|
||||||
|
private String arrivalCur;
|
||||||
|
|
||||||
|
private String searchLocation;
|
||||||
|
|
||||||
|
private String searchCoord;
|
||||||
|
|
||||||
|
private ArrayList<Stop> searchRes;
|
||||||
|
|
||||||
|
private int count = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public View(ArrayList<Stop> s) throws HeadlessException {
|
||||||
|
MainPanel = new JPanel();
|
||||||
|
GridLayout MainLayout = new GridLayout(1, 2, 50, 0);
|
||||||
|
MainPanel.setLayout(MainLayout);
|
||||||
|
CardLayout CardLay = new CardLayout();
|
||||||
|
CardPanel = new JPanel(CardLay);
|
||||||
|
|
||||||
|
HomePanel = new JPanel();
|
||||||
|
CardPanel.add(HomePanel);
|
||||||
|
SearchCoordPanel = new JPanel();
|
||||||
|
SearchLocationPanel = new JPanel();
|
||||||
|
GridLayout SearchLayout = new GridLayout(1, 2);
|
||||||
|
HomePanel.setLayout(SearchLayout);
|
||||||
|
HomePanel.add(SearchCoordPanel);
|
||||||
|
HomePanel.add(SearchLocationPanel);
|
||||||
|
GridLayout SearchCoordLayout = new GridLayout(2, 1);
|
||||||
|
GridLayout SearchLocationLayout = new GridLayout(2, 1);
|
||||||
|
SearchCoordPanel.setLayout(SearchCoordLayout);
|
||||||
|
SearchLocationPanel.setLayout(SearchLocationLayout);
|
||||||
|
TextCoord = new JTextField();
|
||||||
|
ButtonCoord = new JButton("Look for coords");
|
||||||
|
SearchCoordPanel.add(TextCoord);
|
||||||
|
SearchCoordPanel.add(ButtonCoord);
|
||||||
|
TextLocation = new JTextField();
|
||||||
|
ButtonLocation = new JButton("Look for string");
|
||||||
|
SearchLocationPanel.add(TextLocation);
|
||||||
|
SearchLocationPanel.add(ButtonLocation);
|
||||||
|
|
||||||
|
NetworkPanel = new JPanel();
|
||||||
|
CardPanel.add(NetworkPanel);
|
||||||
|
GridLayout NetworkLayout = new GridLayout(3, 1);
|
||||||
|
NetworkPanel.setLayout(NetworkLayout);
|
||||||
|
NetworkLabel = new JLabel("Network");
|
||||||
|
NetworkPanel.add(NetworkLabel);
|
||||||
|
stationsPanel = new JPanel();
|
||||||
|
NetworkPanel.add(stationsPanel);
|
||||||
|
GridLayout StationsLayout = new GridLayout(2, 1);
|
||||||
|
departText = new JLabel("Départ: ");
|
||||||
|
arrText = new JLabel("Arivée: ");
|
||||||
|
stationsPanel.setLayout(StationsLayout);
|
||||||
|
stationsPanel.add(departText);
|
||||||
|
stationsPanel.add(arrText);
|
||||||
|
paneStops = new JScrollPane();
|
||||||
|
tableStops = new JTable();
|
||||||
|
paneStops.add(tableStops);
|
||||||
|
NetworkPanel.add(paneStops);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ItineraryPanel = new JPanel();
|
||||||
|
CardPanel.add(ItineraryPanel);
|
||||||
|
GridLayout ItineraryLayout = new GridLayout(2, 1);
|
||||||
|
ItineraryPanel.setLayout(ItineraryLayout);
|
||||||
|
paneItinerary = new JScrollPane();
|
||||||
|
tableItinerary = new JTable();
|
||||||
|
paneItinerary.add(tableItinerary);
|
||||||
|
ItineraryPanel.add(paneItinerary);
|
||||||
|
|
||||||
|
|
||||||
|
ButtonBar = new JMenuBar();
|
||||||
|
GridLayout ButtonLayout = new GridLayout(3, 1);
|
||||||
|
ButtonBar.setLayout(ButtonLayout);
|
||||||
|
Home = new JMenuItem("Home");
|
||||||
|
ButtonBar.add(Home);
|
||||||
|
Network = new JMenuItem("Network");
|
||||||
|
ButtonBar.add(Network);
|
||||||
|
|
||||||
|
Itinerary = new JMenuItem("Itinerary");
|
||||||
|
ButtonBar.add(Itinerary);
|
||||||
|
ButtonBar.setPreferredSize(new Dimension(50, 500));
|
||||||
|
|
||||||
|
MainPanel.add(ButtonBar);
|
||||||
|
MainPanel.add(CardPanel);
|
||||||
|
|
||||||
|
|
||||||
|
modelStops = (DefaultTableModel) tableStops.getModel();
|
||||||
|
modelStops.setColumnCount(2);
|
||||||
|
modelStops.setColumnIdentifiers(new Object[]{"Line", "Stop"});
|
||||||
|
|
||||||
|
modelItinerary = (DefaultTableModel) tableItinerary.getModel();
|
||||||
|
modelItinerary.setColumnCount(3);
|
||||||
|
modelItinerary.setColumnIdentifiers(new Object[]{"Line", "Stop", "Time"});
|
||||||
|
this.StopList = s;
|
||||||
|
|
||||||
|
|
||||||
|
setContentPane(MainPanel);
|
||||||
|
setTitle("app");
|
||||||
|
setExtendedState(JFrame.MAXIMIZED_BOTH);
|
||||||
|
//setUndecorated(true);
|
||||||
|
setVisible(true);
|
||||||
|
;
|
||||||
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
|
||||||
|
Home.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
CardPanel.removeAll();
|
||||||
|
CardPanel.add(HomePanel);
|
||||||
|
CardPanel.repaint();
|
||||||
|
CardPanel.revalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Network.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
LoadSearchResult(s, modelStops);
|
||||||
|
CardPanel.removeAll();
|
||||||
|
CardPanel.add(NetworkPanel);
|
||||||
|
|
||||||
|
CardPanel.repaint();
|
||||||
|
CardPanel.revalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Itinerary.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
LoadSearchResult(s, modelItinerary);
|
||||||
|
CardPanel.removeAll();
|
||||||
|
CardPanel.add(ItineraryPanel);
|
||||||
|
|
||||||
|
CardPanel.repaint();
|
||||||
|
CardPanel.revalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TextLocation.addKeyListener(new KeyAdapter() {
|
||||||
|
@Override
|
||||||
|
public void keyReleased(KeyEvent e) {
|
||||||
|
super.keyReleased(e);
|
||||||
|
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||||
|
|
||||||
|
searchLocation = TextLocation.getText();
|
||||||
|
LoadSearchResult(s, modelStops);
|
||||||
|
System.out.println("Enter key released with text " + searchLocation);
|
||||||
|
CardPanel.removeAll();
|
||||||
|
CardPanel.add(NetworkPanel);
|
||||||
|
|
||||||
|
CardPanel.repaint();
|
||||||
|
CardPanel.revalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ButtonLocation.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
CardPanel.removeAll();
|
||||||
|
searchLocation = TextLocation.getText();
|
||||||
|
LoadSearchResult(s, modelStops);
|
||||||
|
System.out.println("search location clicked with text " + searchLocation);
|
||||||
|
CardPanel.add(NetworkPanel);
|
||||||
|
|
||||||
|
CardPanel.repaint();
|
||||||
|
CardPanel.revalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ButtonCoord.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
CardPanel.removeAll();
|
||||||
|
searchCoord = TextCoord.getText();
|
||||||
|
LoadSearchResult(s, modelStops);
|
||||||
|
System.out.println("search coord clicked with text " + searchCoord);
|
||||||
|
CardPanel.add(NetworkPanel);
|
||||||
|
|
||||||
|
CardPanel.repaint();
|
||||||
|
CardPanel.revalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tableStops.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
super.mouseClicked(e);
|
||||||
|
System.out.println("MouseClick: " + e.getX() + ";" + e.getY());
|
||||||
|
showOptionsDialog(tableStops, e.getX(), e.getY());
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
paneStops.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
super.mouseClicked(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showOptionsDialog(JTable table, int x, int y) {
|
||||||
|
int selectedRow = table.rowAtPoint(new Point(x, y));
|
||||||
|
if (selectedRow != -1) { // If a row is selected
|
||||||
|
|
||||||
|
String stationSel = (String) table.getValueAt(selectedRow, 1);
|
||||||
|
|
||||||
|
// Options to set Departure, Arrival, or Cancel
|
||||||
|
Object[] options = {"Departure", "Arrival", "Cancel"};
|
||||||
|
int choice = JOptionPane.showOptionDialog(null, "What action would you like to perform for " + stationSel + "?", "Action Selection",
|
||||||
|
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
|
||||||
|
|
||||||
|
// Handling the choice
|
||||||
|
if (choice == 0) {
|
||||||
|
this.departureCur = stationSel;
|
||||||
|
this.departText.setText("Departure: " + stationSel);
|
||||||
|
} else if (choice == 1) {
|
||||||
|
this.arrivalCur = stationSel;
|
||||||
|
this.arrText.setText("Arrival: " + stationSel);
|
||||||
|
} else {
|
||||||
|
System.out.println("rien");
|
||||||
|
}
|
||||||
|
System.out.println("Départ: " + this.departureCur + "; Arrivée: " + this.arrivalCur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ArrayList<Stop> s = new ArrayList<>();
|
||||||
|
s.add(new Stop("M8", "Balard", 1.0315897, 3.0265513));
|
||||||
|
s.add(new Stop("M14", "Gare de Lyon", 2.4658452681, 3.0265513));
|
||||||
|
View v = new View(s);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void LoadSearchResult(ArrayList<Stop> stops, DefaultTableModel model) {
|
||||||
|
// Clear existing rows from the table
|
||||||
|
int cols = model.getColumnCount();
|
||||||
|
model.setRowCount(0);
|
||||||
|
model.setColumnCount(cols);
|
||||||
|
|
||||||
|
|
||||||
|
// Add new rows based on the search results
|
||||||
|
count = 0;
|
||||||
|
for (Stop stop : stops) {
|
||||||
|
// Add a row to the table with Stop's line in the first column and Stop's name in the second column
|
||||||
|
|
||||||
|
model.addRow(new Object[]{String.join(",", stop.getLines()), stop.getName()});
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(stops.toString());
|
||||||
|
for (int i = 0; i < model.getRowCount(); i++) {
|
||||||
|
for (int j = 0; j < model.getColumnCount(); j++) {
|
||||||
|
System.out.print("valeur at coord " + i +";" + j +": " + model.getValueAt(i, j) + "\t");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(count);
|
||||||
|
|
||||||
|
tableStops.revalidate();
|
||||||
|
tableStops.repaint();
|
||||||
|
|
||||||
|
tableItinerary.revalidate();
|
||||||
|
tableItinerary.repaint();
|
||||||
|
|
||||||
|
paneStops.setViewportView(tableStops);
|
||||||
|
paneStops.revalidate();
|
||||||
|
paneStops.repaint();
|
||||||
|
|
||||||
|
|
||||||
|
paneItinerary.setViewportView(tableItinerary);
|
||||||
|
paneItinerary.revalidate();
|
||||||
|
paneItinerary.repaint();
|
||||||
|
|
||||||
|
|
||||||
|
NetworkPanel.revalidate();
|
||||||
|
NetworkPanel.repaint();
|
||||||
|
|
||||||
|
ItineraryPanel.revalidate();
|
||||||
|
ItineraryPanel.repaint();
|
||||||
|
|
||||||
|
|
||||||
|
this.displayTableValues(model);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayTableValues(TableModel mod) {
|
||||||
|
|
||||||
|
for (int row = 0; row < mod.getRowCount(); row++) {
|
||||||
|
for (int column = 0; column < mod.getColumnCount(); column++) {
|
||||||
|
|
||||||
|
if (mod.getValueAt(row, column) != null) System.out.print(mod.getValueAt(row, column).toString() + " ");
|
||||||
|
}
|
||||||
|
System.out.print(";");
|
||||||
|
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
src/main/java/fr/u_paris/gla/project/idfm/BifStop.java
Normal file
18
src/main/java/fr/u_paris/gla/project/idfm/BifStop.java
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package fr.u_paris.gla.project.idfm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a stop with the bifurcation that is needed. All stops
|
||||||
|
* have a list of bifstop called connected. BifStop is just composed of a
|
||||||
|
* connected stop and the bifurcation used to go from the first stop to the
|
||||||
|
* connected one.
|
||||||
|
*/
|
||||||
|
public class BifStop {
|
||||||
|
// The bifurcation
|
||||||
|
public int bifurc;
|
||||||
|
public Stop stop;
|
||||||
|
|
||||||
|
public BifStop(int bif, Stop stop){
|
||||||
|
bifurc = bif;
|
||||||
|
this.stop = stop;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package fr.u_paris.gla.project.idfm;
|
||||||
|
|
||||||
|
import fr.u_paris.gla.project.io.ScheduleFormat;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import fr.u_paris.gla.project.io.ImageFormat;
|
||||||
|
import fr.u_paris.gla.project.utils.GPS;
|
||||||
|
|
||||||
|
public final class CSVImageProvider {
|
||||||
|
private static final NumberFormat MINUTES_SECOND_FORMATTER = NumberFormat
|
||||||
|
.getInstance(Locale.ENGLISH);
|
||||||
|
static {
|
||||||
|
MINUTES_SECOND_FORMATTER.setMinimumIntegerDigits(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String[] line = new String[ImageFormat.NUMBER_COLUMNS];
|
||||||
|
private final Iterator<Transport> current;
|
||||||
|
|
||||||
|
/** Create the stream provider */
|
||||||
|
public CSVImageProvider(Iterator<Transport> traces) {
|
||||||
|
this.current = traces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if next exists */
|
||||||
|
public boolean hasNext() {
|
||||||
|
return this.current.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get Next element */
|
||||||
|
public String[] next() {
|
||||||
|
if (!this.hasNext()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transport element = this.current.next();
|
||||||
|
this.line[ImageFormat.LINE_INDEX] = element.name;
|
||||||
|
this.line[ImageFormat.IMAGE_URL_INDEX] = element.image_url;
|
||||||
|
|
||||||
|
return Arrays.copyOf(this.line, this.line.length);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
package fr.u_paris.gla.project.idfm;
|
||||||
|
|
||||||
|
import fr.u_paris.gla.project.io.ScheduleFormat;
|
||||||
|
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
// import java.time.format.ResolverStyle;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class CSVSchedulesProvider {
|
||||||
|
private static final DateTimeFormatter HOUR_MINUTE_FORMATTER = ScheduleFormat.getTimeFormatter();
|
||||||
|
|
||||||
|
private static final HashMap<String, int[]> timings = new HashMap<String, int[]>(){{
|
||||||
|
put("Bus", new int[]{11, 8, 7});
|
||||||
|
put("Funicular", new int[]{15, 25, 20});
|
||||||
|
put("Tram", new int[]{6, 7, 8, 9});
|
||||||
|
put("Rail", new int[]{10, 11,15,12,20});
|
||||||
|
put("Subway", new int[]{4, 2, 6,3,3,4});
|
||||||
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Time between 2 passages for the transports with a new type we don't know yet
|
||||||
|
private static int DEFAULT_TIMING = 6;
|
||||||
|
private static final NumberFormat MINUTES_SECOND_FORMATTER = NumberFormat
|
||||||
|
.getInstance(Locale.ENGLISH);
|
||||||
|
|
||||||
|
static {
|
||||||
|
MINUTES_SECOND_FORMATTER.setMinimumIntegerDigits(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String[] line = new String[ScheduleFormat.NUMBER_COLUMNS];
|
||||||
|
private final Iterator<Transport> currentTransport;
|
||||||
|
private Iterator<TraceDescription> currentDescription = Collections.emptyIterator();
|
||||||
|
|
||||||
|
private String current_tansport_type = "";
|
||||||
|
private LocalDateTime currentHour = null;
|
||||||
|
private LocalDateTime lastHour = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the stream provider
|
||||||
|
*/
|
||||||
|
public CSVSchedulesProvider(Iterator<Transport> transports) {
|
||||||
|
this.currentTransport = transports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return currentTransport.hasNext() || currentDescription.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipToNext() {
|
||||||
|
if(currentHour == null || lastHour == null){
|
||||||
|
skipToNextTransport();
|
||||||
|
}else if(currentHour.compareTo(lastHour) < 0){
|
||||||
|
// System.out.println("**Skip: Le current hour est plus petit "+currentHour+"|||"+lastHour);
|
||||||
|
addRandomMinutes();
|
||||||
|
}else if (currentHour.compareTo(lastHour) >= 0) {
|
||||||
|
// System.out.println("**Skip: Le current hour est plus grand "+currentHour+"|||"+lastHour);
|
||||||
|
skipToNextDescription();
|
||||||
|
}
|
||||||
|
else if (!this.currentDescription.hasNext()) {
|
||||||
|
skipToNextTransport();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move to the the nextDescription of a Transport line
|
||||||
|
*/
|
||||||
|
private void skipToNextDescription() {
|
||||||
|
if (this.currentDescription.hasNext()) {
|
||||||
|
TraceDescription description = this.currentDescription.next();
|
||||||
|
|
||||||
|
currentHour = convertIntoLocalDateTime(description.first);
|
||||||
|
lastHour = convertIntoLocalDateTime(description.last);
|
||||||
|
|
||||||
|
if(lastHour.compareTo(currentHour) <= 0){
|
||||||
|
lastHour = lastHour.plusDays(1);
|
||||||
|
}
|
||||||
|
this.line[ScheduleFormat.TERMINUS_INDEX] = description.from;
|
||||||
|
this.line[ScheduleFormat.TRIP_SEQUENCE_INDEX] = description.bifurcation.toString();
|
||||||
|
this.line[ScheduleFormat.TIME_INDEX] = currentHour.format(HOUR_MINUTE_FORMATTER);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
skipToNextTransport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move to the next Transport line
|
||||||
|
*/
|
||||||
|
private void skipToNextTransport() {
|
||||||
|
if (this.currentTransport.hasNext()) {
|
||||||
|
Transport transport = this.currentTransport.next();
|
||||||
|
this.line[ScheduleFormat.LINE_INDEX] = transport.name;
|
||||||
|
|
||||||
|
current_tansport_type = transport.type;
|
||||||
|
this.currentDescription = transport.descriptions.iterator();
|
||||||
|
skipToNextDescription();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] next() {
|
||||||
|
if (!hasNext()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
skipToNext();
|
||||||
|
return Arrays.copyOf(this.line, this.line.length);
|
||||||
|
|
||||||
|
// return new String[][]{Arrays.copyOf(this.line, this.line.length)};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add random minutes for the next passage of a transport.
|
||||||
|
* The random minutes depends on the type of the transport
|
||||||
|
*/
|
||||||
|
private void addRandomMinutes() {
|
||||||
|
// System.out.println("** addM: AVANT: "+currentHour);
|
||||||
|
currentHour = currentHour.plusMinutes(pickMinute(current_tansport_type));
|
||||||
|
this.line[ScheduleFormat.TIME_INDEX] = currentHour.format(HOUR_MINUTE_FORMATTER);
|
||||||
|
// System.out.println("** addM: APRES: "+currentHour);
|
||||||
|
// debut ++;
|
||||||
|
// if(debut == 7) throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param transportType the type of a transport
|
||||||
|
* @return a random minute depending on the type of the transport
|
||||||
|
*/
|
||||||
|
public static int pickMinute(String transportType) {
|
||||||
|
if (!timings.containsKey(transportType)) {
|
||||||
|
return DEFAULT_TIMING;
|
||||||
|
}
|
||||||
|
int[] temps = timings.get(transportType);
|
||||||
|
Random random = new Random();
|
||||||
|
int indexAleatoire = random.nextInt(temps.length);
|
||||||
|
return temps[indexAleatoire];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param hourMinute hour and minute representation. Ex: "14:03"
|
||||||
|
* @return a datetime of today but using hourMinute
|
||||||
|
*/
|
||||||
|
public static LocalDateTime convertIntoLocalDateTime(String hourMinute) {
|
||||||
|
LocalDateTime aujourdHui = LocalDateTime.now();
|
||||||
|
LocalTime time = LocalTime.parse(hourMinute, HOUR_MINUTE_FORMATTER);
|
||||||
|
|
||||||
|
return aujourdHui.withHour(time.getHour()).withMinute(time.getMinute()).withSecond(0).withNano(0);
|
||||||
|
}
|
||||||
|
}
|
300
src/main/java/fr/u_paris/gla/project/idfm/CSVStreamProvider.java
Normal file
300
src/main/java/fr/u_paris/gla/project/idfm/CSVStreamProvider.java
Normal file
|
@ -0,0 +1,300 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package fr.u_paris.gla.project.idfm;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import fr.u_paris.gla.project.io.NetworkFormat;
|
||||||
|
import fr.u_paris.gla.project.utils.GPS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSV Stream Provider class
|
||||||
|
*/
|
||||||
|
public final class CSVStreamProvider {
|
||||||
|
|
||||||
|
private static final HashMap<String, Double> two_acceleration_distance_by_type = new HashMap<String, Double>(){{
|
||||||
|
put("Bus", 0.1);
|
||||||
|
put("Funicular", 0.1);
|
||||||
|
put("Tram", 0.1);
|
||||||
|
put("Rail", 0.2);
|
||||||
|
put("Subway", 0.1);
|
||||||
|
}};
|
||||||
|
|
||||||
|
private static final HashMap<String, Double> max_speed_by_type = new HashMap<String, Double>(){{
|
||||||
|
put("Bus", 10.0);
|
||||||
|
put("Funicular", 5.0);
|
||||||
|
put("Tram", 20.0);
|
||||||
|
put("Rail", 50.0);
|
||||||
|
put("Subway", 30.0);
|
||||||
|
}};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatter from numbers into GPS Coordinates
|
||||||
|
*/
|
||||||
|
private static final NumberFormat GPS_FORMATTER = NetworkFormat
|
||||||
|
.getGPSFormatter();
|
||||||
|
/**
|
||||||
|
* Formatter from numbers into MM:SS
|
||||||
|
*/
|
||||||
|
private static final NumberFormat MINUTES_SECOND_FORMATTER = NumberFormat
|
||||||
|
.getInstance(Locale.ENGLISH);
|
||||||
|
static {
|
||||||
|
MINUTES_SECOND_FORMATTER.setMinimumIntegerDigits(2);
|
||||||
|
}
|
||||||
|
/** Number of seconds in a minute. */
|
||||||
|
private static final int SECONDS_IN_MINUTES = 60;
|
||||||
|
/**
|
||||||
|
* Number of seconds in an hour
|
||||||
|
*/
|
||||||
|
private static final long SECONDS_IN_HOURS = 3_600;
|
||||||
|
// Magically chosen values
|
||||||
|
/** Maximal speed in km/h */
|
||||||
|
private static final double MAX_SPEED = 5;
|
||||||
|
/** Distance to reach maximal speed in km */
|
||||||
|
private static final double TWO_ACCELERATION_DISTANCE = 0.2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current CSV Line
|
||||||
|
*/
|
||||||
|
private final String[] line = new String[NetworkFormat.NUMBER_COLUMNS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current CSV transport line iterator
|
||||||
|
*/
|
||||||
|
private final Iterator<TraceEntry> currentTrace;
|
||||||
|
/**
|
||||||
|
* Current Stop path iterator
|
||||||
|
*/
|
||||||
|
private Iterator<List<StopEntry>> currentPath = Collections.emptyIterator();
|
||||||
|
/**
|
||||||
|
* current iterator for the begin of the line
|
||||||
|
*/
|
||||||
|
private Iterator<StopEntry> currentSegmentStart = Collections.emptyIterator();
|
||||||
|
/**
|
||||||
|
* current iterator for the end of the line
|
||||||
|
*/
|
||||||
|
private Iterator<StopEntry> currentSegmentEnd = Collections.emptyIterator();
|
||||||
|
/**
|
||||||
|
* HashMap of the current line's segments
|
||||||
|
*/
|
||||||
|
Map<StopEntry, Set<StopEntry>> lineSegments = new HashMap<>();
|
||||||
|
// The transport id with its value
|
||||||
|
private final Map<String, Transport> transports;
|
||||||
|
List <TraceDescription> descriptions = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* current begin of line
|
||||||
|
*/
|
||||||
|
private StopEntry start = null;
|
||||||
|
/**
|
||||||
|
* current end of line
|
||||||
|
*/
|
||||||
|
private StopEntry end = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* csv stream iterator checker
|
||||||
|
*/
|
||||||
|
private boolean hasNext = false;
|
||||||
|
/**
|
||||||
|
* tells if we're already on the next
|
||||||
|
*/
|
||||||
|
private boolean onNext = false;
|
||||||
|
|
||||||
|
private String traceId = "";
|
||||||
|
private String traceType = "";
|
||||||
|
private String url_image = "";
|
||||||
|
|
||||||
|
/** Create the stream provider
|
||||||
|
* @param traces an iterator of the possible traces
|
||||||
|
* @param t map of transports */
|
||||||
|
public CSVStreamProvider(Iterator<TraceEntry> traces, Map<String, Transport> t) {
|
||||||
|
this.currentTrace = traces;
|
||||||
|
transports = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Method that tells if we have segments or paths to go through
|
||||||
|
* @return if there are next elements or not
|
||||||
|
*/
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (!this.onNext) {
|
||||||
|
skipToNext();
|
||||||
|
}
|
||||||
|
return this.hasNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skip to either the next segment or the next path
|
||||||
|
*/
|
||||||
|
private void skipToNext() {
|
||||||
|
if (this.onNext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (!this.onNext) {
|
||||||
|
if (!this.currentSegmentEnd.hasNext()) {
|
||||||
|
skipToNextCandidatePath();
|
||||||
|
}
|
||||||
|
if (this.onNext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skipToNextNewSegment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skips to the next segment
|
||||||
|
*/
|
||||||
|
private void skipToNextNewSegment() {
|
||||||
|
do {
|
||||||
|
this.start = this.currentSegmentStart.next();
|
||||||
|
this.lineSegments.putIfAbsent(this.start, new HashSet<>());
|
||||||
|
this.end = this.currentSegmentEnd.next();
|
||||||
|
} while (this.lineSegments.get(this.start).contains(this.end)
|
||||||
|
&& this.currentSegmentEnd.hasNext());
|
||||||
|
if (!this.lineSegments.get(this.start).contains(this.end)) {
|
||||||
|
this.lineSegments.get(this.start).add(this.end);
|
||||||
|
this.onNext = true;
|
||||||
|
this.hasNext = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Move the reading head of path to the next one that has at least two
|
||||||
|
* elements */
|
||||||
|
private void skipToNextCandidatePath() {
|
||||||
|
currentSegmentStart = null;
|
||||||
|
do {
|
||||||
|
while (!this.currentPath.hasNext()) {
|
||||||
|
if (!this.currentTrace.hasNext()) {
|
||||||
|
this.hasNext = false;
|
||||||
|
this.onNext = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TraceEntry trace = this.currentTrace.next();
|
||||||
|
|
||||||
|
this.traceId = trace.id;
|
||||||
|
this.traceType = trace.type;
|
||||||
|
this.url_image = trace.url;
|
||||||
|
this.descriptions.clear();
|
||||||
|
this.descriptions.addAll(trace.descriptions);
|
||||||
|
|
||||||
|
this.currentPath = trace.getPaths().iterator();
|
||||||
|
this.line[NetworkFormat.LINE_INDEX] = trace.lname;
|
||||||
|
this.lineSegments.clear();
|
||||||
|
}
|
||||||
|
List<StopEntry> path = this.currentPath.next();
|
||||||
|
this.currentSegmentEnd = path.iterator();
|
||||||
|
if (this.currentSegmentEnd.hasNext()) {
|
||||||
|
this.currentSegmentEnd.next();
|
||||||
|
this.currentSegmentStart = path.iterator();
|
||||||
|
}
|
||||||
|
} while (currentSegmentStart == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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,this.traceType) * 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.
|
||||||
|
// * <p>
|
||||||
|
// * 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);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** A tool method to give a delay to go through a certain distance.
|
||||||
|
* <p>
|
||||||
|
* 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, String type) {
|
||||||
|
Double max_speed = max_speed_by_type.get(type);
|
||||||
|
Double two_acc_distance = two_acceleration_distance_by_type.get(type);
|
||||||
|
return Math.max(0, distance - two_acc_distance) / max_speed
|
||||||
|
+ Math.pow(Math.min(distance, two_acc_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,369 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package fr.u_paris.gla.project.idfm;
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code of an extractor for the data from IDF mobilite.
|
||||||
|
*
|
||||||
|
* @author Emmanuel Bigeon
|
||||||
|
*/
|
||||||
|
public class IDFMNetworkExtractor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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";
|
||||||
|
|
||||||
|
private static final String TRACE_FILE_DOWNLOADED_NAME = "./trace_idfm.csv";
|
||||||
|
private static final String STOPS_FILE_DOWNLOADED_NAME = "./arret_idfm.csv";
|
||||||
|
/**
|
||||||
|
* the index in the CSV of a Trace's ID
|
||||||
|
*/
|
||||||
|
// IDF mobilite csv formats
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The quarter of a kilometer as a static value
|
||||||
|
*/
|
||||||
|
// Well named constants
|
||||||
|
private static final double QUARTER_KILOMETER = .25;
|
||||||
|
|
||||||
|
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 buildFiles() {
|
||||||
|
|
||||||
|
if (checkFileExistence("./"+HOURS_FILE_NAME) && checkFileExistence("./"+TRACE_FILE_NAME)) {
|
||||||
|
LOGGER.severe("Files already exists.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, TraceEntry> traces = new HashMap<>();
|
||||||
|
try {
|
||||||
|
CSVTools.readCSVFromFile(TRACE_FILE_DOWNLOADED_NAME,
|
||||||
|
(String[] line) -> addLine(line, traces));
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Error while reading the line paths", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<StopEntry> stops = new ArrayList<>(traces.size() * GUESS_STOPS_BY_LINE);
|
||||||
|
try {
|
||||||
|
CSVTools.readCSVFromFile(STOPS_FILE_DOWNLOADED_NAME,
|
||||||
|
(String[] line) -> addStop(line, traces, stops));
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Error while reading the stops", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanTraces(traces);
|
||||||
|
|
||||||
|
Map<String, Transport> 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<String, TraceEntry> traces) {
|
||||||
|
Set<String> toRemove = new HashSet<>();
|
||||||
|
for (Entry<String, TraceEntry> traceEntry : traces.entrySet()) {
|
||||||
|
TraceEntry trace = traceEntry.getValue();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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<List<StopEntry>> stops) {
|
||||||
|
for (List<StopEntry> 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<String, TraceEntry> traces,
|
||||||
|
List<StopEntry> 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<TraceDescription> 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<String, TraceEntry> 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<List<StopEntry>> 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<StopEntry> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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<List<StopEntry>> buildPaths(String pathsJSON) {
|
||||||
|
List<List<StopEntry>> all = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
JSONObject json = new JSONObject(pathsJSON);
|
||||||
|
JSONArray paths = json.getJSONArray("coordinates");
|
||||||
|
for (int i = 0; i < paths.length(); i++) {
|
||||||
|
JSONArray path = paths.getJSONArray(i);
|
||||||
|
List<StopEntry> stopsPath = new ArrayList<>();
|
||||||
|
for (int j = 0; j < path.length(); j++) {
|
||||||
|
JSONArray coordinates = path.getJSONArray(j);
|
||||||
|
|
||||||
|
StopEntry entry = new UnidentifiedStopEntry(coordinates.getDouble(0),
|
||||||
|
coordinates.getDouble(1));
|
||||||
|
|
||||||
|
stopsPath.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
all.add(stopsPath);
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// Ignoring invalid element!
|
||||||
|
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<String> extractTerminus(String JSON) {
|
||||||
|
List<String> 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<TraceDescription> extractDescription(String JSON) {
|
||||||
|
List<TraceDescription> 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;
|
||||||
|
}
|
||||||
|
}
|
48
src/main/java/fr/u_paris/gla/project/idfm/Stop.java
Normal file
48
src/main/java/fr/u_paris/gla/project/idfm/Stop.java
Normal file
|
@ -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<String, BifStop> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
71
src/main/java/fr/u_paris/gla/project/idfm/StopEntry.java
Normal file
71
src/main/java/fr/u_paris/gla/project/idfm/StopEntry.java
Normal file
|
@ -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<StopEntry> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Integer> 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;
|
||||||
|
}
|
||||||
|
}
|
71
src/main/java/fr/u_paris/gla/project/idfm/TraceEntry.java
Normal file
71
src/main/java/fr/u_paris/gla/project/idfm/TraceEntry.java
Normal file
|
@ -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<String> terminus = new ArrayList<>();
|
||||||
|
private List<List<StopEntry>> paths = new ArrayList<>();
|
||||||
|
List <TraceDescription> 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<List<StopEntry>> getPaths() {
|
||||||
|
// TODO Ne pas retourner directement la liste
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the list of terminus */
|
||||||
|
public List<String> getTerminus() {
|
||||||
|
return terminus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPath(List<StopEntry> 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<TraceDescription> desctipt){
|
||||||
|
descriptions.addAll(desctipt);
|
||||||
|
}
|
||||||
|
}
|
226
src/main/java/fr/u_paris/gla/project/idfm/Transport.java
Normal file
226
src/main/java/fr/u_paris/gla/project/idfm/Transport.java
Normal file
|
@ -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<String, Stop> stopsMap = new HashMap<>();
|
||||||
|
public String name;
|
||||||
|
public String type;
|
||||||
|
public String image_url;
|
||||||
|
|
||||||
|
//All the line descriptions (directions and schedules)
|
||||||
|
List <TraceDescription> 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<Boolean, List<Integer>> sol = roadToLast(debut.name, fin.name, new ArrayList<String>(), new ArrayList<Integer>());
|
||||||
|
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<String> alreadyVisited = new HashSet<>();
|
||||||
|
SimpleEntry<Boolean, List<Integer>> sol = roadToLastOptimized(debut.name, fin.name, alreadyVisited, new ArrayList<Integer>());
|
||||||
|
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<Boolean,List<Integer> > roadToLast(String currentStop, String last, List<String> alreadyVisited, List<Integer> 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<String> visitedCopy = new ArrayList<>(alreadyVisited);
|
||||||
|
visitedCopy.add(currentStop);
|
||||||
|
|
||||||
|
Stop current = stopsMap.get(currentStop);
|
||||||
|
List <SimpleEntry<Boolean,List<Integer>> > solutions = new ArrayList<>();
|
||||||
|
for(BifStop b: current.connected.values()){
|
||||||
|
if(!visitedCopy.contains(b.stop.name)){
|
||||||
|
List<Integer> 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<Integer> bifSol = new ArrayList<>();
|
||||||
|
boolean trouve = false;
|
||||||
|
for(SimpleEntry<Boolean,List<Integer>> 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<Boolean, List<Integer>> roadToLastOptimized(String currentStop, String last, Set<String> alreadyVisited, List<Integer> 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<SimpleEntry<Boolean, List<Integer>>> solutions = new ArrayList<>();
|
||||||
|
for (BifStop b : current.connected.values()) {
|
||||||
|
if (!alreadyVisited.contains(b.stop.name)) {
|
||||||
|
List<Integer> 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<Integer> bifSol = new ArrayList<>();
|
||||||
|
boolean trouve = false;
|
||||||
|
for (SimpleEntry<Boolean, List<Integer>> 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<String, Stop> 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<TraceDescription> desctipt){
|
||||||
|
descriptions.addAll(desctipt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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<StopEntry> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
src/main/java/fr/u_paris/gla/project/io/ImageFormat.java
Normal file
24
src/main/java/fr/u_paris/gla/project/io/ImageFormat.java
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
76
src/main/java/fr/u_paris/gla/project/io/NetworkFormat.java
Normal file
76
src/main/java/fr/u_paris/gla/project/io/NetworkFormat.java
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package fr.u_paris.gla.project.io;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.Temporal;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/** Definition of the formats of files for this project.
|
||||||
|
*
|
||||||
|
* @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 */
|
||||||
|
public static final int LINE_INDEX = 0;
|
||||||
|
/** The index of the variant number of the segment in the network format */
|
||||||
|
public static final int VARIANT_INDEX = 1;
|
||||||
|
/** The index of the segment trip duration in the network format */
|
||||||
|
public static final int DURATION_INDEX = 6;
|
||||||
|
/** The index of the segment distance in the network format */
|
||||||
|
public static final int DISTANCE_INDEX = 7;
|
||||||
|
/** The index of the segment end stop name in the network format */
|
||||||
|
public static final int STOP_INDEX = 4;
|
||||||
|
/** The index of the segment starting stop name in the network format */
|
||||||
|
public static final int START_INDEX = 2;
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DURATION_FORMATTER = DateTimeFormatter
|
||||||
|
.ofPattern("HH:mm:ss");
|
||||||
|
private static final NumberFormat DURATION_INDIVIDUAL_FORMATTER = NumberFormat
|
||||||
|
.getIntegerInstance(Locale.ENGLISH);
|
||||||
|
static {
|
||||||
|
DURATION_INDIVIDUAL_FORMATTER.setMinimumIntegerDigits(2);
|
||||||
|
}
|
||||||
|
private static final Temporal ZERO = LocalTime.parse("00:00:00");
|
||||||
|
|
||||||
|
/** Hidden constructor for utility class */
|
||||||
|
private 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 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 {@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;
|
||||||
|
}
|
||||||
|
}
|
55
src/main/java/fr/u_paris/gla/project/io/ScheduleFormat.java
Normal file
55
src/main/java/fr/u_paris/gla/project/io/ScheduleFormat.java
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
public final class ScheduleFormat {
|
||||||
|
/** 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;
|
||||||
|
/** 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
|
||||||
|
*
|
||||||
|
* @param representation the representation
|
||||||
|
* @return the sequence of branching
|
||||||
|
*/
|
||||||
|
public static List<Integer> getTripSequence(String representation) {
|
||||||
|
|
||||||
|
List<Integer> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Integer> 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<Integer> 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;
|
||||||
|
}
|
||||||
|
}
|
116
src/main/java/fr/u_paris/gla/project/itinerary/Finder.java
Normal file
116
src/main/java/fr/u_paris/gla/project/itinerary/Finder.java
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package fr.u_paris.gla.project.itinerary;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class Finder {
|
||||||
|
|
||||||
|
private Graph graph;
|
||||||
|
public Finder(Graph graph) {
|
||||||
|
this.graph = graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return a path from startNode to goalNode using A* algorithm
|
||||||
|
* @param startNode
|
||||||
|
* @param goalNode
|
||||||
|
*/
|
||||||
|
public List<Path> findPath(Stop startNode, Stop goalNode, double startTime) {
|
||||||
|
|
||||||
|
PriorityQueue<Stop> openSet = new PriorityQueue<>(Comparator.comparingDouble(GraphNode::getF));
|
||||||
|
HashSet<Stop> closedSet = new HashSet<>();
|
||||||
|
HashMap<Stop, Path> cameFrom = new HashMap<>();
|
||||||
|
HashMap<Stop, Double> gScore = new HashMap<>();
|
||||||
|
HashMap<Stop, Double> 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, new Path(current, connection, currentTime));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once we found the destination we reconstruct the path
|
||||||
|
* @param cameFrom
|
||||||
|
* @param current
|
||||||
|
* @return path
|
||||||
|
*/
|
||||||
|
private List<Path> reconstructPath(HashMap<Stop, Path> cameFrom, Stop current) {
|
||||||
|
List<Path> totalPath = new ArrayList<>();
|
||||||
|
totalPath.add(cameFrom.get(current));
|
||||||
|
|
||||||
|
while (cameFrom.containsKey(current)) {
|
||||||
|
current = cameFrom.get(current).getCurrentStop();
|
||||||
|
if(cameFrom.get(current) != null) {
|
||||||
|
totalPath.add(0, cameFrom.get(current)); // Add to the beginning of the list to maintain order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the priority queue
|
||||||
|
* @param openSet
|
||||||
|
* @param node
|
||||||
|
* @param newF
|
||||||
|
*/
|
||||||
|
public void updatePriority(PriorityQueue<Stop> openSet, Stop node, double newF) {
|
||||||
|
openSet.remove(node);
|
||||||
|
node.setF(newF);
|
||||||
|
openSet.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
28
src/main/java/fr/u_paris/gla/project/itinerary/Graph.java
Normal file
28
src/main/java/fr/u_paris/gla/project/itinerary/Graph.java
Normal file
|
@ -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<Stop> nodes;
|
||||||
|
|
||||||
|
private final Map<Stop, Set<Connection>> connections;
|
||||||
|
|
||||||
|
public Graph(Set<Stop> nodes, Map<Stop, Set<Connection>> connections) {
|
||||||
|
this.nodes = nodes;
|
||||||
|
this.connections = connections;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Connection> getConnections(Stop node) {
|
||||||
|
return connections.get(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Stop> getNodes() {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Stop, Set<Connection>> getConnections() {
|
||||||
|
return connections;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Stop> getNeighbors();
|
||||||
|
double getCost(Stop neighbor);
|
||||||
|
double getF();
|
||||||
|
void setF(double value);
|
||||||
|
}
|
244
src/main/java/fr/u_paris/gla/project/itinerary/Parse.java
Normal file
244
src/main/java/fr/u_paris/gla/project/itinerary/Parse.java
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
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 Parse {
|
||||||
|
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.;
|
||||||
|
|
||||||
|
private HashSet<Stop> nodes = new HashSet<>();
|
||||||
|
private HashMap<Stop, Set<Connection>> connections = new HashMap<>();
|
||||||
|
private HashMap<String, ArrayList<Stop>> tmp = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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])};
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, ArrayList<Stop>> getTmp(){
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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<Stop> nodes, HashMap<String, ArrayList<Stop>> tmp, String name, String gps, String lineId, HashMap<Stop, Set<Connection>> connections) {
|
||||||
|
ArrayList<Stop> 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<Stop> 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<Stop> nodes, HashMap<String, ArrayList<Stop>> tmp, HashMap<Stop, Set<Connection>> 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<Integer> bifurcations, int time, HashMap<String, ArrayList<Stop>> stopsHashSet, HashMap<Stop, Set<Connection>> connections, HashSet<Stop> processed){
|
||||||
|
time = time%86400;
|
||||||
|
//If the stop has already been processed, it is not reprocessed.
|
||||||
|
if(processed.contains(current)) {return;}
|
||||||
|
processed.add(current);
|
||||||
|
|
||||||
|
Set<Connection> neighborhood = connections.get(current);
|
||||||
|
if(neighborhood == null) {return;}
|
||||||
|
|
||||||
|
|
||||||
|
ArrayList<Connection> 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 : directions) {
|
||||||
|
if(n.getStop() == next_stop) {
|
||||||
|
n.addSchedule(time);
|
||||||
|
time += n.getTime() + STOP_TIME;
|
||||||
|
addScheduleRec(next_stop, current, line, bifurcations, time, stopsHashSet, connections, processed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSchedule(String[] input, HashMap<String, ArrayList<Stop>> stopsHashSet, HashMap<Stop, Set<Connection>> connections) {
|
||||||
|
|
||||||
|
String line = input[0];
|
||||||
|
|
||||||
|
ArrayList<Integer> 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;
|
||||||
|
|
||||||
|
|
||||||
|
ArrayList<Stop> 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 void parseFiles(){
|
||||||
|
|
||||||
|
IDFMNetworkExtractor.buildFiles();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
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<Connection> set : connections.values()) {
|
||||||
|
for(Connection c : set) {
|
||||||
|
c.sortSchedule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Error while reading the line paths", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Path> getItinerary(Stop src, Stop dst, double startTime ){
|
||||||
|
Graph graph = new Graph(nodes, connections);
|
||||||
|
Finder finder = new Finder(graph);
|
||||||
|
return finder.findPath(src, dst, startTime);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
50
src/main/java/fr/u_paris/gla/project/itinerary/Path.java
Normal file
50
src/main/java/fr/u_paris/gla/project/itinerary/Path.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package fr.u_paris.gla.project.itinerary;
|
||||||
|
|
||||||
|
public class Path {
|
||||||
|
private Stop current;
|
||||||
|
|
||||||
|
private Stop next;
|
||||||
|
|
||||||
|
private double startTime;
|
||||||
|
|
||||||
|
private double travelTime;
|
||||||
|
|
||||||
|
private double distance;
|
||||||
|
|
||||||
|
private String line;
|
||||||
|
|
||||||
|
private Connection connection;
|
||||||
|
|
||||||
|
public Path(Stop current, Connection connection, double startTime) {
|
||||||
|
this.current = current;
|
||||||
|
this.connection = connection;
|
||||||
|
this.next = connection.getStop();
|
||||||
|
this.startTime = startTime;
|
||||||
|
this.travelTime = connection.getTime();
|
||||||
|
this.line = connection.getLineName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getConnection(){
|
||||||
|
return this.connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stop getCurrentStop() {
|
||||||
|
return this.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stop getNextStop() {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getStartTime() {
|
||||||
|
return this.startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double travelTime() {
|
||||||
|
return this.travelTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLine() {
|
||||||
|
return this.line;
|
||||||
|
}
|
||||||
|
}
|
90
src/main/java/fr/u_paris/gla/project/itinerary/Stop.java
Normal file
90
src/main/java/fr/u_paris/gla/project/itinerary/Stop.java
Normal file
|
@ -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<String> 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<Stop> 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<String> getLines() { return this.lines; }
|
||||||
|
}
|
54
src/main/java/fr/u_paris/gla/project/utils/ApiUtils.java
Normal file
54
src/main/java/fr/u_paris/gla/project/utils/ApiUtils.java
Normal file
|
@ -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};
|
||||||
|
}
|
||||||
|
}
|
107
src/main/java/fr/u_paris/gla/project/utils/CSVTools.java
Normal file
107
src/main/java/fr/u_paris/gla/project/utils/CSVTools.java
Normal file
|
@ -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<String[]> 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<String[]> 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<String[]> 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<String[]> 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<String[][]> 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));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
47
src/main/java/fr/u_paris/gla/project/utils/GPS.java
Normal file
47
src/main/java/fr/u_paris/gla/project/utils/GPS.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package fr.u_paris.gla.project.utils;
|
||||||
|
|
||||||
|
/** A utility class for computations related to GPS.
|
||||||
|
*
|
||||||
|
* @author Emmanuel Bigeon */
|
||||||
|
public final class GPS {
|
||||||
|
|
||||||
|
/** The value of a flat angle, in degrees. */
|
||||||
|
private static final int FLAT_ANGLE_DEGREE = 180;
|
||||||
|
/** The (approximated) earth radius in km. */
|
||||||
|
private static final double EARTH_RADIUS = 6_370.0;
|
||||||
|
|
||||||
|
/** Hidden constructor for tool class */
|
||||||
|
private GPS() {
|
||||||
|
// Tool class
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert a degree angle value in a radian angle one.
|
||||||
|
*
|
||||||
|
* @param degree the degree value
|
||||||
|
* @return the radian value */
|
||||||
|
private static double degreeToRadian(double degree) {
|
||||||
|
return degree / FLAT_ANGLE_DEGREE * Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Compute the flying distance between two GPS positions.
|
||||||
|
*
|
||||||
|
* @param latitude1 the latitude of the first position
|
||||||
|
* @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 in km*/
|
||||||
|
public static double distance(double latitude1, double longitude1, double latitude2,
|
||||||
|
double longitude2) {
|
||||||
|
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), 2) * Math.cos(latitude1)
|
||||||
|
* Math.cos(latitude2);
|
||||||
|
return 2 * EARTH_RADIUS * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
app.name=${name}
|
app.name=${name}
|
||||||
app.version=${version}
|
app.version=${version}
|
||||||
app.team=No team
|
app.team=Team 3
|
|
@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test;
|
||||||
class AppTest {
|
class AppTest {
|
||||||
/** Rigorous Test :-) */
|
/** Rigorous Test :-) */
|
||||||
@Test
|
@Test
|
||||||
void testPlaceholder() {
|
public void testPlaceholder() {
|
||||||
assertTrue(true, "It should be true that true is true...");
|
assertTrue(true, "It should be true that true is true...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<StopEntry> 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.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<List<StopEntry>> 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<StopEntry> 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.");
|
||||||
|
}
|
||||||
|
}
|
63
src/test/java/fr/u_paris/gla/project/idfm/StopEntryTest.java
Normal file
63
src/test/java/fr/u_paris/gla/project/idfm/StopEntryTest.java
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
68
src/test/java/fr/u_paris/gla/project/idfm/StopTest.java
Normal file
68
src/test/java/fr/u_paris/gla/project/idfm/StopTest.java
Normal file
|
@ -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"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<String> 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<StopEntry> path = Arrays.asList(stop1, stop2);
|
||||||
|
traceEntry.addPath(path);
|
||||||
|
List<List<StopEntry>> 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'.");
|
||||||
|
}
|
||||||
|
}
|
64
src/test/java/fr/u_paris/gla/project/idfm/TransportTest.java
Normal file
64
src/test/java/fr/u_paris/gla/project/idfm/TransportTest.java
Normal file
|
@ -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<String> visited = new ArrayList<>();
|
||||||
|
List<Integer> bifurcations = new ArrayList<>();
|
||||||
|
SimpleEntry<Boolean, List<Integer>> result = transport.roadToLast("A", "D", visited, bifurcations);
|
||||||
|
assertFalse(result.getKey());
|
||||||
|
assertEquals(List.of(1, 2, 3), result.getValue());
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*@Test
|
||||||
|
public void testRoadToLastOptimized() {
|
||||||
|
SimpleEntry<Boolean, List<Integer>> 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"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(" ",""));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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<Integer> 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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
88
src/test/java/fr/u_paris/gla/project/utils/CSVToolsTest.java
Normal file
88
src/test/java/fr/u_paris/gla/project/utils/CSVToolsTest.java
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package fr.u_paris.gla.project.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
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<String[]> test = s -> System.out.println(Arrays.toString(s));
|
||||||
|
CSVTools.readCSVFromURL("https://google.fr",
|
||||||
|
test);
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testreadCSVFromURL_valid() {
|
||||||
|
assertDoesNotThrow(() -> {
|
||||||
|
Consumer<String[]> test = s -> System.out.println(Arrays.toString(s));
|
||||||
|
CSVTools.readCSVFromURL("https://people.sc.fsu.edu/~jburkardt/data/csv/addresses.csv",
|
||||||
|
test);
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String randomUniqueFilename() {
|
||||||
|
String prefix = "file_";
|
||||||
|
String randomString = UUID.randomUUID().toString().substring(0, 8);
|
||||||
|
return prefix + randomString + ".csv";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void writeCSVToFile() {
|
||||||
|
String fileName = randomUniqueFilename();
|
||||||
|
assertDoesNotThrow(() -> {
|
||||||
|
String[] stuff = {"jsqdsqdsqsqffdfgzava", "pfezegrrbeebn", "dfbsduifzegbczi", "sdfsdfcy"};
|
||||||
|
String[][] t = {stuff, stuff};
|
||||||
|
Stream<String[]> test = Arrays.stream(t);
|
||||||
|
CSVTools.writeCSVToFile(fileName, test);
|
||||||
|
File tmp = new File(fileName);
|
||||||
|
tmp.delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void writeCSVToFile_specialName() {
|
||||||
|
String fileName = randomUniqueFilename();
|
||||||
|
assertDoesNotThrow(() -> {
|
||||||
|
String[] stuff = {"jsqdsqdsqsqffdfgzava", "pfezegrrbeebn", "dfbsduifzegbczi", "sdfsdfcy"};
|
||||||
|
String[][] t = {stuff, stuff};
|
||||||
|
Stream<String[]> test = Arrays.stream(t);
|
||||||
|
CSVTools.writeCSVToFile(fileName, test);
|
||||||
|
File tmp = new File(fileName);
|
||||||
|
tmp.delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void writeCSVToFile_invalidName() {
|
||||||
|
|
||||||
|
assertThrows( IOException.class ,() -> {
|
||||||
|
String[] stuff = {"jsqdsqdsqsqffdfgzava", "pfezegrrbeebn", "dfbsduifzegbczi", "sdfsdfcy"};
|
||||||
|
String[][] t = {stuff, stuff};
|
||||||
|
Stream<String[]> test = Arrays.stream(t);
|
||||||
|
CSVTools.writeCSVToFile(".", test);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
39
src/test/java/fr/u_paris/gla/project/utils/GPSTest.java
Normal file
39
src/test/java/fr/u_paris/gla/project/utils/GPSTest.java
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
1950
trace_idfm.csv
Normal file
1950
trace_idfm.csv
Normal file
File diff suppressed because one or more lines are too long
Reference in a new issue