Merge branch 'idf-mobilite-net-graph' into 'idf-mobilite-net'
Implementation of the pathfinding algorithm See merge request gla-groupe-3/projet!6
This commit is contained in:
commit
6c678bfa21
10 changed files with 432 additions and 15 deletions
2
pom.xml
2
pom.xml
|
@ -62,7 +62,7 @@
|
||||||
<manifest>
|
<manifest>
|
||||||
<addClasspath>true</addClasspath>
|
<addClasspath>true</addClasspath>
|
||||||
<classpathPrefix>${project.build.finalName}.lib/</classpathPrefix>
|
<classpathPrefix>${project.build.finalName}.lib/</classpathPrefix>
|
||||||
<mainClass>fr.u_paris.gla.project.idfm.IDFMNetworkExtractor</mainClass>
|
<mainClass>fr.u_paris.gla.project.itinerary.Main</mainClass>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
91
src/main/java/fr/u_paris/gla/project/itinerary/Finder.java
Normal file
91
src/main/java/fr/u_paris/gla/project/itinerary/Finder.java
Normal file
|
@ -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<Stop> findPath(Stop startNode, Stop goalNode) {
|
||||||
|
PriorityQueue<Stop> openSet = new PriorityQueue<>(Comparator.comparingDouble(GraphNode::getF));
|
||||||
|
HashSet<Stop> closedSet = new HashSet<>();
|
||||||
|
HashMap<Stop, Stop> 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 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<Stop> reconstructPath(HashMap<Stop, Stop> cameFrom, Stop current) {
|
||||||
|
List<Stop> 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<Stop> findPath(double longitude, double latitude){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
29
src/main/java/fr/u_paris/gla/project/itinerary/Graph.java
Normal file
29
src/main/java/fr/u_paris/gla/project/itinerary/Graph.java
Normal file
|
@ -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<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);
|
||||||
|
}
|
137
src/main/java/fr/u_paris/gla/project/itinerary/Main.java
Normal file
137
src/main/java/fr/u_paris/gla/project/itinerary/Main.java
Normal file
|
@ -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<Stop> nodes, HashMap<String, ArrayList<Stop>> tmp, String name, String gps, String lineId) {
|
||||||
|
ArrayList<Stop> 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<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]);
|
||||||
|
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<Stop> nodes = new HashSet<>();
|
||||||
|
HashMap<Stop, Set<Connection>> connections = new HashMap<>();
|
||||||
|
HashMap<String, ArrayList<Stop>> 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<Stop, Set<Connection>> 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<Stop> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
18
src/main/java/fr/u_paris/gla/project/itinerary/Path.java
Normal file
18
src/main/java/fr/u_paris/gla/project/itinerary/Path.java
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package fr.u_paris.gla.project.itinerary;
|
||||||
|
|
||||||
|
public class Path <T extends GraphNode> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
86
src/main/java/fr/u_paris/gla/project/itinerary/Stop.java
Normal file
86
src/main/java/fr/u_paris/gla/project/itinerary/Stop.java
Normal file
|
@ -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<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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,12 +3,7 @@
|
||||||
*/
|
*/
|
||||||
package fr.u_paris.gla.project.utils;
|
package fr.u_paris.gla.project.utils;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.*;
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -32,12 +27,11 @@ public final class CSVTools {
|
||||||
// Tool class
|
// Tool class
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void readCSVFromURL(String url, Consumer<String[]> contentLineConsumer)
|
private static void readCSVFromInputStream(InputStream is, Consumer<String[]> contentLineConsumer)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
ICSVParser parser = new CSVParserBuilder().withSeparator(';').build();
|
ICSVParser parser = new CSVParserBuilder().withSeparator(';').build();
|
||||||
try (InputStream is = new URL(url).openStream();
|
try (Reader reader = new BufferedReader(
|
||||||
Reader reader = new BufferedReader(
|
new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
||||||
new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
|
||||||
CSVReaderBuilder csvBuilder = new CSVReaderBuilder(reader)
|
CSVReaderBuilder csvBuilder = new CSVReaderBuilder(reader)
|
||||||
.withCSVParser(parser);
|
.withCSVParser(parser);
|
||||||
try (CSVReader csv = csvBuilder.build()) {
|
try (CSVReader csv = csvBuilder.build()) {
|
||||||
|
@ -52,6 +46,17 @@ public final class CSVTools {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void readCSVFromFile(String filename, Consumer<String[]> contentLineConsumer)
|
||||||
|
throws IOException {
|
||||||
|
File file = new File(filename);
|
||||||
|
readCSVFromInputStream(new FileInputStream(file), contentLineConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void readCSVFromURL(String url, Consumer<String[]> contentLineConsumer)
|
||||||
|
throws IOException {
|
||||||
|
readCSVFromInputStream(new URL(url).openStream(), contentLineConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
public static void writeCSVToFile(String filename,
|
public static void writeCSVToFile(String filename,
|
||||||
Stream<String[]> contentLineConsumer) throws IOException {
|
Stream<String[]> contentLineConsumer) throws IOException {
|
||||||
try (FileWriter writer = new FileWriter(filename, StandardCharsets.UTF_8)) {
|
try (FileWriter writer = new FileWriter(filename, StandardCharsets.UTF_8)) {
|
||||||
|
|
|
@ -30,14 +30,16 @@ public final class GPS {
|
||||||
* @param longitude1 the longitude of the first position
|
* @param longitude1 the longitude of the first position
|
||||||
* @param latitude2 the latitude of the second position
|
* @param latitude2 the latitude of the second position
|
||||||
* @param longitude2 the longitude 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,
|
public static double distance(double latitude1, double longitude1, double latitude2,
|
||||||
double longitude2) {
|
double longitude2) {
|
||||||
double deltaLatitude = degreeToRadian(latitude2 - latitude1);
|
latitude1 = degreeToRadian(latitude1);
|
||||||
|
latitude2 = degreeToRadian(latitude2);
|
||||||
|
double deltaLatitude = latitude2 - latitude1;
|
||||||
double deltaLongitude = degreeToRadian(longitude2 - longitude1);
|
double deltaLongitude = degreeToRadian(longitude2 - longitude1);
|
||||||
double a = Math.pow(Math.sin(deltaLatitude / 2), 2)
|
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);
|
* 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue