Initial commit.
This commit is contained in:
commit
65c81b9921
23
.classpath
Normal file
23
.classpath
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main"/>
|
||||
<classpathentry kind="src" path="src/test"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
|
||||
<classpathentry kind="lib" path="libs/piccolo2d-core-3.0.jar">
|
||||
<attributes>
|
||||
<attribute name="javadoc_location" value="jar:platform:/resource/be-graphes-base/libs/piccolo2d-core-3.0-javadoc.jar!/"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="libs/piccolo2d-extras-3.0.jar">
|
||||
<attributes>
|
||||
<attribute name="javadoc_location" value="jar:platform:/resource/be-graphes-base/libs/piccolo2d-extras-3.0-javadoc.jar!/"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="libs/piccolo2d-swt-3.0.jar">
|
||||
<attributes>
|
||||
<attribute name="javadoc_location" value="jar:platform:/resource/be-graphes-base/libs/piccolo2d-swt-3.0-javadoc.jar!/"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
17
.project
Normal file
17
.project
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>be-graphes-base</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
61
FORMAT
Normal file
61
FORMAT
Normal file
@ -0,0 +1,61 @@
|
||||
=== Format des fichiers .map ===
|
||||
|
||||
- Version du document (= version du format) : 4
|
||||
|
||||
- Sauf mention contraire, les entiers sont codés en big endian (compatible DataOutputStream).
|
||||
|
||||
[No d'octets] = signification
|
||||
|
||||
[0-3] = Magic number 0xbacaff (doit se trouver au début du fichier)
|
||||
[4-7] = Version du format
|
||||
[8-11] = Identifiant de carte
|
||||
[12-15] = Numéro de zone
|
||||
[16-19] = Nombre de descripteurs dans ce fichier
|
||||
[20-23] = Nombre de noeuds dans ce fichier
|
||||
|
||||
[24-..] =
|
||||
* Tous les noeuds, les uns après les autres, en commençant par le numéro 0. Voir le format d'un noeud.
|
||||
* Puis un octet à 255.
|
||||
|
||||
* Puis, tous les descripteurs, les uns après les autres, en commençant par le numéro 0.
|
||||
Voir le format des descripteurs.
|
||||
* Puis un octet à 254.
|
||||
|
||||
* Puis, toutes les routes sortantes (routes sortantes du premier noeud, puis celles du deuxième noeud, etc. )
|
||||
* Puis un octet à 253.
|
||||
|
||||
(fin du fichier)
|
||||
|
||||
|
||||
=== Format des noeuds ===
|
||||
|
||||
[0-3] = longitude sur 32 bits (à diviser par 1E6)
|
||||
[4-7] = latitude sur 32 bits (à diviser par 1E6)
|
||||
[8] = Nombre de routes sortantes sur 8 bits
|
||||
|
||||
|
||||
=== Format des routes sortantes (taille variable car dépend du nombre de segments) ===
|
||||
|
||||
[0] = Numéro de zone du noeud destination (8 bits)
|
||||
[1-3] = Numéro du noeud destination, dans la zone donnée (24 bits, big endian)
|
||||
[4-6] = Numéro de descripteur (24 bits)
|
||||
[7-8] = Longueur de l'arête (16 bits), en mètres, prenant en compte tous les segments.
|
||||
[9-10] = Nombre de segments (16 bits), éventuellement 0.
|
||||
[11-...] = Segments
|
||||
|
||||
|
||||
=== Format des segments ===
|
||||
|
||||
[0-1] = Delta de longitude, sur 16 bits signés (à diviser par 2.0E5)
|
||||
[2-3] = Delta de latitude, sur 16 bits signés (à diviser par 2.0E5)
|
||||
|
||||
=== Format des descripteurs (la taille est variable, car elle dépend du nom du chemin) ===
|
||||
|
||||
[0] = Un caractère indiquant le type de chemin (voir dans Descripteur.java)
|
||||
[1]
|
||||
.bit 7 = sens unique
|
||||
.bits 0-6 = vitesse max en km/h à multiplier par 5.
|
||||
|
||||
[2-] = Nom du chemin, de type String-UTF8 (les deux premiers octets donnent la longueur de la chaîne)
|
||||
|
||||
|
21
FORMAT_PATH
Normal file
21
FORMAT_PATH
Normal file
@ -0,0 +1,21 @@
|
||||
=== Format des fichiers .path ===
|
||||
|
||||
- Version du document (= version du format) : 1
|
||||
|
||||
- Sauf mention contraire, les entiers sont codés en big endian (compatible DataOutputStream).
|
||||
|
||||
[No d'octets] = signification
|
||||
|
||||
[0-3] = Magic number 0xdecafe (doit se trouver au début du fichier)
|
||||
[4-7] = Version du format
|
||||
[8-11] = Identifiant de carte
|
||||
[12-15] = Nombre de noeuds dans le chemin
|
||||
[16-19] = Identifiant du premier noeud (8 bits zone + 24 bits numéro noeud)
|
||||
[20-23] = Identifiant du dernier noeud (8 bits zone + 24 bits numéro noeud)
|
||||
|
||||
[24-27] = Identifiant du premier noeud (encore)
|
||||
[28-31] = Identifiant du deuxième noeud
|
||||
[32-35] = Identifiant du troisième noeud
|
||||
etc.
|
||||
[derniers octets] = Identifiant du dernier noeud
|
||||
|
59
src/main/org/insa/algo/AbstractAlgorithm.java
Normal file
59
src/main/org/insa/algo/AbstractAlgorithm.java
Normal file
@ -0,0 +1,59 @@
|
||||
package org.insa.algo ;
|
||||
|
||||
import java.io.* ;
|
||||
|
||||
public abstract class AbstractAlgorithm {
|
||||
|
||||
protected PrintStream output;
|
||||
protected AbstractInstance instance;
|
||||
protected AbstractSolution solution;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param instance
|
||||
* @param logOutput
|
||||
*/
|
||||
protected AbstractAlgorithm(AbstractInstance instance, PrintStream logOutput) {
|
||||
this.instance = instance;
|
||||
this.output = logOutput;
|
||||
this.solution = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current solution.
|
||||
*
|
||||
* @param solution New solution, or null to unset the current solution.
|
||||
*
|
||||
*/
|
||||
protected void updateLastSolution(AbstractSolution solution) {
|
||||
this.solution = solution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Instance corresponding to this algorithm.
|
||||
*/
|
||||
public AbstractInstance getInstance() { return instance; }
|
||||
|
||||
/**
|
||||
* @return Last solution, or null if no solution was stored.
|
||||
*/
|
||||
public AbstractSolution getLastSolution() { return solution; }
|
||||
|
||||
/**
|
||||
* Run the algorithm and update the current solution.
|
||||
*
|
||||
* @return true if a feasible solution was found (even non-optimal).
|
||||
*/
|
||||
public boolean run() {
|
||||
this.solution = this.doRun();
|
||||
return this.solution != null && this.solution.isFeasible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method that should be implemented by child class.
|
||||
*
|
||||
* @return A solution, if one was found, or null.
|
||||
*/
|
||||
protected abstract AbstractSolution doRun();
|
||||
|
||||
}
|
20
src/main/org/insa/algo/AbstractInstance.java
Normal file
20
src/main/org/insa/algo/AbstractInstance.java
Normal file
@ -0,0 +1,20 @@
|
||||
package org.insa.algo;
|
||||
|
||||
import org.insa.graph.Graph;
|
||||
|
||||
public abstract class AbstractInstance {
|
||||
|
||||
protected Graph graph;
|
||||
|
||||
/**
|
||||
* Create a new abstract instance with the given graph.
|
||||
*
|
||||
* @param graph
|
||||
*/
|
||||
protected AbstractInstance(Graph graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
|
||||
public Graph getGraph() { return graph; }
|
||||
|
||||
}
|
67
src/main/org/insa/algo/AbstractSolution.java
Normal file
67
src/main/org/insa/algo/AbstractSolution.java
Normal file
@ -0,0 +1,67 @@
|
||||
package org.insa.algo;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public abstract class AbstractSolution {
|
||||
|
||||
/**
|
||||
* Possible status for a solution.
|
||||
*
|
||||
*/
|
||||
public enum Status {
|
||||
UNKNOWN,
|
||||
INFEASIBLE,
|
||||
FEASIBLE,
|
||||
OPTIMAL,
|
||||
};
|
||||
|
||||
// Status of the solution.
|
||||
Status status;
|
||||
|
||||
// Solving time for the solution
|
||||
Duration solvingTime;
|
||||
|
||||
// Original instance of the solution
|
||||
AbstractInstance instance;
|
||||
|
||||
/**
|
||||
* Create a new abstract solution with unknown status.
|
||||
*
|
||||
* @param instance
|
||||
*/
|
||||
protected AbstractSolution(AbstractInstance instance) {
|
||||
this.instance = instance;
|
||||
this.solvingTime = Duration.ZERO;
|
||||
this.status = Status.UNKNOWN;
|
||||
}
|
||||
|
||||
protected AbstractSolution(AbstractInstance instance,
|
||||
Duration solvingTime, Status status) {
|
||||
this.instance = instance;
|
||||
this.solvingTime = solvingTime;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Original instance for this solution.
|
||||
*/
|
||||
public AbstractInstance getInstance() { return instance; }
|
||||
|
||||
/**
|
||||
* @return Status of this solution.
|
||||
*/
|
||||
public Status getStatus() { return status; }
|
||||
|
||||
/**
|
||||
* @return Solving time of this solution.
|
||||
*/
|
||||
public Duration getSolvingTime() { return solvingTime; }
|
||||
|
||||
/**
|
||||
* @return true if the solution is feasible or optimal.
|
||||
*/
|
||||
public boolean isFeasible() {
|
||||
return status == Status.FEASIBLE || status == Status.OPTIMAL;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package org.insa.algo.connectivity ;
|
||||
|
||||
import java.io.* ;
|
||||
|
||||
import org.insa.algo.AbstractAlgorithm;
|
||||
import org.insa.algo.AbstractSolution;
|
||||
|
||||
public class ConnectivityAlgorithm extends AbstractAlgorithm {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param instance
|
||||
* @param logOutput
|
||||
*/
|
||||
public ConnectivityAlgorithm(ConnectivityInstance instance, PrintStream logOutput) {
|
||||
super(instance, logOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected AbstractSolution doRun() {
|
||||
ConnectivityInstance instance = (ConnectivityInstance)getInstance();
|
||||
ConnectivitySolution solution = null;
|
||||
// TODO:
|
||||
return solution;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package org.insa.algo.connectivity;
|
||||
|
||||
import org.insa.algo.AbstractInstance;
|
||||
import org.insa.graph.Graph;
|
||||
|
||||
public class ConnectivityInstance extends AbstractInstance {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param graph
|
||||
*/
|
||||
public ConnectivityInstance(Graph graph) {
|
||||
super(graph);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.insa.algo.connectivity;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.insa.algo.AbstractSolution;
|
||||
|
||||
public class ConnectivitySolution extends AbstractSolution {
|
||||
|
||||
protected ConnectivitySolution(ConnectivityInstance instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
protected ConnectivitySolution(ConnectivityInstance instance,
|
||||
Duration solvingTime, Status status) {
|
||||
super(instance, solvingTime, status);
|
||||
|
||||
// TODO:
|
||||
}
|
||||
|
||||
}
|
58
src/main/org/insa/base/Couleur.java
Normal file
58
src/main/org/insa/base/Couleur.java
Normal file
@ -0,0 +1,58 @@
|
||||
package org.insa.base ;
|
||||
|
||||
/**
|
||||
* Choix des couleurs pour l'affichage.
|
||||
*/
|
||||
|
||||
import java.awt.* ;
|
||||
|
||||
import org.insa.drawing.Drawing;
|
||||
|
||||
public class Couleur {
|
||||
|
||||
static final Color autoroute = Color.red ;
|
||||
static final Color bigroute = new Color(255, 105, 0) ;
|
||||
static final Color tiroute = new Color(255, 234, 0) ;
|
||||
static final Color cote = Color.blue ;
|
||||
|
||||
public static void set(Drawing d, char type) {
|
||||
|
||||
// Voir le fichier Descripteur.java pour le type des routes.
|
||||
switch (type) {
|
||||
case 'a':
|
||||
d.setWidth(2) ;
|
||||
d.setColor(Color.red) ;
|
||||
break ;
|
||||
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'g':
|
||||
d.setWidth(1) ;
|
||||
d.setColor(bigroute) ;
|
||||
break ;
|
||||
case 'h':
|
||||
case 'i':
|
||||
case 'j':
|
||||
case 'k':
|
||||
case 'l':
|
||||
case 'm':
|
||||
case 'n':
|
||||
case 'o':
|
||||
d.setWidth(1) ;
|
||||
d.setColor(tiroute) ;
|
||||
break ;
|
||||
|
||||
case 'z':
|
||||
d.setWidth(4) ;
|
||||
d.setColor(cote) ;
|
||||
break ;
|
||||
|
||||
default:
|
||||
d.setWidth(1) ;
|
||||
d.setColor(Color.black) ;
|
||||
}
|
||||
}
|
||||
}
|
115
src/main/org/insa/base/Openfile.java
Normal file
115
src/main/org/insa/base/Openfile.java
Normal file
@ -0,0 +1,115 @@
|
||||
package org.insa.base ;
|
||||
|
||||
import java.io.* ;
|
||||
import java.util.zip.* ;
|
||||
|
||||
/* Ne lisez pas cette classe. Lancez javadoc et lisez la doc generee plutot. */
|
||||
|
||||
/**
|
||||
* La classe Openfile permet de lire les fichiers contenant les cartes :
|
||||
* <ul>
|
||||
* <li> en trouvant le bon dossier parmi les dossiers pre-configures </li>
|
||||
* <li> en dezippant automatiquement si besoin </li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
public class Openfile {
|
||||
|
||||
// Le programme examine chaque dossier dans l'ordre jusqu'a trouver celui qui contient la carte voulue
|
||||
private static final String[] datadirs =
|
||||
{ // NE MODIFIEZ PAS CELUI-CI
|
||||
// car il permet de tester en etant a l'INSA.
|
||||
"/home/commetud/3eme Annee MIC/Graphes-et-Algorithmes/Maps",
|
||||
|
||||
// Celui-ci pour les chemins
|
||||
"/home/commetud/3eme Annee MIC/Graphes-et-Algorithmes/",
|
||||
|
||||
// On cherche aussi dans le sous-repertoire local "Maps" (s'il existe)
|
||||
"Maps",
|
||||
|
||||
// et dans le repertoire courant (Unix uniquement)
|
||||
".",
|
||||
|
||||
// Si vous utilisez votre propre dossier pour les donnees, mettez-le ici.
|
||||
"/home/votrepropredossier/a/vous",
|
||||
} ;
|
||||
|
||||
// Extension testees. Garder l'extension vide dans la liste.
|
||||
private static final String[] extensions = { ".map", ".gz", ".map.gz", ".path", ".path.gz", "" } ;
|
||||
|
||||
/**
|
||||
* Ouvre le fichier indiqué et renvoie un DataInputStream sur ce fichier.
|
||||
* Le fichier ne sera pas ferme avant la fin de l'application.
|
||||
* @param filename Nom du fichier a ouvrir (sans chemin)
|
||||
*/
|
||||
public static DataInputStream open (String filename) {
|
||||
|
||||
if (!filename.equals (new File(filename).getName())) {
|
||||
System.out.println("Le nom du fichier ne doit pas contenir un chemin (ni absolu, ni relatif).") ;
|
||||
System.out.println("Il doit juste contenir le nom du fichier contenant la carte.") ;
|
||||
System.out.println("Si vous voulez utiliser un dossier specifique, configurez base/Openfile.java") ;
|
||||
System.exit(1) ;
|
||||
}
|
||||
|
||||
boolean trouve = false ;
|
||||
InputStream fileinput = null ;
|
||||
String fname = null ;
|
||||
String fullpath = null ;
|
||||
|
||||
for (int extn = 0 ; !trouve && extn < extensions.length ; extn++) {
|
||||
fname = filename + extensions[extn] ;
|
||||
for (int index = 0 ; !trouve && index < datadirs.length ; index++) {
|
||||
fullpath = datadirs[index] + File.separator + fname ;
|
||||
File file = new File(fullpath) ;
|
||||
if (file.canRead()) {
|
||||
trouve = true ;
|
||||
try {
|
||||
fileinput = new FileInputStream(file) ;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace() ;
|
||||
System.exit(1) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!trouve) {
|
||||
// Pas trouve
|
||||
System.out.println("Impossible de trouver le fichier " + filename) ;
|
||||
System.out.println(" pourtant j'ai cherche dans les dossiers : ") ;
|
||||
int existepas = 0 ;
|
||||
for (int i = 0 ; i < datadirs.length ; i++) {
|
||||
System.out.println(" - " + datadirs[i]) ;
|
||||
if (!new File(datadirs[i]).isDirectory()) {
|
||||
switch (existepas) {
|
||||
case 0: System.out.println(" (Ce dossier n'existe pas d'ailleurs)") ; break;
|
||||
case 1: System.out.println(" (Ce dossier n'existe pas non plus)") ; break;
|
||||
default: System.out.println(" (Celui-la non plus)") ; break;
|
||||
}
|
||||
existepas++ ;
|
||||
}
|
||||
System.out.println() ;
|
||||
}
|
||||
System.exit(1) ;
|
||||
}
|
||||
|
||||
System.out.println("Fichier utilisee : " + fullpath) ;
|
||||
System.out.println() ;
|
||||
|
||||
if (fname.endsWith(".gz")) {
|
||||
// The file is gzipped.
|
||||
try {
|
||||
fileinput = new GZIPInputStream(fileinput) ;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace() ;
|
||||
System.exit(1) ;
|
||||
}
|
||||
}
|
||||
else {
|
||||
fileinput = new BufferedInputStream(fileinput) ;
|
||||
}
|
||||
|
||||
return new DataInputStream(fileinput) ;
|
||||
}
|
||||
|
||||
}
|
86
src/main/org/insa/base/Readarg.java
Normal file
86
src/main/org/insa/base/Readarg.java
Normal file
@ -0,0 +1,86 @@
|
||||
package org.insa.base ;
|
||||
|
||||
import java.io.* ;
|
||||
|
||||
/* Ne lisez pas cette classe. Lancez javadoc et lisez la doc generee plutot. */
|
||||
|
||||
/**
|
||||
* La classe Readarg facilite la lecture de donnees depuis le clavier ou depuis la ligne de commande.
|
||||
*
|
||||
*/
|
||||
public class Readarg {
|
||||
|
||||
private final String[] args ;
|
||||
private int next ;
|
||||
|
||||
// Le Java est le langage prefere des Shadoks.
|
||||
private final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
||||
|
||||
public Readarg(String[] argz) {
|
||||
this.args = argz ;
|
||||
this.next = 0 ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient une chaine, ou bien depuis la ligne de commande, ou depuis l'entree standard.
|
||||
* @param msg Message affiche avant de demander la chaine
|
||||
*/
|
||||
public String lireString (String msg) {
|
||||
|
||||
String resultat = "" ;
|
||||
|
||||
System.out.print(msg) ;
|
||||
|
||||
if (this.next >= this.args.length) {
|
||||
try {
|
||||
resultat = br.readLine () ;
|
||||
} catch (Exception e) {
|
||||
System.err.println ("Erreur de lecture de l'entree standard.") ;
|
||||
System.exit(1) ;
|
||||
}
|
||||
}
|
||||
else {
|
||||
resultat = this.args[this.next] ;
|
||||
this.next++ ;
|
||||
System.out.println (resultat) ;
|
||||
}
|
||||
|
||||
return resultat ;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtient un entier, ou bien depuis la ligne de commande, ou depuis l'entree standard.
|
||||
* @param msg Message affiche avant de demander l'entier
|
||||
*/
|
||||
public int lireInt (String msg) {
|
||||
String lu = lireString (msg) ;
|
||||
int result = 0 ;
|
||||
try {
|
||||
result = Integer.parseInt(lu) ;
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.err.println ("Un entier est attendu mais je lis " + lu) ;
|
||||
System.exit(1) ;
|
||||
}
|
||||
return result ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient un float, ou bien depuis la ligne de commande, ou depuis l'entree standard.
|
||||
* @param msg Message affiche avant de demander le float.
|
||||
*/
|
||||
public float lireFloat (String msg) {
|
||||
String lu = lireString (msg) ;
|
||||
float result = 0 ;
|
||||
try {
|
||||
result = Float.parseFloat(lu) ;
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.err.println ("Un reel est attendu mais je lis " + lu) ;
|
||||
System.exit(1) ;
|
||||
}
|
||||
|
||||
return result ;
|
||||
}
|
||||
}
|
57
src/main/org/insa/base/Utils.java
Normal file
57
src/main/org/insa/base/Utils.java
Normal file
@ -0,0 +1,57 @@
|
||||
package org.insa.base ;
|
||||
|
||||
import java.io.* ;
|
||||
|
||||
import org.insa.drawing.Drawing;
|
||||
|
||||
/**
|
||||
* Fonctions accessoires dont vous n'avez pas a vous servir directement.
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
|
||||
// Calibrer la sortie graphique en fonction de la carte
|
||||
// Vous pouvez modifier les coordonnees pour ameliorer le rendu.
|
||||
public static void calibrer(String nomCarte, Drawing dessin) {
|
||||
|
||||
if (nomCarte.startsWith("insa")) {
|
||||
// L'INSA
|
||||
dessin.setBB (1.462, 1.473, 43.567, 43.5744) ;
|
||||
}
|
||||
else if (nomCarte.startsWith("paris")) {
|
||||
// Ile de la Cité, Paris
|
||||
dessin.setBB (2.329, 2.372, 48.839, 48.867) ;
|
||||
}
|
||||
else if (nomCarte.startsWith("mayot")) {
|
||||
// Mayotte
|
||||
dessin.setBB (44.5, 45.5, -13.25, -12.25) ;
|
||||
}
|
||||
else if (nomCarte.startsWith("reuni")) {
|
||||
// La Réunion
|
||||
dessin.setBB (55.0, 56.0, -21.5, -20.5) ;
|
||||
}
|
||||
else if (nomCarte.startsWith("midip")) {
|
||||
dessin.setBB (-0.6, 3.8, 42.2, 45.3) ;
|
||||
}
|
||||
else if (nomCarte.startsWith("franc")) {
|
||||
dessin.setBB (-5.2, 10.0, 41.0, 51.5) ;
|
||||
}
|
||||
else if (nomCarte.startsWith("pfranc")) {
|
||||
dessin.setBB (-5.2, 10.0, 41.0, 51.5) ;
|
||||
}
|
||||
else if (nomCarte.startsWith("morbihan")) {
|
||||
dessin.setBB (-3.53, -2.452, 47.27, 47.665) ;
|
||||
}
|
||||
else if (nomCarte.startsWith("newzealand")) {
|
||||
dessin.setBB (153.415, 179.912, -47.931, -33.980) ;
|
||||
}
|
||||
else if (nomCarte.startsWith("fract") || nomCarte.startsWith("carr")) {
|
||||
dessin.setBB (-0.05, 1.05, -0.05, 1.05) ;
|
||||
}
|
||||
else {
|
||||
dessin.setBB (-20.0, 50.0, 20.0, 70.0) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
83
src/main/org/insa/drawing/Drawing.java
Normal file
83
src/main/org/insa/drawing/Drawing.java
Normal file
@ -0,0 +1,83 @@
|
||||
package org.insa.drawing ;
|
||||
|
||||
/**
|
||||
* Classe abstraite pour dessiner a l'ecran.
|
||||
* Deux implementations : une sous-classe DessinVisible qui dessine vraiment a l'ecran
|
||||
* et une sous-classe DessinInvisible qui ne dessine rien (pour ne pas ralentir les tests avec l'affichage).
|
||||
*/
|
||||
|
||||
import java.awt.* ;
|
||||
|
||||
public interface Drawing {
|
||||
|
||||
/**
|
||||
* Enable auto-repaint mode - When this mode is enable, call to
|
||||
* drawing function will automatically repaint the drawing, which
|
||||
* may be very slow in some case.
|
||||
*
|
||||
* @param autoRepaint Use true to enable auto-repaint, false to disable.
|
||||
*
|
||||
*/
|
||||
public void setAutoRepaint(boolean autoRepaint);
|
||||
|
||||
/**
|
||||
* Repaint the drawing.
|
||||
*
|
||||
*/
|
||||
public void repaint();
|
||||
|
||||
/**
|
||||
* Set the pencil width.
|
||||
*
|
||||
* @param width Width for the pencil.
|
||||
*
|
||||
*/
|
||||
public void setWidth(int width);
|
||||
|
||||
/**
|
||||
* Set the pencil color.
|
||||
*
|
||||
* param color Color for the pencil.
|
||||
*
|
||||
*/
|
||||
public void setColor(Color col);
|
||||
|
||||
/**
|
||||
* Indique les bornes de la fenetre graphique.
|
||||
* Le calcul des coordonnees en pixel se fera automatiquement
|
||||
* a l'appel des methodes drawLine et autres.
|
||||
*
|
||||
* @param long1 longitude du bord gauche
|
||||
* @param long2 longitude du bord droit
|
||||
* @param lat1 latitude du bord bas
|
||||
* @param lat2 latitude du bord haut
|
||||
*
|
||||
*/
|
||||
public void setBB(double long1, double long2, double lat1, double lat2);
|
||||
|
||||
/**
|
||||
* Trace un segment.
|
||||
* @param long1 longitude du premier point
|
||||
* @param lat1 latitude du premier point
|
||||
* @param long2 longitude du second point
|
||||
* @param lat2 latitude du second point
|
||||
*/
|
||||
public void drawLine(float long1, float lat1, float long2, float lat2);
|
||||
|
||||
/**
|
||||
* Trace un point.
|
||||
* @param lon longitude du point
|
||||
* @param lat latitude du point
|
||||
* @param width grosseur du point
|
||||
*/
|
||||
public void drawPoint(float lon, float lat, int width);
|
||||
|
||||
/**
|
||||
* Ecrit du texte a la position indiquee.
|
||||
* @param lon longitude du point ou positionner le texte.
|
||||
* @param lat latitude du point ou positionner le texte.
|
||||
* @param txt le texte a ecrire.
|
||||
*/
|
||||
public void putText(float lon, float lat, String txt);
|
||||
|
||||
}
|
38
src/main/org/insa/drawing/DrawingInvisible.java
Normal file
38
src/main/org/insa/drawing/DrawingInvisible.java
Normal file
@ -0,0 +1,38 @@
|
||||
package org.insa.drawing;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* Cette implementation de la classe Dessin ne produit pas d'affichage,
|
||||
* ce qui accelere l'execution (utile pour ne pas ralentir les tests).
|
||||
*/
|
||||
|
||||
public class DrawingInvisible implements Drawing {
|
||||
|
||||
public DrawingInvisible () { }
|
||||
|
||||
@Override
|
||||
public void setWidth(int width) { }
|
||||
|
||||
@Override
|
||||
public void setColor(Color col) { }
|
||||
|
||||
@Override
|
||||
public void setBB(double long1, double long2, double lat1, double lat2) { }
|
||||
|
||||
@Override
|
||||
public void drawLine(float long1, float lat1, float long2, float lat2) { }
|
||||
|
||||
@Override
|
||||
public void drawPoint(float lon, float lat, int width) { }
|
||||
|
||||
@Override
|
||||
public void putText(float lon, float lat, String txt) { }
|
||||
|
||||
@Override
|
||||
public void setAutoRepaint(boolean autoRepaint) { }
|
||||
|
||||
@Override
|
||||
public void repaint() { }
|
||||
|
||||
}
|
184
src/main/org/insa/drawing/DrawingVisible.java
Normal file
184
src/main/org/insa/drawing/DrawingVisible.java
Normal file
@ -0,0 +1,184 @@
|
||||
package org.insa.drawing;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
/**
|
||||
* Cette implementation de la classe Dessin produit vraiment un affichage
|
||||
* (au contraire de la classe DessinInvisible).
|
||||
*/
|
||||
|
||||
public class DrawingVisible extends Canvas implements Drawing {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 96779785877771827L;
|
||||
|
||||
private final Graphics2D gr;
|
||||
|
||||
private float long1;
|
||||
private float long2;
|
||||
private float lat1;
|
||||
private float lat2;
|
||||
private final float width;
|
||||
private final float height;
|
||||
|
||||
private boolean bb_is_set ;
|
||||
|
||||
private Image image;
|
||||
private ZoomAndPanListener zoomAndPanListener;
|
||||
|
||||
public boolean autoRepaint = true;
|
||||
|
||||
/**
|
||||
* Cree et affiche une nouvelle fenetre de dessin.
|
||||
*/
|
||||
public DrawingVisible (int largeur, int hauteur) {
|
||||
super();
|
||||
|
||||
this.zoomAndPanListener = new ZoomAndPanListener(this, 0, ZoomAndPanListener.DEFAULT_MAX_ZOOM_LEVEL, 1.2);
|
||||
this.addMouseListener(zoomAndPanListener);
|
||||
this.addMouseMotionListener(zoomAndPanListener);
|
||||
this.addMouseWheelListener(zoomAndPanListener);
|
||||
|
||||
BufferedImage img = new BufferedImage (largeur, hauteur, BufferedImage.TYPE_3BYTE_BGR);
|
||||
|
||||
this.image = img;
|
||||
this.gr = img.createGraphics();
|
||||
|
||||
this.zoomAndPanListener.setCoordTransform(this.gr.getTransform());
|
||||
|
||||
this.bb_is_set = false;
|
||||
|
||||
this.width = largeur;
|
||||
this.height = hauteur;
|
||||
|
||||
this.long1 = (float)0.0;
|
||||
this.long2 = (float)largeur;
|
||||
this.lat1 = (float)0.0;
|
||||
this.lat2 = (float)hauteur;
|
||||
|
||||
this.setColor(Color.white);
|
||||
gr.fillRect(0,0, largeur, hauteur);
|
||||
this.repaint();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g1) {
|
||||
Graphics2D g = (Graphics2D)g1;
|
||||
g.setTransform(zoomAndPanListener.getCoordTransform());
|
||||
g.drawImage(image, 0, 0, this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension size = new Dimension(0, 0);
|
||||
|
||||
if (image != null) {
|
||||
int w = image.getWidth(null);
|
||||
int h = image.getHeight(null);
|
||||
size = new Dimension(w > 0 ? w : 0, h > 0 ? h : 0);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoRepaint(boolean autoRepaint) {
|
||||
this.autoRepaint = autoRepaint;
|
||||
}
|
||||
|
||||
protected void doAutoPaint() {
|
||||
if (autoRepaint) {
|
||||
this.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public void setWidth (int width) {
|
||||
this.gr.setStroke(new BasicStroke(width));
|
||||
}
|
||||
|
||||
public void setColor (Color col) {
|
||||
this.gr.setColor (col);
|
||||
}
|
||||
|
||||
public void setBB (double long1, double long2, double lat1, double lat2) {
|
||||
|
||||
if (long1 > long2 || lat1 > lat2) {
|
||||
throw new Error("DessinVisible.setBB : mauvaises coordonnees.");
|
||||
}
|
||||
|
||||
/* Adapte la BB en fonction de la taille du dessin, pour préserver le ratio largeur/hauteur */
|
||||
double deltalong = long2 - long1 ;
|
||||
double deltalat = lat2 - lat1 ;
|
||||
double ratiobb = deltalong / deltalat ;
|
||||
double ratiogr = width / height ;
|
||||
|
||||
/* On ne peut qu'agrandir la BB, pour ne rien perdre.
|
||||
* Si le ratiobb est trop petit, il faut agrandir deltalong
|
||||
* s'il est trop grand, il faut agrandir deltalat. */
|
||||
if (ratiobb < ratiogr) {
|
||||
/* De combien faut-il agrandir ? */
|
||||
double delta = (ratiogr - ratiobb) * deltalat ;
|
||||
|
||||
this.long1 = (float)(long1 - 0.5*delta) ;
|
||||
this.long2 = (float)(long2 + 0.5*delta) ;
|
||||
this.lat1 = (float)lat1 ;
|
||||
this.lat2 = (float)lat2 ;
|
||||
}
|
||||
else {
|
||||
double delta = (deltalong / ratiogr) - deltalat ;
|
||||
|
||||
this.long1 = (float)long1 ;
|
||||
this.long2 = (float)long2 ;
|
||||
this.lat1 = (float)(lat1 - 0.5*delta);
|
||||
this.lat2 = (float)(lat2 + 0.5*delta);
|
||||
}
|
||||
|
||||
this.bb_is_set = true ;
|
||||
}
|
||||
|
||||
private int projx(float lon) {
|
||||
return (int)(width * (lon - this.long1) / (this.long2 - this.long1)) ;
|
||||
}
|
||||
|
||||
private int projy(float lat) {
|
||||
return (int)(height * (1 - (lat - this.lat1) / (this.lat2 - this.lat1))) ;
|
||||
}
|
||||
|
||||
private void checkBB() {
|
||||
if (!this.bb_is_set) {
|
||||
throw new Error("Classe DessinVisible : vous devez invoquer la methode setBB avant de dessiner.") ;
|
||||
}
|
||||
}
|
||||
|
||||
public void drawLine (float long1, float lat1, float long2, float lat2) {
|
||||
this.checkBB() ;
|
||||
int x1 = this.projx(long1) ;
|
||||
int x2 = this.projx(long2) ;
|
||||
int y1 = this.projy(lat1) ;
|
||||
int y2 = this.projy(lat2) ;
|
||||
|
||||
gr.drawLine(x1, y1, x2, y2) ;
|
||||
this.doAutoPaint();
|
||||
}
|
||||
|
||||
public void drawPoint (float lon, float lat, int width) {
|
||||
this.checkBB() ;
|
||||
int x = this.projx(lon) - width / 2 ;
|
||||
int y = this.projy(lat) - width / 2 ;
|
||||
gr.fillOval (x, y, width, width) ;
|
||||
this.doAutoPaint();
|
||||
}
|
||||
|
||||
public void putText (float lon, float lat, String txt) {
|
||||
this.checkBB() ;
|
||||
int x = this.projx(lon) ;
|
||||
int y = this.projy(lat) ;
|
||||
gr.drawString (txt, x, y) ;
|
||||
this.doAutoPaint();
|
||||
}
|
||||
|
||||
}
|
135
src/main/org/insa/drawing/ZoomAndPanListener.java
Normal file
135
src/main/org/insa/drawing/ZoomAndPanListener.java
Normal file
@ -0,0 +1,135 @@
|
||||
package org.insa.drawing;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.NoninvertibleTransformException;
|
||||
import java.awt.geom.Point2D;
|
||||
|
||||
public class ZoomAndPanListener implements MouseListener, MouseMotionListener, MouseWheelListener {
|
||||
public static final int DEFAULT_MIN_ZOOM_LEVEL = -20;
|
||||
public static final int DEFAULT_MAX_ZOOM_LEVEL = 10;
|
||||
public static final double DEFAULT_ZOOM_MULTIPLICATION_FACTOR = 1.2;
|
||||
|
||||
private Component targetComponent;
|
||||
|
||||
private int zoomLevel = 0;
|
||||
private int minZoomLevel = DEFAULT_MIN_ZOOM_LEVEL;
|
||||
private int maxZoomLevel = DEFAULT_MAX_ZOOM_LEVEL;
|
||||
private double zoomMultiplicationFactor = DEFAULT_ZOOM_MULTIPLICATION_FACTOR;
|
||||
|
||||
private Point dragStartScreen;
|
||||
private Point dragEndScreen;
|
||||
private AffineTransform coordTransform = new AffineTransform();
|
||||
|
||||
public ZoomAndPanListener(Component targetComponent) {
|
||||
this.targetComponent = targetComponent;
|
||||
}
|
||||
|
||||
public ZoomAndPanListener(Component targetComponent, int minZoomLevel, int maxZoomLevel, double zoomMultiplicationFactor) {
|
||||
this.targetComponent = targetComponent;
|
||||
this.minZoomLevel = minZoomLevel;
|
||||
this.maxZoomLevel = maxZoomLevel;
|
||||
this.zoomMultiplicationFactor = zoomMultiplicationFactor;
|
||||
}
|
||||
|
||||
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e) {
|
||||
dragStartScreen = e.getPoint();
|
||||
dragEndScreen = null;
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
// moveCamera(e);
|
||||
}
|
||||
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
moveCamera(e);
|
||||
}
|
||||
|
||||
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||
zoomCamera(e);
|
||||
}
|
||||
|
||||
private void moveCamera(MouseEvent e) {
|
||||
try {
|
||||
dragEndScreen = e.getPoint();
|
||||
Point2D.Float dragStart = transformPoint(dragStartScreen);
|
||||
Point2D.Float dragEnd = transformPoint(dragEndScreen);
|
||||
double dx = dragEnd.getX() - dragStart.getX();
|
||||
double dy = dragEnd.getY() - dragStart.getY();
|
||||
coordTransform.translate(dx, dy);
|
||||
dragStartScreen = dragEndScreen;
|
||||
dragEndScreen = null;
|
||||
targetComponent.repaint();
|
||||
} catch (NoninvertibleTransformException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void zoomCamera(MouseWheelEvent e) {
|
||||
try {
|
||||
int wheelRotation = e.getWheelRotation();
|
||||
Point p = e.getPoint();
|
||||
if (wheelRotation > 0) {
|
||||
if (zoomLevel < maxZoomLevel) {
|
||||
zoomLevel++;
|
||||
Point2D p1 = transformPoint(p);
|
||||
coordTransform.scale(1 / zoomMultiplicationFactor, 1 / zoomMultiplicationFactor);
|
||||
Point2D p2 = transformPoint(p);
|
||||
coordTransform.translate(p2.getX() - p1.getX(), p2.getY() - p1.getY());
|
||||
targetComponent.repaint();
|
||||
}
|
||||
} else {
|
||||
if (zoomLevel > minZoomLevel) {
|
||||
zoomLevel--;
|
||||
Point2D p1 = transformPoint(p);
|
||||
coordTransform.scale(zoomMultiplicationFactor, zoomMultiplicationFactor);
|
||||
Point2D p2 = transformPoint(p);
|
||||
coordTransform.translate(p2.getX() - p1.getX(), p2.getY() - p1.getY());
|
||||
targetComponent.repaint();
|
||||
}
|
||||
}
|
||||
} catch (NoninvertibleTransformException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Point2D.Float transformPoint(Point p1) throws NoninvertibleTransformException {
|
||||
|
||||
AffineTransform inverse = coordTransform.createInverse();
|
||||
|
||||
Point2D.Float p2 = new Point2D.Float();
|
||||
inverse.transform(p1, p2);
|
||||
return p2;
|
||||
}
|
||||
|
||||
public int getZoomLevel() {
|
||||
return zoomLevel;
|
||||
}
|
||||
|
||||
|
||||
public void setZoomLevel(int zoomLevel) {
|
||||
this.zoomLevel = zoomLevel;
|
||||
}
|
||||
|
||||
|
||||
public AffineTransform getCoordTransform() {
|
||||
return coordTransform;
|
||||
}
|
||||
|
||||
public void setCoordTransform(AffineTransform coordTransform) {
|
||||
this.coordTransform = coordTransform;
|
||||
}
|
||||
}
|
81
src/main/org/insa/graph/Arc.java
Normal file
81
src/main/org/insa/graph/Arc.java
Normal file
@ -0,0 +1,81 @@
|
||||
package org.insa.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Arc {
|
||||
|
||||
// Destination node.
|
||||
private Node dest;
|
||||
|
||||
// Length of the road (in meters).
|
||||
private int length;
|
||||
|
||||
// Road information.
|
||||
RoadInformation info;
|
||||
|
||||
// Segments.
|
||||
ArrayList<Point> points;
|
||||
|
||||
/**
|
||||
* @param dest
|
||||
* @param length
|
||||
* @param roadInformation
|
||||
* @param points
|
||||
*/
|
||||
public Arc(Node dest, int length, RoadInformation roadInformation) {
|
||||
this.dest = dest;
|
||||
this.length = length;
|
||||
this.info = roadInformation;
|
||||
this.points = new ArrayList<Point>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dest
|
||||
* @param length
|
||||
* @param roadInformation
|
||||
* @param points
|
||||
*/
|
||||
public Arc(Node dest, int length, RoadInformation roadInformation, ArrayList<Point> points) {
|
||||
this.dest = dest;
|
||||
this.length = length;
|
||||
this.info = roadInformation;
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Destination node of this arc.
|
||||
*/
|
||||
public Node getDest() {
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Length of this arc, in meters.
|
||||
*/
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Minimum time required to travel this arc, in seconds.
|
||||
*/
|
||||
public float getMinimumTravelTime() {
|
||||
return getLength() * 3600f / (info.getMaximumSpeed() * 1000f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Road information for this arc.
|
||||
*/
|
||||
public RoadInformation getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Points representing segments of this arc. This function may return an empty
|
||||
* ArrayList if the segments are stored in the reversed arc (for two-ways road).
|
||||
*/
|
||||
public ArrayList<Point> getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
}
|
28
src/main/org/insa/graph/Graph.java
Normal file
28
src/main/org/insa/graph/Graph.java
Normal file
@ -0,0 +1,28 @@
|
||||
package org.insa.graph ;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Graph {
|
||||
|
||||
// Map identifier.
|
||||
private int mapId;
|
||||
|
||||
// Nodes of the graph.
|
||||
private ArrayList<Node> nodes;
|
||||
|
||||
public Graph(int mapId, ArrayList<Node> nodes) {
|
||||
this.mapId = mapId;
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Nodes of this graph.
|
||||
*/
|
||||
public ArrayList<Node> getNodes() { return nodes; }
|
||||
|
||||
/**
|
||||
* @return Map ID of this graph.
|
||||
*/
|
||||
public int getMapId() { return mapId; }
|
||||
|
||||
}
|
52
src/main/org/insa/graph/Node.java
Normal file
52
src/main/org/insa/graph/Node.java
Normal file
@ -0,0 +1,52 @@
|
||||
package org.insa.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Node {
|
||||
|
||||
// ID of the node.
|
||||
private int id;
|
||||
|
||||
// Point of this graph.
|
||||
private Point point;
|
||||
|
||||
// Successors.
|
||||
private ArrayList<Arc> successors;
|
||||
|
||||
/**
|
||||
* Create a new Node corresponding to the given Point with
|
||||
* an empty list of successors.
|
||||
*
|
||||
* @param point
|
||||
*/
|
||||
public Node(int id, Point point) {
|
||||
this.id = id;
|
||||
this.point = point;
|
||||
this.successors = new ArrayList<Arc>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a successor to this node.
|
||||
*
|
||||
* @param arc Arc to the successor.
|
||||
*/
|
||||
public void addSuccessor(Arc arc) {
|
||||
successors.add(arc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ID of this node.
|
||||
*/
|
||||
public int getId() { return id; }
|
||||
|
||||
/**
|
||||
* @return List of successors of this node.
|
||||
*/
|
||||
public ArrayList<Arc> getSuccessors() { return successors; }
|
||||
|
||||
/**
|
||||
* @return Point of this node.
|
||||
*/
|
||||
public Point getPoint() { return point; }
|
||||
|
||||
}
|
62
src/main/org/insa/graph/Point.java
Normal file
62
src/main/org/insa/graph/Point.java
Normal file
@ -0,0 +1,62 @@
|
||||
package org.insa.graph;
|
||||
|
||||
/**
|
||||
* Class representing a point on Earth.
|
||||
*
|
||||
*/
|
||||
public class Point {
|
||||
|
||||
// Earth radius, in meters;
|
||||
private static final double EARTH_RADIUS = 6378137.0 ;
|
||||
|
||||
/**
|
||||
* Compute the distance between the two given points.
|
||||
*
|
||||
* @param long1
|
||||
* @param lat1
|
||||
* @param long2
|
||||
* @param lat2
|
||||
* @return
|
||||
*/
|
||||
public static double distance(Point p1, Point p2) {
|
||||
double sinLat = Math.sin(Math.toRadians(p1.getLatitude()))*Math.sin(Math.toRadians(p2.getLatitude()));
|
||||
double cosLat = Math.cos(Math.toRadians(p1.getLatitude()))*Math.cos(Math.toRadians(p2.getLatitude()));
|
||||
double cosLong = Math.cos(Math.toRadians(p2.getLongitude() - p1.getLongitude()));
|
||||
return EARTH_RADIUS * Math.acos(sinLat+cosLat*cosLong);
|
||||
}
|
||||
|
||||
// Longitude and latitude of the point.
|
||||
private float longitude, latitude;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param longitude Longitude of the point, in degrees.
|
||||
* @param latitude Latitude of the point, in degrees.
|
||||
*/
|
||||
public Point(float longitude, float latitude) {
|
||||
this.longitude = longitude;
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Longitude of this point (in degrees).
|
||||
*/
|
||||
public float getLongitude() { return longitude; }
|
||||
|
||||
/**
|
||||
* @return Latitude of this point (in degrees).
|
||||
*/
|
||||
public float getLatitude() { return latitude; }
|
||||
|
||||
/**
|
||||
* Compute the distance from this point to the given point
|
||||
*
|
||||
* @param target Target point.
|
||||
*
|
||||
* @return Distane between this point and the target point, in meters.
|
||||
*/
|
||||
public double distanceTo(Point target) {
|
||||
return distance(this, target);
|
||||
}
|
||||
|
||||
}
|
85
src/main/org/insa/graph/RoadInformation.java
Normal file
85
src/main/org/insa/graph/RoadInformation.java
Normal file
@ -0,0 +1,85 @@
|
||||
package org.insa.graph ;
|
||||
|
||||
/**
|
||||
* Class containing information for road that may be shared
|
||||
* by multiple arcs.
|
||||
*
|
||||
*/
|
||||
public class RoadInformation {
|
||||
|
||||
/**
|
||||
* Road type.
|
||||
*/
|
||||
public enum RoadType {
|
||||
MOTORWAY,
|
||||
TRUNK,
|
||||
PRIMARY,
|
||||
SECONDARY,
|
||||
MOTORWAY_LINK,
|
||||
TRUNK_LINK,
|
||||
PRIMARY_LINK,
|
||||
SECONDARY_LINK,
|
||||
TERTIARY,
|
||||
RESIDENTIAL,
|
||||
UNCLASSIFIED,
|
||||
ROAD,
|
||||
LIVING_STREET,
|
||||
SERVICE,
|
||||
ROUNDABOUT,
|
||||
COASTLINE
|
||||
}
|
||||
|
||||
// Type of the road (see above).
|
||||
private RoadType type;
|
||||
|
||||
// One way road?
|
||||
private boolean oneway;
|
||||
|
||||
// Max speed in kilometers per hour.
|
||||
private int maxSpeed;
|
||||
|
||||
// Name of the road.
|
||||
private String name;
|
||||
|
||||
public RoadInformation(RoadType roadType, boolean isOneWay, int maxSpeed, String name) {
|
||||
this.type = roadType;
|
||||
this.oneway = isOneWay;
|
||||
this.maxSpeed = maxSpeed;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Type of the road.
|
||||
*/
|
||||
public RoadType getType() { return type; }
|
||||
|
||||
/**
|
||||
* @return true if this is a one-way road.
|
||||
*/
|
||||
public boolean isOneWay() { return oneway; }
|
||||
|
||||
/**
|
||||
* @return Maximum speed for this road (in km/h).
|
||||
*/
|
||||
public int getMaximumSpeed() { return maxSpeed; }
|
||||
|
||||
/**
|
||||
* @return Name of the road.
|
||||
*/
|
||||
public String getName() { return name; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String typeAsString = "road";
|
||||
if (getType() == RoadType.COASTLINE) {
|
||||
typeAsString = "coast";
|
||||
}
|
||||
if (getType() == RoadType.MOTORWAY) {
|
||||
typeAsString = "highway";
|
||||
}
|
||||
return typeAsString + " : " + getName()
|
||||
+ " " + (isOneWay() ? " (oneway) " : "")
|
||||
+ maxSpeed + " km/h (max.)";
|
||||
}
|
||||
|
||||
}
|
16
src/main/org/insa/graph/io/AbstractGraphReader.java
Normal file
16
src/main/org/insa/graph/io/AbstractGraphReader.java
Normal file
@ -0,0 +1,16 @@
|
||||
package org.insa.graph.io;
|
||||
|
||||
import org.insa.graph.Graph;
|
||||
|
||||
public interface AbstractGraphReader {
|
||||
|
||||
/**
|
||||
* Read a graph an returns it.
|
||||
*
|
||||
* @return Graph.
|
||||
* @throws Exception
|
||||
*
|
||||
*/
|
||||
public Graph read() throws Exception;
|
||||
|
||||
}
|
18
src/main/org/insa/graph/io/AbstractPathReader.java
Normal file
18
src/main/org/insa/graph/io/AbstractPathReader.java
Normal file
@ -0,0 +1,18 @@
|
||||
package org.insa.graph.io;
|
||||
|
||||
import org.insa.graph.Graph;
|
||||
import org.insa.graph.Path;
|
||||
|
||||
public interface AbstractPathReader {
|
||||
|
||||
/**
|
||||
* Read a path of the given graph and returns it.
|
||||
*
|
||||
* @param graph Graph of the path.
|
||||
*
|
||||
* @return A new path.
|
||||
* @throws Exception
|
||||
*/
|
||||
public Path readPath(Graph graph) throws Exception;
|
||||
|
||||
}
|
21
src/main/org/insa/graph/io/BadFormatException.java
Normal file
21
src/main/org/insa/graph/io/BadFormatException.java
Normal file
@ -0,0 +1,21 @@
|
||||
package org.insa.graph.io;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class BadFormatException extends IOException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -5455552814725826052L;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param actualVersion
|
||||
* @param expectedVersion
|
||||
*/
|
||||
public BadFormatException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
36
src/main/org/insa/graph/io/BadMagicNumberException.java
Normal file
36
src/main/org/insa/graph/io/BadMagicNumberException.java
Normal file
@ -0,0 +1,36 @@
|
||||
package org.insa.graph.io;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class BadMagicNumberException extends IOException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -2176603967548838864L;
|
||||
|
||||
// Actual and expected magic numbers.
|
||||
private int actualNumber, expectedNumber;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param actualVersion
|
||||
* @param expectedVersion
|
||||
*/
|
||||
public BadMagicNumberException(int actualNumber, int expectedNumber) {
|
||||
super();
|
||||
this.actualNumber = actualNumber;
|
||||
this.expectedNumber = expectedNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public int getActualMagicNumber() { return actualNumber; }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public int getExpectedMagicNumber() { return expectedNumber; }
|
||||
|
||||
}
|
35
src/main/org/insa/graph/io/BadVersionException.java
Normal file
35
src/main/org/insa/graph/io/BadVersionException.java
Normal file
@ -0,0 +1,35 @@
|
||||
package org.insa.graph.io;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class BadVersionException extends IOException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 7776317018302386042L;
|
||||
|
||||
// Actual and expected version..
|
||||
private int actualVersion, expectedVersion;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param actualVersion
|
||||
* @param expectedVersion
|
||||
*/
|
||||
public BadVersionException(int actualVersion, int expectedVersion) {
|
||||
super();
|
||||
this.actualVersion = actualVersion;
|
||||
this.expectedVersion = expectedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public int getActualVersion() { return actualVersion; }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public int getExpectedVersion() { return expectedVersion; }
|
||||
}
|
176
src/main/org/insa/graph/io/BinaryGraphReader.java
Normal file
176
src/main/org/insa/graph/io/BinaryGraphReader.java
Normal file
@ -0,0 +1,176 @@
|
||||
package org.insa.graph.io;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.insa.graph.Arc;
|
||||
import org.insa.graph.Graph;
|
||||
import org.insa.graph.Node;
|
||||
import org.insa.graph.Point;
|
||||
import org.insa.graph.RoadInformation;
|
||||
import org.insa.graph.RoadInformation.RoadType;
|
||||
|
||||
public class BinaryGraphReader extends BinaryReader implements AbstractGraphReader {
|
||||
|
||||
// Map version and magic number targeted for this reader.
|
||||
private static final int VERSION = 4;
|
||||
private static final int MAGIC_NUMBER = 0xbacaff;
|
||||
|
||||
/**
|
||||
* Convert a character to its corresponding road type.
|
||||
*
|
||||
* @param ch Character to convert.
|
||||
*
|
||||
* @return Road type corresponding to ch.
|
||||
*
|
||||
* @see http://wiki.openstreetmap.org/wiki/Highway_tag_usage.
|
||||
*/
|
||||
public static RoadType toRoadType(char ch) {
|
||||
switch (ch) {
|
||||
case 'a': return RoadType.MOTORWAY;
|
||||
case 'b': return RoadType.TRUNK;
|
||||
case 'c': return RoadType.PRIMARY;
|
||||
case 'd': return RoadType.SECONDARY;
|
||||
case 'e': return RoadType.MOTORWAY_LINK;
|
||||
case 'f': return RoadType.TRUNK_LINK;
|
||||
case 'g': return RoadType.PRIMARY_LINK;
|
||||
case 'h': return RoadType.SECONDARY_LINK;
|
||||
case 'i': return RoadType.TERTIARY;
|
||||
case 'j': return RoadType.RESIDENTIAL;
|
||||
case 'k': return RoadType.UNCLASSIFIED;
|
||||
case 'l': return RoadType.ROAD;
|
||||
case 'm': return RoadType.LIVING_STREET;
|
||||
case 'n': return RoadType.SERVICE;
|
||||
case 'o': return RoadType.ROUNDABOUT;
|
||||
case 'z': return RoadType.COASTLINE;
|
||||
}
|
||||
return RoadType.UNCLASSIFIED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BinaryGraphReader using the given DataInputStream.
|
||||
*
|
||||
* @param dis
|
||||
*/
|
||||
public BinaryGraphReader(DataInputStream dis) {
|
||||
super(MAGIC_NUMBER, VERSION, dis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graph read() throws IOException {
|
||||
|
||||
// Read and check magic number and file version.
|
||||
checkMagicNumberOrThrow(dis.readInt());
|
||||
checkVersionOrThrow(dis.readInt());
|
||||
|
||||
// Read map id.
|
||||
int mapId = dis.readInt();
|
||||
|
||||
// Read zone.
|
||||
int graphZone = dis.readInt();
|
||||
|
||||
// Number of descriptors and nodes.
|
||||
int nbDesc = dis.readInt();
|
||||
int nbNodes = dis.readInt();
|
||||
|
||||
// Number of successors for each nodes.
|
||||
int[] nbSuccessors = new int[nbNodes];
|
||||
|
||||
// Construct an array list with initial capacity of nbNodes.
|
||||
ArrayList<Node> nodes = new ArrayList<Node>(nbNodes);
|
||||
|
||||
// Read nodes.
|
||||
for (int node = 0; node < nbNodes; ++node) {
|
||||
float longitude = ((float)dis.readInt ()) / 1E6f;
|
||||
float latitude = ((float)dis.readInt ()) / 1E6f;
|
||||
nbSuccessors[node] = dis.readUnsignedByte();
|
||||
nodes.add(new Node(node, new Point(longitude, latitude)));
|
||||
}
|
||||
|
||||
// Check format.
|
||||
checkByteOrThrow(255);
|
||||
|
||||
// Read descriptors.
|
||||
RoadInformation[] descs = new RoadInformation[nbDesc];
|
||||
|
||||
// Read
|
||||
for (int descr = 0; descr < nbDesc; ++descr) {
|
||||
descs[descr] = readRoadInformation();
|
||||
}
|
||||
|
||||
// Check format.
|
||||
checkByteOrThrow(254);
|
||||
|
||||
// Read successors and convert to arcs.
|
||||
for (int node = 0; node < nbNodes; ++node) {
|
||||
for (int succ = 0; succ < nbSuccessors[node]; ++succ) {
|
||||
|
||||
// Read destination zone.
|
||||
int destZone = dis.readUnsignedByte();
|
||||
|
||||
// Read target node number.
|
||||
int destNode = this.read24bits();
|
||||
|
||||
// Read information number.
|
||||
int descrNum = this.read24bits();
|
||||
|
||||
// Length of the arc.
|
||||
int length = dis.readUnsignedShort();
|
||||
|
||||
// Number of segments.
|
||||
int nbSegments = dis.readUnsignedShort();
|
||||
|
||||
// Chain of points corresponding to the segments.
|
||||
ArrayList<Point> points = new ArrayList<Point>(nbSegments + 2);
|
||||
points.add(nodes.get(node).getPoint());
|
||||
|
||||
for (int seg = 0; seg < nbSegments; ++seg) {
|
||||
Point lastPoint = points.get(points.size() - 1);
|
||||
|
||||
float dlon = (dis.readShort()) / 2.0e5f;
|
||||
float dlat = (dis.readShort()) / 2.0e5f;
|
||||
|
||||
points.add(new Point(lastPoint.getLongitude() + dlon,
|
||||
lastPoint.getLatitude() + dlat));
|
||||
}
|
||||
|
||||
points.add(nodes.get(destNode).getPoint());
|
||||
|
||||
if (graphZone == destZone) {
|
||||
|
||||
RoadInformation info = descs[descrNum];
|
||||
Node orig = nodes.get(node);
|
||||
Node dest = nodes.get(destNode);
|
||||
|
||||
// Add successor to initial arc.
|
||||
orig.addSuccessor(new Arc(dest, length, info, points));
|
||||
|
||||
// And reverse arc if its a two-way road.
|
||||
if (!info.isOneWay()) {
|
||||
// Add without segments.
|
||||
dest.addSuccessor(new Arc(orig, length, info));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check format.
|
||||
checkByteOrThrow(253);
|
||||
|
||||
return new Graph(mapId, nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the next road information from the stream.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private RoadInformation readRoadInformation() throws IOException {
|
||||
char type = (char)dis.readUnsignedByte();
|
||||
int x = dis.readUnsignedByte() ;
|
||||
return new RoadInformation(toRoadType(type), (x & 0x80) > 0, (x & 0x7F) * 5, dis.readUTF());
|
||||
}
|
||||
|
||||
}
|
70
src/main/org/insa/graph/io/BinaryPathReader.java
Normal file
70
src/main/org/insa/graph/io/BinaryPathReader.java
Normal file
@ -0,0 +1,70 @@
|
||||
package org.insa.graph.io;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.insa.graph.Graph;
|
||||
import org.insa.graph.Node;
|
||||
import org.insa.graph.Path;
|
||||
|
||||
public class BinaryPathReader extends BinaryReader implements AbstractPathReader {
|
||||
|
||||
// Map version and magic number targeted for this reader.
|
||||
private static final int VERSION = 1;
|
||||
private static final int MAGIC_NUMBER = 0xdecafe;
|
||||
|
||||
public BinaryPathReader(DataInputStream dis) {
|
||||
super(MAGIC_NUMBER, VERSION, dis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path readPath(Graph graph) throws Exception {
|
||||
|
||||
// Read and check magic number and version.
|
||||
checkMagicNumberOrThrow(dis.readInt());
|
||||
checkVersionOrThrow(dis.readInt());
|
||||
|
||||
// Read map ID and check against graph.
|
||||
int mapId = dis.readInt();
|
||||
|
||||
if (mapId != graph.getMapId()) {
|
||||
throw new MapMismatchException(mapId, graph.getMapId());
|
||||
}
|
||||
|
||||
// Number of nodes in the path (without first and last).
|
||||
int nbNodes = dis.readInt();
|
||||
|
||||
ArrayList<Node> nodes = new ArrayList<Node>(nbNodes + 2);
|
||||
|
||||
// Read first node
|
||||
nodes.add(readNode(graph));
|
||||
|
||||
// Read last node
|
||||
Node lastNode = readNode(graph);
|
||||
|
||||
// Read intermediate nodes:
|
||||
for (int node = 0; node < nbNodes; ++node) {
|
||||
nodes.add(readNode(graph));
|
||||
}
|
||||
|
||||
// Add last node
|
||||
nodes.add(lastNode);
|
||||
|
||||
return new Path(graph, nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a node from the stream and returns id.
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
protected Node readNode(Graph graph) throws IOException {
|
||||
// Discard zone.
|
||||
dis.readUnsignedByte();
|
||||
|
||||
return graph.getNodes().get(read24bits());
|
||||
}
|
||||
|
||||
}
|
67
src/main/org/insa/graph/io/BinaryReader.java
Normal file
67
src/main/org/insa/graph/io/BinaryReader.java
Normal file
@ -0,0 +1,67 @@
|
||||
package org.insa.graph.io;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class BinaryReader {
|
||||
|
||||
// Map version and magic number targeted for this reader.
|
||||
private int version;
|
||||
private int magicNumber;
|
||||
|
||||
// InputStream
|
||||
protected DataInputStream dis;
|
||||
|
||||
protected BinaryReader(int magicNumber, int version, DataInputStream dis) {
|
||||
this.magicNumber = magicNumber;
|
||||
this.version = version;
|
||||
this.dis = dis;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param version
|
||||
* @throws BadVersionException
|
||||
*/
|
||||
public void checkVersionOrThrow(int version) throws BadVersionException {
|
||||
if (this.version != version) {
|
||||
throw new BadVersionException(version, this.version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param magicNumber
|
||||
* @throws BadMagicNumberException
|
||||
*/
|
||||
public void checkMagicNumberOrThrow(int magicNumber) throws BadMagicNumberException {
|
||||
if (this.magicNumber != magicNumber) {
|
||||
throw new BadMagicNumberException(magicNumber, this.magicNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the next byte in the input stream correspond to the
|
||||
* given byte. This function consumes the next byte in the input
|
||||
* stream.
|
||||
*
|
||||
* @param i Byte to check against.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void checkByteOrThrow(int i) throws IOException {
|
||||
if (dis.readUnsignedByte() != i) {
|
||||
throw new BadFormatException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read 24 bits from the stream and return the corresponding integer value.
|
||||
*
|
||||
* @return Integer value read from the next 24 bits of the stream.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
protected int read24bits() throws IOException {
|
||||
int x = dis.readUnsignedShort() ;
|
||||
return (x << 8) | dis.readUnsignedByte() ;
|
||||
}
|
||||
}
|
36
src/main/org/insa/graph/io/MapMismatchException.java
Normal file
36
src/main/org/insa/graph/io/MapMismatchException.java
Normal file
@ -0,0 +1,36 @@
|
||||
package org.insa.graph.io;
|
||||
|
||||
public class MapMismatchException extends Exception {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 3076730078387819138L;
|
||||
// Actual and expected magic numbers.
|
||||
private int actualMapId, expectedMapId;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param actualVersion
|
||||
* @param expectedVersion
|
||||
*/
|
||||
public MapMismatchException(int actualMapId, int expectedMapId) {
|
||||
super();
|
||||
this.actualMapId = actualMapId;
|
||||
this.expectedMapId = expectedMapId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public int getActualMapId() {
|
||||
return actualMapId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public int getExpectedMapId() {
|
||||
return expectedMapId;
|
||||
}
|
||||
}
|
252
src/main/org/insa/utility/BinaryHeap.java
Normal file
252
src/main/org/insa/utility/BinaryHeap.java
Normal file
@ -0,0 +1,252 @@
|
||||
//
|
||||
// ******************PUBLIC OPERATIONS*********************
|
||||
// void insert( x ) --> Insert x
|
||||
// Comparable deleteMin( )--> Return and remove smallest item
|
||||
// Comparable findMin( ) --> Return smallest item
|
||||
// boolean isEmpty( ) --> Return true if empty; else false
|
||||
// ******************ERRORS********************************
|
||||
// Throws RuntimeException for findMin and deleteMin when empty
|
||||
|
||||
package org.insa.utility;
|
||||
|
||||
import java.util.* ;
|
||||
|
||||
/**
|
||||
* Implements a binary heap.
|
||||
* Note that all "matching" is based on the compareTo method.
|
||||
* @author Mark Allen Weiss
|
||||
* @author DLB
|
||||
*/
|
||||
public class BinaryHeap<E extends Comparable<E>> {
|
||||
|
||||
private int currentSize; // Number of elements in heap
|
||||
|
||||
// Java genericity does not work with arrays.
|
||||
// We have to use an ArrayList
|
||||
private ArrayList<E> array; // The heap array
|
||||
|
||||
/**
|
||||
* Construct the binary heap.
|
||||
*/
|
||||
public BinaryHeap() {
|
||||
this.currentSize = 0;
|
||||
this.array = new ArrayList<E>() ;
|
||||
}
|
||||
|
||||
// Constructor used for debug.
|
||||
public BinaryHeap(BinaryHeap<E> heap) {
|
||||
this.currentSize = heap.currentSize ;
|
||||
this.array = new ArrayList<E>(heap.array) ;
|
||||
}
|
||||
|
||||
// Sets an element in the array
|
||||
private void arraySet(int index, E value) {
|
||||
if (index == this.array.size()) {
|
||||
this.array.add(value) ;
|
||||
}
|
||||
else {
|
||||
this.array.set(index, value) ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the heap is logically empty.
|
||||
* @return true if empty, false otherwise.
|
||||
*/
|
||||
public boolean isEmpty() { return this.currentSize == 0; }
|
||||
|
||||
/**
|
||||
* Returns size.
|
||||
* @return current size.
|
||||
*/
|
||||
public int size() { return this.currentSize; }
|
||||
|
||||
|
||||
/**
|
||||
* Returns index of parent.
|
||||
*/
|
||||
private int index_parent(int index) {
|
||||
return (index - 1) / 2 ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns index of left child.
|
||||
*/
|
||||
private int index_left(int index) {
|
||||
return index * 2 + 1 ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert into the heap.
|
||||
* @param x the item to insert.
|
||||
*/
|
||||
public void insert(E x) {
|
||||
int index = this.currentSize++ ;
|
||||
this.arraySet(index, x) ;
|
||||
this.percolateUp(index) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to percolate up in the heap.
|
||||
* @param index the index at which the percolate begins.
|
||||
*/
|
||||
private void percolateUp(int index) {
|
||||
E x = this.array.get(index) ;
|
||||
|
||||
for( ; index > 0 && x.compareTo(this.array.get(index_parent(index)) ) < 0; index = index_parent(index) ) {
|
||||
E moving_val = this.array.get(index_parent(index)) ;
|
||||
this.arraySet(index, moving_val) ;
|
||||
}
|
||||
|
||||
this.arraySet(index, x) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to percolate down in the heap.
|
||||
* @param index the index at which the percolate begins.
|
||||
*/
|
||||
private void percolateDown(int index) {
|
||||
int ileft = index_left(index) ;
|
||||
int iright = ileft + 1 ;
|
||||
|
||||
if (ileft < this.currentSize) {
|
||||
E current = this.array.get(index) ;
|
||||
E left = this.array.get(ileft) ;
|
||||
boolean hasRight = iright < this.currentSize ;
|
||||
E right = (hasRight)?this.array.get(iright):null ;
|
||||
|
||||
if (!hasRight || left.compareTo(right) < 0) {
|
||||
// Left is smaller
|
||||
if (left.compareTo(current) < 0) {
|
||||
this.arraySet(index, left) ;
|
||||
this.arraySet(ileft, current) ;
|
||||
this.percolateDown( ileft ) ;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Right is smaller
|
||||
if (right.compareTo(current) < 0) {
|
||||
this.arraySet(index, right) ;
|
||||
this.arraySet(iright, current) ;
|
||||
this.percolateDown( iright ) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the smallest item in the heap.
|
||||
* @return the smallest item.
|
||||
* @throws Exception if empty.
|
||||
*/
|
||||
public E findMin( ) {
|
||||
if( isEmpty() )
|
||||
throw new RuntimeException( "Empty binary heap" );
|
||||
return this.array.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the smallest item from the heap.
|
||||
* @return the smallest item.
|
||||
* @throws Exception if empty.
|
||||
*/
|
||||
public E deleteMin( ) {
|
||||
E minItem = findMin( );
|
||||
E lastItem = this.array.get(--this.currentSize) ;
|
||||
this.arraySet(0, lastItem) ;
|
||||
this.percolateDown( 0 );
|
||||
return minItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the heap
|
||||
*/
|
||||
public void print() {
|
||||
System.out.println() ;
|
||||
System.out.println("======== HEAP (size = " + this.currentSize + ") ========") ;
|
||||
System.out.println() ;
|
||||
|
||||
for (int i = 0 ; i < this.currentSize ; i++) {
|
||||
System.out.println(this.array.get(i).toString()) ;
|
||||
}
|
||||
|
||||
System.out.println() ;
|
||||
System.out.println("-------- End of heap --------") ;
|
||||
System.out.println() ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the elements of the heap according to their respective order.
|
||||
*/
|
||||
public void printSorted() {
|
||||
|
||||
BinaryHeap<E> copy = new BinaryHeap<E>(this) ;
|
||||
|
||||
System.out.println() ;
|
||||
System.out.println("======== Sorted HEAP (size = " + this.currentSize + ") ========") ;
|
||||
System.out.println() ;
|
||||
|
||||
while (!copy.isEmpty()) {
|
||||
System.out.println(copy.deleteMin()) ;
|
||||
}
|
||||
|
||||
System.out.println() ;
|
||||
System.out.println("-------- End of heap --------") ;
|
||||
System.out.println() ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Test program : compare with the reference implementation PriorityQueue.
|
||||
public static void main(String [] args) {
|
||||
BinaryHeap<Integer> heap = new BinaryHeap<Integer>() ;
|
||||
PriorityQueue<Integer> queue = new PriorityQueue<Integer>() ;
|
||||
|
||||
int count = 0 ;
|
||||
int blocksize = 10000 ;
|
||||
|
||||
System.out.println("Interrupt to stop the test.") ;
|
||||
|
||||
while (true) {
|
||||
|
||||
// Insert up to blocksize elements
|
||||
int nb_insert = (int)(Math.random() * (blocksize + 1)) ;
|
||||
|
||||
for (int i = 0 ; i < nb_insert ; i++) {
|
||||
Integer obj = new Integer(i) ;
|
||||
heap.insert(obj) ;
|
||||
queue.add(obj) ;
|
||||
}
|
||||
|
||||
// Remove up to blocksize elements
|
||||
int nb_remove = (int)(Math.random() * blocksize * 1.1) ;
|
||||
|
||||
if (nb_remove > queue.size()) {
|
||||
nb_remove = queue.size() ;
|
||||
}
|
||||
|
||||
for (int i = 0 ; i < nb_remove ; i++) {
|
||||
|
||||
int removed1 = queue.poll().intValue() ;
|
||||
int removed2 = heap.deleteMin().intValue() ;
|
||||
|
||||
if (removed1 != removed2) {
|
||||
System.out.println("Ouch : expected " + removed1 + " .. but got " + removed2) ;
|
||||
System.exit(1) ;
|
||||
}
|
||||
}
|
||||
|
||||
if (heap.size() != queue.size()) {
|
||||
System.out.println("Ouch : heap size = " + heap.size() + " queue size = " + queue.size() ) ;
|
||||
System.exit(1) ;
|
||||
}
|
||||
|
||||
count += nb_remove ;
|
||||
|
||||
if (count > 1000000) {
|
||||
System.out.println("" + count + " items successfully compared. Heap size : " + heap.size()) ;
|
||||
count = 0 ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
68
src/test/org/insa/utility/BinaryHeapTest.java
Normal file
68
src/test/org/insa/utility/BinaryHeapTest.java
Normal file
@ -0,0 +1,68 @@
|
||||
package org.insa.utility;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public class BinaryHeapTest {
|
||||
|
||||
private BinaryHeap<Integer> rangeHeap1;
|
||||
|
||||
static IntStream dataRange1() {
|
||||
return IntStream.range(0, 20);
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
static void initAll() {
|
||||
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
// Create the range heap
|
||||
this.rangeHeap1 = new BinaryHeap<Integer>();
|
||||
dataRange1().forEach((int x) -> rangeHeap1.insert(x));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInsert() {
|
||||
BinaryHeap<Integer> heap = new BinaryHeap<Integer>();
|
||||
int size = 0;
|
||||
for (int x: dataRange1().toArray()) {
|
||||
heap.insert(x);
|
||||
size += 1;
|
||||
assertEquals(heap.size(), size);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteMin() {
|
||||
int[] range1 = dataRange1().toArray();
|
||||
int size = range1.length;
|
||||
assertEquals(rangeHeap1.size(), size);
|
||||
for (int x: range1) {
|
||||
assertEquals(rangeHeap1.deleteMin().intValue(), x);
|
||||
size -= 1;
|
||||
assertEquals(rangeHeap1.size(), size);
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDownAll() {
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user