diff --git a/pom.xml b/pom.xml index 6eb663f..89cd54d 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ true ${project.build.finalName}.lib/ - fr.u_paris.gla.project.idfm.IDFMNetworkExtractor + fr.u_paris.gla.project.itinerary.Main diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Connection.java b/src/main/java/fr/u_paris/gla/project/itinerary/Connection.java new file mode 100644 index 0000000..ccc3bfd --- /dev/null +++ b/src/main/java/fr/u_paris/gla/project/itinerary/Connection.java @@ -0,0 +1,35 @@ +package fr.u_paris.gla.project.itinerary; + +public class Connection{ + private final Stop stop; + + private final String lineName; + + private final double distance; + + private final int time; + + public Connection(Stop stop, String lineName, double distance, int time){ + this.stop = stop; + this.lineName=lineName; + this.distance = distance; + this.time = time; + } + + + public String getLineName() { + return lineName; + } + + public double getDistance() { + return distance; + } + + public int getTime() { + return time; + } + + public Stop getStop() { + return stop; + } +} diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Finder.java b/src/main/java/fr/u_paris/gla/project/itinerary/Finder.java new file mode 100644 index 0000000..f91047d --- /dev/null +++ b/src/main/java/fr/u_paris/gla/project/itinerary/Finder.java @@ -0,0 +1,91 @@ +package fr.u_paris.gla.project.itinerary; + +import java.util.*; + +public class Finder { + private Graph graph; + public Finder(Graph graph) { + this.graph = graph; + } + + public List findPath(Stop startNode, Stop goalNode) { + PriorityQueue openSet = new PriorityQueue<>(Comparator.comparingDouble(GraphNode::getF)); + HashSet closedSet = new HashSet<>(); + HashMap cameFrom = new HashMap<>(); + HashMap gScore = new HashMap<>(); + HashMap fScore = new HashMap<>(); + + // Initialize scores for all nodes to infinity + for (Stop node : graph.getNodes()) { + gScore.put(node, Double.POSITIVE_INFINITY); + fScore.put(node, Double.POSITIVE_INFINITY); + } + + // The cost of going from start to start is zero + gScore.put(startNode, 0.0); + // For the first node, fScore = gScore + heuristic + fScore.put(startNode, startNode.getHeuristicCost(goalNode)); + openSet.add(startNode); + + while (!openSet.isEmpty()) { + Stop current = openSet.poll(); + //System.out.println(current); + //System.out.println(graph.getConnections(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 = gScore.get(current) + connection.getDistance(); + + if (!openSet.contains(neighbor)) { + openSet.add(neighbor); + } else if (tentativeGScore >= gScore.get(neighbor)) { + continue; // This is not a better path. + } + + // This path is the best until now. Record it! + cameFrom.put(neighbor, current); + gScore.put(neighbor, tentativeGScore); + fScore.put(neighbor, tentativeGScore + neighbor.getHeuristicCost(goalNode)); + neighbor.setF(fScore.get(neighbor)); + } + } + + // If we reach here, it means there's no path from start to goal + return null; + } + + private List reconstructPath(HashMap cameFrom, Stop current) { + List totalPath = new ArrayList<>(); + totalPath.add(current); + + while (cameFrom.containsKey(current)) { + current = cameFrom.get(current); + totalPath.add(0, current); // Add to the beginning of the list to maintain order + } + + return totalPath; + } + + //TODO: + public List findPath(double longitude, double latitude){ + return null; + } + + +} + + diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Graph.java b/src/main/java/fr/u_paris/gla/project/itinerary/Graph.java new file mode 100644 index 0000000..3025a04 --- /dev/null +++ b/src/main/java/fr/u_paris/gla/project/itinerary/Graph.java @@ -0,0 +1,29 @@ +package fr.u_paris.gla.project.itinerary; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class Graph{ + private final Set nodes; + + private final Map> connections; + + public Graph(Set nodes, Map> connections) { + this.nodes = nodes; + this.connections = connections; + } + + public Set getConnections(Stop node) { + return connections.get(node); + } + + public Set getNodes() { + return nodes; + } + + + public Map> getConnections() { + return connections; + } +} diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/GraphNode.java b/src/main/java/fr/u_paris/gla/project/itinerary/GraphNode.java new file mode 100644 index 0000000..175db6e --- /dev/null +++ b/src/main/java/fr/u_paris/gla/project/itinerary/GraphNode.java @@ -0,0 +1,14 @@ +package fr.u_paris.gla.project.itinerary; + +import java.util.List; +import java.util.Set; + +public interface GraphNode { + int getId(); + double getHeuristicCost(Stop goalNode); + + Set getNeighbors(); + double getCost(Stop neighbor); + double getF(); + void setF(double value); +} diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Main.java b/src/main/java/fr/u_paris/gla/project/itinerary/Main.java new file mode 100644 index 0000000..5a75b34 --- /dev/null +++ b/src/main/java/fr/u_paris/gla/project/itinerary/Main.java @@ -0,0 +1,137 @@ +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 Main{ + 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 MARGE_ERREUR = 1.; + + // Parser helper values + + /** + * 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])}; + } + + private static Stop getOrCreateStop(HashSet nodes, HashMap> tmp, String name, String gps, String lineId) { + ArrayList stopList = tmp.get(name); + if (stopList != null) { + for(Stop stop : stopList) { + double[] coords = getCoords(gps); + double dist = GPS.distance(coords[0], coords[1], stop.getLatitude(), stop.getLongitude()); + if(dist < MARGE_ERREUR) { + stop.addLine(lineId); + return stop; + } + } + } + + double[] coords = getCoords(gps); + 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); + return newStop; + } + + public static int lineNB = 0; + + private static void addLine(String[] line, HashSet nodes, HashMap> tmp, HashMap> connections) { + Stop fromStop = getOrCreateStop(nodes, tmp, line[IDFM_TRACE_FROM_INDEX], line[IDFM_TRACE_FROM_GPS_INDEX], line[IDFM_TRACE_ID_INDEX]); + Stop toStop = getOrCreateStop(nodes, tmp, line[IDFM_TRACE_TO_INDEX], line[IDFM_TRACE_TO_GPS_INDEX], line[IDFM_TRACE_ID_INDEX]); + + String[] timeString = line[IDFM_TRACE_TIME_INDEX].split(":"); + int time = Integer.parseInt(timeString[0]) * 60 + Integer.parseInt(timeString[1]); + + Connection connection = new Connection(toStop, line[IDFM_TRACE_ID_INDEX], Double.parseDouble(line[IDFM_TRACE_DISTANCE_INDEX]), time); + + connections.computeIfAbsent(fromStop, k -> new HashSet<>()).add(connection); + } + + public static void find(Graph graph){ + + + } + + public static void main(String[] args){ + if (args.length != 0) { + LOGGER.severe("Invalid command line. Target file names are in the main file for now."); + return; + } + + try { + HashSet nodes = new HashSet<>(); + HashMap> connections = new HashMap<>(); + HashMap> tmp = new HashMap<>(); + CSVTools.readCSVFromFile(TRACE_FILE_NAME, + (String[] line) -> addLine(line, nodes, tmp, connections)); + + + Stop porteivry = tmp.get("Porte d'Ivry").get(0); + Stop repu = tmp.get("République").get(0); + + Graph graph = new Graph(nodes, connections); + int cpt = 0; + for (Map.Entry> entry : graph.getConnections().entrySet()) { + if (entry.getValue() == null) cpt +=1; + } + Stop garenord = tmp.get("Gare du Nord").get(0); + + Stop chatelet = tmp.get("Châtelet").get(0); + //System.out.println(graph.getConnections(garenord)); + //System.out.println(cpt); + //System.out.println(graph.getConnections(porteivry)); + Finder finder = new Finder(graph); + + List res = finder.findPath(porteivry, chatelet); + + for (Stop element : res) { + System.out.println(element); + } + + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error while reading the line paths", e); + } + + + } +} diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Path.java b/src/main/java/fr/u_paris/gla/project/itinerary/Path.java new file mode 100644 index 0000000..a492b99 --- /dev/null +++ b/src/main/java/fr/u_paris/gla/project/itinerary/Path.java @@ -0,0 +1,18 @@ +package fr.u_paris.gla.project.itinerary; + +public class Path { + private T current; + + private T previous; + + private double currentScore; + + private final double targetScore; + + public Path(T current, T previous, double currentScore, double targetScore) { + this.current = current; + this.previous = previous; + this.currentScore = currentScore; + this.targetScore = targetScore; + } +} diff --git a/src/main/java/fr/u_paris/gla/project/itinerary/Stop.java b/src/main/java/fr/u_paris/gla/project/itinerary/Stop.java new file mode 100644 index 0000000..240c9b0 --- /dev/null +++ b/src/main/java/fr/u_paris/gla/project/itinerary/Stop.java @@ -0,0 +1,86 @@ +package fr.u_paris.gla.project.itinerary; + +import java.util.HashSet; +import java.util.Set; + +public class Stop implements GraphNode { + private static int counter = 0; + + private final int id; + + private final Set lines; + + private final String name; + + private final double latitude; + + private final double longitude; + + private double f; + + public Stop(String line, String name, double latitude, double longitude) { + lines = new HashSet<>(); + lines.add(line); + this.id = counter++; + this.name = name; + this.latitude = latitude; + this.longitude = longitude; + this.f = 0; + } + + @Override + public String toString() { + return "Stop{" + + "id=" + id + + ", lines=" + lines + + ", name='" + name + '\'' + + ", latitude=" + latitude + + ", longitude=" + longitude + + '}'; + } + + @Override + public int getId(){ + return id; + } + + @Override + public double getHeuristicCost(Stop goalNode) { + return 0; + } + + @Override + public Set getNeighbors() { + return null; + } + + @Override + public double getCost(Stop neighbor) { + return 0; + } + + @Override + public double getF() { + return f; + } + + public void setF(double value) { + this.f = value; + } + + public String getName(){ + return name; + } + + public double getLatitude(){ + return latitude; + } + + public double getLongitude(){ + return longitude; + } + + public void addLine(String s){ + lines.add(s); + } +} diff --git a/src/main/java/fr/u_paris/gla/project/utils/CSVTools.java b/src/main/java/fr/u_paris/gla/project/utils/CSVTools.java index b3194f9..7d50613 100644 --- a/src/main/java/fr/u_paris/gla/project/utils/CSVTools.java +++ b/src/main/java/fr/u_paris/gla/project/utils/CSVTools.java @@ -3,12 +3,7 @@ */ package fr.u_paris.gla.project.utils; -import java.io.BufferedReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; +import java.io.*; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; @@ -32,12 +27,11 @@ public final class CSVTools { // Tool class } - public static void readCSVFromURL(String url, Consumer contentLineConsumer) + private static void readCSVFromInputStream(InputStream is, Consumer contentLineConsumer) throws IOException { ICSVParser parser = new CSVParserBuilder().withSeparator(';').build(); - try (InputStream is = new URL(url).openStream(); - Reader reader = new BufferedReader( - new InputStreamReader(is, StandardCharsets.UTF_8))) { + try (Reader reader = new BufferedReader( + new InputStreamReader(is, StandardCharsets.UTF_8))) { CSVReaderBuilder csvBuilder = new CSVReaderBuilder(reader) .withCSVParser(parser); try (CSVReader csv = csvBuilder.build()) { @@ -52,6 +46,17 @@ public final class CSVTools { } } + public static void readCSVFromFile(String filename, Consumer contentLineConsumer) + throws IOException { + File file = new File(filename); + readCSVFromInputStream(new FileInputStream(file), contentLineConsumer); + } + + public static void readCSVFromURL(String url, Consumer contentLineConsumer) + throws IOException { + readCSVFromInputStream(new URL(url).openStream(), contentLineConsumer); + } + public static void writeCSVToFile(String filename, Stream contentLineConsumer) throws IOException { try (FileWriter writer = new FileWriter(filename, StandardCharsets.UTF_8)) { diff --git a/src/main/java/fr/u_paris/gla/project/utils/GPS.java b/src/main/java/fr/u_paris/gla/project/utils/GPS.java index 1aaca79..0eb7130 100644 --- a/src/main/java/fr/u_paris/gla/project/utils/GPS.java +++ b/src/main/java/fr/u_paris/gla/project/utils/GPS.java @@ -30,14 +30,16 @@ public final class GPS { * @param longitude1 the longitude of the first position * @param latitude2 the latitude of the second position * @param longitude2 the longitude of the second position - * @return the flying distance */ + * @return the flying distance in km*/ public static double distance(double latitude1, double longitude1, double latitude2, double longitude2) { - double deltaLatitude = degreeToRadian(latitude2 - latitude1); + latitude1 = degreeToRadian(latitude1); + latitude2 = degreeToRadian(latitude2); + double deltaLatitude = latitude2 - latitude1; double deltaLongitude = degreeToRadian(longitude2 - longitude1); double a = Math.pow(Math.sin(deltaLatitude / 2), 2) - + Math.pow(Math.sin(deltaLongitude), 2) * Math.cos(latitude1) + + Math.pow(Math.sin(deltaLongitude / 2), 2) * Math.cos(latitude1) * Math.cos(latitude2); - return EARTH_RADIUS * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + return 2 * EARTH_RADIUS * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); } }