Disabled external gits
1
Alpano
@ -1 +0,0 @@
|
|||||||
Subproject commit 7792b5cd98f77942a04e32474594aaf0a18749b2
|
|
13
Alpano/.classpath
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.fx.ide.jdt.core.JAVAFX_CONTAINER"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
|
||||||
|
<accessrules>
|
||||||
|
<accessrule kind="accessible" pattern="java/**"/>
|
||||||
|
</accessrules>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" path="tests"/>
|
||||||
|
<classpathentry kind="output" path="bin"/>
|
||||||
|
</classpath>
|
5
Alpano/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
*.DS_Store
|
||||||
|
*.hgt
|
||||||
|
*.hgt*
|
||||||
|
bin/*
|
||||||
|
/bin/
|
17
Alpano/.project
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>alpano</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>
|
11
Alpano/.settings/org.eclipse.jdt.core.prefs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.8
|
21069
Alpano/alps.txt
Normal file
BIN
Alpano/rapport.pages
Normal file
BIN
Alpano/rapport.pdf
Normal file
98
Alpano/src/ch/epfl/alpano/Azimuth.java
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
import static ch.epfl.alpano.Math2.floorMod;
|
||||||
|
import static ch.epfl.alpano.Math2.PI2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class of commonly used calculations on azimuths
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface Azimuth {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the azimuth is canonical
|
||||||
|
*
|
||||||
|
* @param azimuth
|
||||||
|
* @return true if the given azimuth is in [0,2PI[
|
||||||
|
*/
|
||||||
|
public static boolean isCanonical(double azimuth) {
|
||||||
|
return (azimuth >= 0 && azimuth < PI2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modular value of a given azimuth angle
|
||||||
|
*
|
||||||
|
* @param azimuth
|
||||||
|
* @return azimuth in [0, 2PI[
|
||||||
|
*/
|
||||||
|
public static double canonicalize(double azimuth) {
|
||||||
|
return floorMod(azimuth, PI2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mathematical counterpart of the azimuth value
|
||||||
|
*
|
||||||
|
* @param azimuth
|
||||||
|
* @return azimuth value; expressed clockwise
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if azimuth input is not canonical
|
||||||
|
* @see isCanonical(double azimuth)
|
||||||
|
*/
|
||||||
|
public static double toMath(double azimuth) {
|
||||||
|
checkArgument(isCanonical(azimuth));
|
||||||
|
return canonicalize(PI2 - azimuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* azimuth counterpart of the mathematical value
|
||||||
|
*
|
||||||
|
* @param angle
|
||||||
|
* @return angle value; expressed counterclockwise
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if angle input is not canonical
|
||||||
|
* @see isCanonical(double azimuth)
|
||||||
|
*/
|
||||||
|
public static double fromMath(double angle) {
|
||||||
|
checkArgument(isCanonical(angle));
|
||||||
|
return canonicalize(PI2 - angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cardinal points of a given azimuth angle
|
||||||
|
*
|
||||||
|
* @param azimuth
|
||||||
|
* @param n
|
||||||
|
* : north string
|
||||||
|
* @param e
|
||||||
|
* : east string
|
||||||
|
* @param s
|
||||||
|
* : south string
|
||||||
|
* @param w
|
||||||
|
* : west string
|
||||||
|
* @return a string expressing the closest cardinal point to the given
|
||||||
|
* azimuth
|
||||||
|
*/
|
||||||
|
public static String toOctantString(double azimuth, String n, String e,
|
||||||
|
String s, String w) {
|
||||||
|
|
||||||
|
checkArgument(isCanonical(azimuth));
|
||||||
|
|
||||||
|
String output = "";
|
||||||
|
double angle = PI2 / 16.0;
|
||||||
|
|
||||||
|
if (azimuth <= 3 * angle || azimuth >= 13 * angle)
|
||||||
|
output += n;
|
||||||
|
if (azimuth >= 5 * angle && azimuth <= 11 * angle)
|
||||||
|
output += s;
|
||||||
|
if (azimuth >= 1 * angle && azimuth <= 7 * angle)
|
||||||
|
output += e;
|
||||||
|
if (azimuth >= 9 * angle && azimuth <= 15 * angle)
|
||||||
|
output += w;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
48
Alpano/src/ch/epfl/alpano/Distance.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility interface of commonly used distance transformations and known values
|
||||||
|
*/
|
||||||
|
public interface Distance {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value of the earth radius
|
||||||
|
*/
|
||||||
|
public final double EARTH_RADIUS = 6371000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms meters to radians
|
||||||
|
*
|
||||||
|
* @param meters
|
||||||
|
* : distance to be converted to radians
|
||||||
|
* @return radians
|
||||||
|
*/
|
||||||
|
public static double toRadians(double meters) {
|
||||||
|
return meters / EARTH_RADIUS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms radians to meters
|
||||||
|
*
|
||||||
|
* @param radians
|
||||||
|
* : angle to be converted to meters
|
||||||
|
* @return meters
|
||||||
|
*/
|
||||||
|
public static double toMeters(double radians) {
|
||||||
|
return EARTH_RADIUS * radians;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms meters to degrees
|
||||||
|
* @param meters : distance to be converted to degrees
|
||||||
|
* @return degrees
|
||||||
|
* @see toRadians
|
||||||
|
*/
|
||||||
|
public static double toDegrees(double meters){
|
||||||
|
return Math.toDegrees(toRadians(meters));
|
||||||
|
}
|
||||||
|
}
|
116
Alpano/src/ch/epfl/alpano/GeoPoint.java
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import static java.lang.Math.abs;
|
||||||
|
import static java.lang.Math.cos;
|
||||||
|
import static java.lang.Math.sin;
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
import static java.lang.Math.atan2;
|
||||||
|
import static java.lang.Math.sqrt;
|
||||||
|
import static java.lang.Math.toDegrees;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
import static ch.epfl.alpano.Math2.haversin;
|
||||||
|
import static ch.epfl.alpano.Distance.toMeters;
|
||||||
|
import static ch.epfl.alpano.Azimuth.fromMath;
|
||||||
|
import static ch.epfl.alpano.Azimuth.canonicalize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition of a geographical point on Earth's surface using spherical
|
||||||
|
* coordinates.
|
||||||
|
*/
|
||||||
|
public class GeoPoint {
|
||||||
|
|
||||||
|
private final double longitude;
|
||||||
|
private final double latitude;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a GeoPoint
|
||||||
|
*
|
||||||
|
* @param longitude
|
||||||
|
* : longitude position
|
||||||
|
* @param latitude
|
||||||
|
* : latitude position
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if longitude is not in [−Pi;Pi] or latitude is not in
|
||||||
|
* [−2Pi;2Pi]
|
||||||
|
*/
|
||||||
|
public GeoPoint(double longitude, double latitude) {
|
||||||
|
checkArgument(!(abs(longitude) > PI || abs(latitude) * 2.0 > PI));
|
||||||
|
|
||||||
|
this.longitude = longitude;
|
||||||
|
this.latitude = latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the longitude value of the GeoPoint
|
||||||
|
*
|
||||||
|
* @return longitude
|
||||||
|
*/
|
||||||
|
public double longitude() {
|
||||||
|
return this.longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the latitude value of the GeoPoint
|
||||||
|
*
|
||||||
|
* @return latitude
|
||||||
|
*/
|
||||||
|
public double latitude() {
|
||||||
|
return this.latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the distance between two points
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : a GeoPoint
|
||||||
|
* @return distance in meters between two points
|
||||||
|
*/
|
||||||
|
public double distanceTo(GeoPoint that) {
|
||||||
|
return toMeters(
|
||||||
|
2 * Math.asin(sqrt(haversin(this.latitude - that.latitude)
|
||||||
|
+ cos(this.latitude) * cos(that.latitude)
|
||||||
|
* haversin(this.longitude - that.longitude))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mathematical angular distance from a geoPoint to another
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* @return angle expressed in radian in the mathematical convention
|
||||||
|
*/
|
||||||
|
private double mathAzimuthTo(GeoPoint that) {
|
||||||
|
return atan2(
|
||||||
|
(sin(this.longitude - that.longitude) * cos(that.latitude)),
|
||||||
|
(cos(this.latitude) * sin(that.latitude)
|
||||||
|
- sin(this.latitude) * cos(that.latitude)
|
||||||
|
* cos(this.longitude - that.longitude)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Angular distance from a geoPoint to another in azimuth
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : a GeoPoint
|
||||||
|
* @return angle expressed in radian in the azimuth convention
|
||||||
|
*/
|
||||||
|
public double azimuthTo(GeoPoint that) {
|
||||||
|
return fromMath(canonicalize(mathAzimuthTo(that)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redefines the to String method
|
||||||
|
*
|
||||||
|
* @return a string , [lon,lat], lon and lat being the longitude and
|
||||||
|
* latitude values of the GeoPoint
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format((Locale) null, "(%.4f,%.4f)",
|
||||||
|
toDegrees(this.longitude), toDegrees(this.latitude));
|
||||||
|
}
|
||||||
|
}
|
179
Alpano/src/ch/epfl/alpano/Interval1D.java
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition of a one-dimensional interval
|
||||||
|
*/
|
||||||
|
public final class Interval1D {
|
||||||
|
|
||||||
|
private final int includedFrom;
|
||||||
|
private final int includedTo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of an Interval
|
||||||
|
*
|
||||||
|
* @param includedFrom
|
||||||
|
* : min bound of the interval
|
||||||
|
* @param includedTo
|
||||||
|
* : max bound of the interval
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if min bound is bigger than max bound
|
||||||
|
*/
|
||||||
|
public Interval1D(int includedFrom, int includedTo) {
|
||||||
|
checkArgument(includedTo >= includedFrom);
|
||||||
|
this.includedFrom = includedFrom;
|
||||||
|
this.includedTo = includedTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the min value of the interval
|
||||||
|
*
|
||||||
|
* @return includedFrom
|
||||||
|
*/
|
||||||
|
public int includedFrom() {
|
||||||
|
return this.includedFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the max value of the interval
|
||||||
|
*
|
||||||
|
* @return includeTo
|
||||||
|
*/
|
||||||
|
public int includedTo() {
|
||||||
|
return this.includedTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the value v is contained in the interval
|
||||||
|
*
|
||||||
|
* @param v
|
||||||
|
* : integer
|
||||||
|
* @return true if v belongs to the interval
|
||||||
|
*/
|
||||||
|
public boolean contains(int v) {
|
||||||
|
return ((v >= this.includedFrom()) && (v <= this.includedTo()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of elements contained in an interval
|
||||||
|
*
|
||||||
|
* @return size of the interval
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return (this.includedTo() - this.includedFrom() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of elements shared between both intervals
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : an interval
|
||||||
|
* @return size of the Intersection between both intervals
|
||||||
|
* @see isUniunableWith(Interval that)
|
||||||
|
*/
|
||||||
|
public int sizeOfIntersectionWith(Interval1D that) {
|
||||||
|
int max = min(this.includedTo(), that.includedTo());
|
||||||
|
int min = max(this.includedFrom(), that.includedFrom());
|
||||||
|
int size = max - min + 1;
|
||||||
|
return (size < 0) ? 0 : size;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new Interval containing all elements of both intervals
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : an interval
|
||||||
|
* @return new Interval of the area generated by the bounding union of both
|
||||||
|
* intervals
|
||||||
|
*/
|
||||||
|
public Interval1D boundingUnion(Interval1D that) {
|
||||||
|
int max = max(this.includedTo(), that.includedTo());
|
||||||
|
int min = min(this.includedFrom(), that.includedFrom());
|
||||||
|
|
||||||
|
return new Interval1D(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if intervals have an intersection
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : an interval
|
||||||
|
* @return true if intervals intersects
|
||||||
|
*/
|
||||||
|
public boolean isUnionableWith(Interval1D that) {
|
||||||
|
return this.size() + that.size()
|
||||||
|
- this.sizeOfIntersectionWith(that) == this.boundingUnion(that)
|
||||||
|
.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the intersection of both intervals as a new interval
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : an interval
|
||||||
|
* @return new Interval generated from the intersection of both intervals
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if intervals do not intersect
|
||||||
|
* @see isUnionableWith(Interval1D that)
|
||||||
|
*/
|
||||||
|
public Interval1D union(Interval1D that) {
|
||||||
|
checkArgument(isUnionableWith(that));
|
||||||
|
|
||||||
|
int max = max(this.includedTo(), that.includedTo());
|
||||||
|
int min = min(this.includedFrom(), that.includedFrom());
|
||||||
|
|
||||||
|
return new Interval1D(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare an objects with the interval
|
||||||
|
*
|
||||||
|
* @param thatO
|
||||||
|
* : an object
|
||||||
|
* @return true if the object is an Interval with the same min and max
|
||||||
|
* bounds
|
||||||
|
* @see includedFrom() && includedTo()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object thatO) {
|
||||||
|
return (thatO instanceof Interval1D
|
||||||
|
&& this.includedFrom() == ((Interval1D) thatO).includedFrom()
|
||||||
|
&& this.includedTo() == ((Interval1D) thatO).includedTo());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redefines the hashCode for the method equals
|
||||||
|
*
|
||||||
|
* @return an integer, hash of min and max bounds
|
||||||
|
* @see equals(Object thatO)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(includedFrom(), includedTo());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redefines the toString method
|
||||||
|
*
|
||||||
|
* @return a string , [min..max], min and max being the min and max values
|
||||||
|
* of the interval
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
Locale l = null;
|
||||||
|
return String.format(l, "[%d..%d]", this.includedFrom(),
|
||||||
|
this.includedTo());
|
||||||
|
}
|
||||||
|
}
|
164
Alpano/src/ch/epfl/alpano/Interval2D.java
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition of a two dimensional interval
|
||||||
|
*
|
||||||
|
* @see Interval1D
|
||||||
|
*/
|
||||||
|
public final class Interval2D {
|
||||||
|
|
||||||
|
private final Interval1D iX;
|
||||||
|
private final Interval1D iY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a 2D interval based on two given 1D intervals
|
||||||
|
*
|
||||||
|
* @param iX
|
||||||
|
* @param iY
|
||||||
|
* @throws NullPointerException if iX or iY are null
|
||||||
|
*/
|
||||||
|
public Interval2D(Interval1D iX, Interval1D iY) {
|
||||||
|
Objects.requireNonNull(iX);
|
||||||
|
Objects.requireNonNull(iY);
|
||||||
|
this.iX = iX;
|
||||||
|
this.iY = iY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First 1D X-wise component of the 2D interval
|
||||||
|
*
|
||||||
|
* @return Interval1D
|
||||||
|
*/
|
||||||
|
public Interval1D iX() {
|
||||||
|
return this.iX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Second Y-wise 1D component of the 2D interval
|
||||||
|
*
|
||||||
|
* @return Interval1D
|
||||||
|
*/
|
||||||
|
public Interval1D iY() {
|
||||||
|
return this.iY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the value (x,y) is contained in the 2D interval
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : x value of the tuple
|
||||||
|
* @param y
|
||||||
|
* : y value of the tuple
|
||||||
|
* @return true iff the 2D interval contains a given tuple
|
||||||
|
*/
|
||||||
|
public boolean contains(int x, int y) {
|
||||||
|
return (iX().contains(x) && iY().contains(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of elements in the 2D interval
|
||||||
|
*
|
||||||
|
* @return cardinality of the 2D interval
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return (iX().size() * iY().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the interval between two 2D intervals
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : the other interval to check intersection with
|
||||||
|
* @return cardinality of the intersection interval of this interval an
|
||||||
|
* other given 2D interval
|
||||||
|
*/
|
||||||
|
public int sizeOfIntersectionWith(Interval2D that) {
|
||||||
|
return (this.iX().sizeOfIntersectionWith(that.iX())
|
||||||
|
* this.iY().sizeOfIntersectionWith(that.iY()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interval containing 2 given intersecting 2D intervals
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : the compared 2D interval
|
||||||
|
* @return 2D interval of the bounding union of two given 2D intervals
|
||||||
|
*/
|
||||||
|
public Interval2D boundingUnion(Interval2D that) {
|
||||||
|
return new Interval2D(this.iX().boundingUnion(that.iX()),
|
||||||
|
this.iY().boundingUnion(that.iY()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : the other interval to check union with
|
||||||
|
* @return true if the intersection of this interval with another exists
|
||||||
|
* false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isUnionableWith(Interval2D that) {
|
||||||
|
return (this.size() + that.size()
|
||||||
|
- this.sizeOfIntersectionWith(that) == this.boundingUnion(that)
|
||||||
|
.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the union of two 2D intervals
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : the other interval to check intersection with
|
||||||
|
* @return the 2D interval intersection of this interval with another
|
||||||
|
* @throws IllegalArgumentException if they are not unionable
|
||||||
|
*/
|
||||||
|
public Interval2D union(Interval2D that) {
|
||||||
|
checkArgument(this.isUnionableWith(that));
|
||||||
|
return this.boundingUnion(that);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare an object with the interval
|
||||||
|
*
|
||||||
|
* @param thatO
|
||||||
|
* : an object
|
||||||
|
* @return true if the object is a 2D Interval with the same size and
|
||||||
|
* Intervals
|
||||||
|
* @see Interval.equals(object thatO)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object thatO) {
|
||||||
|
return ((thatO instanceof Interval2D)
|
||||||
|
&& this.iX().equals(((Interval2D) thatO).iX())
|
||||||
|
&& this.iY().equals(((Interval2D) thatO).iY()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redefines the hashCode for the method equals
|
||||||
|
*
|
||||||
|
* @return an integer, hash of the hashes of both intervals
|
||||||
|
* @see equals(Object thatO)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(iX().includedFrom(),iX().includedTo(), iY().includedFrom(),iY().includedTo());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redefines the toString method
|
||||||
|
*
|
||||||
|
* @return a string in the form : [x1,y1]x[x2,y2] where xi, yi are the
|
||||||
|
* boundaries of a 1D interval Ii
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
|
||||||
|
return this.iX() + "x" + this.iY();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
178
Alpano/src/ch/epfl/alpano/Math2.java
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
import static java.lang.Math.sin;
|
||||||
|
import static java.lang.Math.floor;
|
||||||
|
import static java.lang.Math.abs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility interface of commonly used math calculations
|
||||||
|
*
|
||||||
|
* @see Math
|
||||||
|
*/
|
||||||
|
public interface Math2 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pi*2
|
||||||
|
*
|
||||||
|
* @see Math.PI
|
||||||
|
*/
|
||||||
|
public static final double PI2 = 2 * PI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the square of a value
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @return square function
|
||||||
|
*/
|
||||||
|
public static double sq(double x) {
|
||||||
|
return x * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* floor division remainder of x over y
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @return largest positive integer smaller or equal to x/y float value
|
||||||
|
*/
|
||||||
|
public static double floorMod(double x, double y) {
|
||||||
|
return x - y * floor(x / y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the haversin
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : a double, an angle
|
||||||
|
* @return sinus of a half angle squared
|
||||||
|
*/
|
||||||
|
public static double haversin(double x) {
|
||||||
|
return sq(sin(x / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the angular distance
|
||||||
|
*
|
||||||
|
* @param a1
|
||||||
|
* @param a2
|
||||||
|
* @return distance between two angles
|
||||||
|
*/
|
||||||
|
public static double angularDistance(double a1, double a2) {
|
||||||
|
return floorMod(a2 - a1 + PI, PI2) - PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linear interpolation
|
||||||
|
*
|
||||||
|
* @param y0
|
||||||
|
* : y value before x
|
||||||
|
* @param y1
|
||||||
|
* : y value after x
|
||||||
|
* @param x
|
||||||
|
* : a point
|
||||||
|
* @return linear interpolation at x
|
||||||
|
*/
|
||||||
|
public static double lerp(double y0, double y1, double x) {
|
||||||
|
return (y1 - y0) * x + y0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bilinear interpolation
|
||||||
|
*
|
||||||
|
* @param z00
|
||||||
|
* : z coordinate
|
||||||
|
* @param z10
|
||||||
|
* : z coordinate
|
||||||
|
* @param z01
|
||||||
|
* : z coordinate
|
||||||
|
* @param z11
|
||||||
|
* : z coordinate
|
||||||
|
* @param x
|
||||||
|
* : x coordinate of a point
|
||||||
|
* @param y
|
||||||
|
* : y coordinate of a point
|
||||||
|
* @return bilinear interpolation at (x,y)
|
||||||
|
* @see lerp(double y0, double y1, double x) for a linear interpolation
|
||||||
|
*/
|
||||||
|
public static double bilerp(double z00, double z10, double z01, double z11,
|
||||||
|
double x, double y) {
|
||||||
|
return (lerp(z00,z10,x)+lerp(z00,z01,y)+ ((z00 + z11) - (z10 + z01)) * x * y-z00);
|
||||||
|
/*return (z10 - z00) * x + (z01 - z00) * y
|
||||||
|
+ ((z00 + z11) - (z10 + z01)) * x * y + z00;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the first interval of size dX containing a root for the input function,
|
||||||
|
* properly contained in a given interval
|
||||||
|
*
|
||||||
|
* @param f
|
||||||
|
* : input function
|
||||||
|
* @param minX
|
||||||
|
* : starting interval's lower bound
|
||||||
|
* @param maxX
|
||||||
|
* : starting interval's upper bound
|
||||||
|
* @param dX
|
||||||
|
* : interval search's step size
|
||||||
|
* @return lower bound (as double) of the first interval of size dX
|
||||||
|
* containing a 0
|
||||||
|
* @throws IllegalArgumentException if minX is smaller than maxX or if dX is negative or null
|
||||||
|
*/
|
||||||
|
public static double firstIntervalContainingRoot(DoubleUnaryOperator f,
|
||||||
|
double minX, double maxX, double dX) {
|
||||||
|
checkArgument(minX<=maxX);
|
||||||
|
checkArgument(dX>0);
|
||||||
|
double newLowerBound = minX + dX;
|
||||||
|
|
||||||
|
if (newLowerBound > maxX) {
|
||||||
|
return ((f.applyAsDouble(minX) * f.applyAsDouble(maxX)) > 0)
|
||||||
|
? Double.POSITIVE_INFINITY : maxX - dX;
|
||||||
|
}
|
||||||
|
if (f.applyAsDouble(minX) * f.applyAsDouble(newLowerBound) < 0) {
|
||||||
|
return minX;
|
||||||
|
}
|
||||||
|
return firstIntervalContainingRoot(f, newLowerBound, maxX, dX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dichotomy algorithm implementation that finds the lower bound of the
|
||||||
|
* interval of size epsilon containing a root
|
||||||
|
*
|
||||||
|
* @param f
|
||||||
|
* : input function
|
||||||
|
* @param x1
|
||||||
|
* : starting interval's lower bound
|
||||||
|
* @param x2
|
||||||
|
* : starting interval's upper bound
|
||||||
|
* @param epsilon
|
||||||
|
* : smallest interval size allowed of this implementation
|
||||||
|
* @return lower bound (as double) of the interval of size epsilon
|
||||||
|
* containing a zero
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the function returns values of same sign
|
||||||
|
*/
|
||||||
|
public static double improveRoot(DoubleUnaryOperator f, double x1,
|
||||||
|
double x2, double epsilon) {
|
||||||
|
|
||||||
|
checkArgument(!(f.applyAsDouble(x1) * f.applyAsDouble(x2) > 0));
|
||||||
|
|
||||||
|
while (abs(x1 - x2) > epsilon) {
|
||||||
|
double avgX = (x1 + x2) * 0.5;
|
||||||
|
double avgImage = f.applyAsDouble(avgX);
|
||||||
|
if (avgImage == 0)
|
||||||
|
return avgX;
|
||||||
|
else if (avgImage * f.applyAsDouble(x1) > 0)
|
||||||
|
x1 = avgX;
|
||||||
|
else
|
||||||
|
x2 = avgX;
|
||||||
|
|
||||||
|
}
|
||||||
|
return x1;
|
||||||
|
}
|
||||||
|
}
|
349
Alpano/src/ch/epfl/alpano/Panorama.java
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkBounds;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panorama data holder computed using PanoramaComputer class using given
|
||||||
|
* panorama parameters
|
||||||
|
*
|
||||||
|
* @see PanoramaComputer
|
||||||
|
* @see PanoramaParameters
|
||||||
|
* @see Panorama.Builder
|
||||||
|
*/
|
||||||
|
public final class Panorama {
|
||||||
|
|
||||||
|
private final float[] distance;
|
||||||
|
private final float[] longitude;
|
||||||
|
private final float[] latitude;
|
||||||
|
private final float[] altitude;
|
||||||
|
private final float[] slope;
|
||||||
|
private final PanoramaParameters parameters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panorama constructor
|
||||||
|
*
|
||||||
|
* @param parameters
|
||||||
|
* : the PanoramaParameters
|
||||||
|
* @param distance
|
||||||
|
* : float table containing the distances
|
||||||
|
* @param longitude
|
||||||
|
* : float table containing the longitudes
|
||||||
|
* @param latitude
|
||||||
|
* : float table containing the latitudes
|
||||||
|
* @param altitude
|
||||||
|
* : float table containing the altitudes
|
||||||
|
* @param slope
|
||||||
|
* : float table containing the slopes
|
||||||
|
*/
|
||||||
|
private Panorama(PanoramaParameters parameters, float[] distance,
|
||||||
|
float[] longitude, float[] latitude, float[] altitude,
|
||||||
|
float[] slope) {
|
||||||
|
|
||||||
|
this.distance = distance;
|
||||||
|
this.longitude = longitude;
|
||||||
|
this.latitude = latitude;
|
||||||
|
this.altitude = altitude;
|
||||||
|
this.slope = slope;
|
||||||
|
this.parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parameters of the panorama
|
||||||
|
*
|
||||||
|
* @return the PanoramaParameters of the panorama
|
||||||
|
*/
|
||||||
|
public PanoramaParameters parameters() {
|
||||||
|
return this.parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the distance given a certain index x,y
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @return the distance given x,y
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
*/
|
||||||
|
public float distanceAt(int x, int y) {
|
||||||
|
checkBounds(parameters().isValidSampleIndex(x, y));
|
||||||
|
return this.distance[parameters().linearSampleIndex(x, y)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the distance given a certain index x,y
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @param d
|
||||||
|
* : the default distance
|
||||||
|
* @return the distance given x,y or the default distance if x or y are out
|
||||||
|
* of bounds
|
||||||
|
*/
|
||||||
|
public float distanceAt(int x, int y, float d) {
|
||||||
|
if (parameters().isValidSampleIndex(x, y))
|
||||||
|
return this.distance[parameters().linearSampleIndex(x, y)];
|
||||||
|
else
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the latitude given a certain index x,y
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @return the latitude given x,y
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
*/
|
||||||
|
public float latitudeAt(int x, int y) {
|
||||||
|
checkBounds(parameters().isValidSampleIndex(x, y));
|
||||||
|
return this.latitude[parameters().linearSampleIndex(x, y)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the longitude given a certain index x,y
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @return the longitude given x,y
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
*/
|
||||||
|
public float longitudeAt(int x, int y) {
|
||||||
|
checkBounds(parameters().isValidSampleIndex(x, y));
|
||||||
|
return this.longitude[parameters().linearSampleIndex(x, y)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the altitude given a certain index x,y
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @return the altitude given x,y
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
*/
|
||||||
|
public float elevationAt(int x, int y) {
|
||||||
|
checkBounds(parameters().isValidSampleIndex(x, y));
|
||||||
|
return this.altitude[parameters().linearSampleIndex(x, y)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slope given a certain index x,y
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @return the slope given x,y
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
*/
|
||||||
|
public float slopeAt(int x, int y) {
|
||||||
|
checkBounds(parameters().isValidSampleIndex(x, y));
|
||||||
|
return this.slope[parameters().linearSampleIndex(x, y)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panorama Builder:
|
||||||
|
*
|
||||||
|
* Gathers data for a Panorama and builds it.
|
||||||
|
*
|
||||||
|
* @see Panorama
|
||||||
|
* @see PanoramaComputer
|
||||||
|
*/
|
||||||
|
public final static class Builder {
|
||||||
|
|
||||||
|
private float[] distance;
|
||||||
|
private float[] longitude;
|
||||||
|
private float[] latitude;
|
||||||
|
private float[] altitude;
|
||||||
|
private float[] slope;
|
||||||
|
private final PanoramaParameters parameters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a panorama builder
|
||||||
|
*
|
||||||
|
* @param pp
|
||||||
|
* : the panorama parameters
|
||||||
|
* @throws NullPointerException
|
||||||
|
* if the PanoramaParameters object is null
|
||||||
|
*/
|
||||||
|
public Builder(PanoramaParameters pp) {
|
||||||
|
|
||||||
|
this.parameters = pp;
|
||||||
|
int size = pp.width() * pp.height();
|
||||||
|
this.distance = new float[size];
|
||||||
|
this.longitude = new float[size];
|
||||||
|
this.latitude = new float[size];
|
||||||
|
this.altitude = new float[size];
|
||||||
|
this.slope = new float[size];
|
||||||
|
Arrays.fill(distance, Float.POSITIVE_INFINITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enters the distance into the table at a given index
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @param distance
|
||||||
|
* : the value to enter in the table
|
||||||
|
* @return the builder (itself)
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if the Panorama has already been created
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
* @see checkBuilder
|
||||||
|
*/
|
||||||
|
public Builder setDistanceAt(int x, int y, float distance) {
|
||||||
|
checkBuilder();
|
||||||
|
checkBounds(this.parameters.isValidSampleIndex(x, y));
|
||||||
|
this.distance[this.parameters.linearSampleIndex(x, y)] = distance;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enters the longitude into the table at a given index
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @param longitude
|
||||||
|
* : the value to enter in the table
|
||||||
|
* @return the builder (itself)
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if the panorama has already been created
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
* @see checkBuilder
|
||||||
|
*/
|
||||||
|
public Builder setLongitudeAt(int x, int y, float longitude) {
|
||||||
|
checkBuilder();
|
||||||
|
checkBounds(this.parameters.isValidSampleIndex(x, y));
|
||||||
|
this.longitude[this.parameters.linearSampleIndex(x, y)] = longitude;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enters the latitude into the table at a given index
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @param latitude
|
||||||
|
* : the value to enter in the table
|
||||||
|
* @return the builder (itself)
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if the panorama has already been created
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
* @see checkBuilder
|
||||||
|
*/
|
||||||
|
public Builder setLatitudeAt(int x, int y, float latitude) {
|
||||||
|
checkBuilder();
|
||||||
|
checkBounds(this.parameters.isValidSampleIndex(x, y));
|
||||||
|
this.latitude[this.parameters.linearSampleIndex(x, y)] = latitude;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enters the altitude into the table at a given index
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @param elevation
|
||||||
|
* : the value to enter in the table
|
||||||
|
* @return the builder (itself)
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if the panorama has already been created
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
* @see checkBuilder
|
||||||
|
*/
|
||||||
|
public Builder setElevationAt(int x, int y, float elevation) {
|
||||||
|
checkBuilder();
|
||||||
|
checkBounds(this.parameters.isValidSampleIndex(x, y));
|
||||||
|
this.altitude[this.parameters.linearSampleIndex(x, y)] = elevation;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enters the slope into the table at a given index
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @param slope
|
||||||
|
* : the value to enter in the table
|
||||||
|
* @return the builder (itself)
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if the panorama has already been created
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if x or y are out of bounds
|
||||||
|
* @see checkBuilder
|
||||||
|
*/
|
||||||
|
public Builder setSlopeAt(int x, int y, float slope) {
|
||||||
|
checkBuilder();
|
||||||
|
checkBounds(this.parameters.isValidSampleIndex(x, y));
|
||||||
|
this.slope[this.parameters.linearSampleIndex(x, y)] = slope;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns the panorama
|
||||||
|
*
|
||||||
|
* @return the panorama
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if the panorama has already been created
|
||||||
|
* @see checkBuilder
|
||||||
|
*/
|
||||||
|
public Panorama build() {
|
||||||
|
checkBuilder();
|
||||||
|
Panorama p = new Panorama(parameters, distance, longitude, latitude,
|
||||||
|
altitude, slope);
|
||||||
|
this.distance = null;
|
||||||
|
this.longitude = null;
|
||||||
|
this.latitude = null;
|
||||||
|
this.altitude = null;
|
||||||
|
this.slope = null;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the panorama has already been built
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if the panorama has already been created
|
||||||
|
*/
|
||||||
|
private void checkBuilder() {
|
||||||
|
if (!(distance != null && longitude != null && latitude != null
|
||||||
|
&& altitude != null && slope != null))
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
132
Alpano/src/ch/epfl/alpano/PanoramaComputer.java
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.ElevationProfile;
|
||||||
|
import static java.lang.Math.tan;
|
||||||
|
import static java.lang.Math.cos;
|
||||||
|
import static ch.epfl.alpano.Math2.firstIntervalContainingRoot;
|
||||||
|
import static ch.epfl.alpano.Math2.improveRoot;
|
||||||
|
import static ch.epfl.alpano.Math2.sq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panorama computer that defines a panorama using its parameters and raycasting
|
||||||
|
*
|
||||||
|
* @see Panorama
|
||||||
|
* @see Panorama.Builder
|
||||||
|
*/
|
||||||
|
public final class PanoramaComputer {
|
||||||
|
|
||||||
|
private final ContinuousElevationModel cdem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Earth Athmosphere Refraction value
|
||||||
|
*/
|
||||||
|
private static final double REFRACTION_INDEX = 0.13;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height variation constant
|
||||||
|
*/
|
||||||
|
public static final double NATURAL_VARIATION = (1 - REFRACTION_INDEX)/ (2 * Distance.EARTH_RADIUS);
|
||||||
|
/**
|
||||||
|
* Precision for the first Root computation
|
||||||
|
*
|
||||||
|
* @see firstIntervalContainingRoot
|
||||||
|
*/
|
||||||
|
private static final int ROOTL = 64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Precision for the precise Root computation
|
||||||
|
*
|
||||||
|
* @see improveRoot
|
||||||
|
*/
|
||||||
|
private static final int ROOTP = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a panoramaComputer using continuous elevation values
|
||||||
|
*
|
||||||
|
* @param dem
|
||||||
|
* : the ContinuousElevationProfile
|
||||||
|
*
|
||||||
|
* @throws NullPointerExcepetion
|
||||||
|
* : if the parameter is null
|
||||||
|
*/
|
||||||
|
public PanoramaComputer(ContinuousElevationModel dem) {
|
||||||
|
Objects.requireNonNull(dem);
|
||||||
|
this.cdem = dem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses raycasting and finds its roots to retrieve visible GeoPoints from a
|
||||||
|
* given perspective
|
||||||
|
*
|
||||||
|
* @param pp
|
||||||
|
* : the Parameters of the panorama to build
|
||||||
|
* @return Panorama instance which has its attributes figured out using
|
||||||
|
* raycasting
|
||||||
|
*/
|
||||||
|
public Panorama computePanorama(PanoramaParameters pp) {
|
||||||
|
|
||||||
|
Panorama.Builder b = new Panorama.Builder(pp);
|
||||||
|
|
||||||
|
double h_azimuth, v_azimuth, rootL, rootP;
|
||||||
|
ElevationProfile evP;
|
||||||
|
double ray0 = pp.observerElevation();
|
||||||
|
double maxDistance = pp.maxDistance();
|
||||||
|
double lastroot = 0;
|
||||||
|
DoubleUnaryOperator ray;
|
||||||
|
for (int x = 0; x < pp.width(); ++x) {
|
||||||
|
h_azimuth = pp.azimuthForX(x);
|
||||||
|
lastroot = 0;
|
||||||
|
rootL = 0;
|
||||||
|
// Create elevation profile
|
||||||
|
evP = new ElevationProfile(cdem, pp.observerPosition(), h_azimuth,
|
||||||
|
maxDistance);
|
||||||
|
for (int y = pp.height() - 1; y >= 0
|
||||||
|
&& rootL != Double.POSITIVE_INFINITY; --y) {
|
||||||
|
v_azimuth = pp.altitudeForY(y);
|
||||||
|
|
||||||
|
// Raycast
|
||||||
|
ray = rayToGroundDistance(evP, ray0, tan(v_azimuth));
|
||||||
|
rootL = firstIntervalContainingRoot(ray, lastroot, maxDistance,
|
||||||
|
ROOTL);
|
||||||
|
if (rootL != Double.POSITIVE_INFINITY) {
|
||||||
|
rootP = improveRoot(ray, rootL, rootL + ROOTL, ROOTP);
|
||||||
|
// Fill in values
|
||||||
|
lastroot = rootP;
|
||||||
|
GeoPoint hit = evP.positionAt(rootP);
|
||||||
|
b.setDistanceAt(x, y, (float) (rootP / cos(v_azimuth)))
|
||||||
|
.setElevationAt(x, y,(float) cdem.elevationAt(hit))
|
||||||
|
.setLatitudeAt(x, y, (float) hit.latitude())
|
||||||
|
.setLongitudeAt(x, y, (float) hit.longitude())
|
||||||
|
.setSlopeAt(x, y, (float) cdem.slopeAt(hit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* distance function between a raycast from a point of view and the profile
|
||||||
|
*
|
||||||
|
* @param profile
|
||||||
|
* : profile elevation data
|
||||||
|
* @param ray0
|
||||||
|
* : vertical offset
|
||||||
|
* @param raySlope
|
||||||
|
* : vertical angle of view
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static DoubleUnaryOperator rayToGroundDistance(
|
||||||
|
ElevationProfile profile, double ray0, double raySlope) {
|
||||||
|
|
||||||
|
return (x) -> ray0 + x * raySlope - profile.elevationAt(x)
|
||||||
|
+ sq(x) *NATURAL_VARIATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
262
Alpano/src/ch/epfl/alpano/PanoramaParameters.java
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import static java.lang.Math.abs;
|
||||||
|
import static ch.epfl.alpano.Math2.angularDistance;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
import static ch.epfl.alpano.Azimuth.isCanonical;
|
||||||
|
import static ch.epfl.alpano.Azimuth.canonicalize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parameters of a panorama that are needed to compute a panorama when
|
||||||
|
* passed to PanoramaComputer class
|
||||||
|
*
|
||||||
|
* @see PanoramaComputer
|
||||||
|
* @see Panorama
|
||||||
|
*/
|
||||||
|
public final class PanoramaParameters {
|
||||||
|
|
||||||
|
private final GeoPoint observerPoint;
|
||||||
|
|
||||||
|
private final int observerElevation;
|
||||||
|
private final int maxDistance;
|
||||||
|
private final int width, height;
|
||||||
|
|
||||||
|
private final double centerAzimuth;
|
||||||
|
private final double horizontalFieldOfView, verticalFieldOfView;
|
||||||
|
private final double dX, dY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of PanoramaParameters
|
||||||
|
*
|
||||||
|
* @param observerPoint
|
||||||
|
* : the origin point of the viewer
|
||||||
|
* @param observerElevation
|
||||||
|
* : the origin altitude of the viewer
|
||||||
|
* @param centerAzimuth
|
||||||
|
* : the direction the viewer is facing
|
||||||
|
* @param horizontalFieldOfView
|
||||||
|
* : the horizontal field of view
|
||||||
|
* @param maxDistance
|
||||||
|
* : the maximum render distance
|
||||||
|
* @param width
|
||||||
|
* : the width to render(/of the view)
|
||||||
|
* @param height
|
||||||
|
* : the height to render(/of the view)
|
||||||
|
* @throws NullPointerException
|
||||||
|
* if the observerPoint is null
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the horizontal field of view is smaller or equal to 0 or
|
||||||
|
* bigger than 2pi, the height or width is negative or zero, the
|
||||||
|
* max distance is negative or zero,
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public PanoramaParameters(GeoPoint observerPoint, int observerElevation,
|
||||||
|
double centerAzimuth, double horizontalFieldOfView, int maxDistance,
|
||||||
|
int width, int height) {
|
||||||
|
Objects.requireNonNull(observerPoint);
|
||||||
|
checkArgument(isCanonical(centerAzimuth));
|
||||||
|
checkArgument(horizontalFieldOfView > 0);
|
||||||
|
checkArgument(horizontalFieldOfView <= Math2.PI2);
|
||||||
|
checkArgument(maxDistance > 0);
|
||||||
|
checkArgument(width > 0 && height > 0);
|
||||||
|
|
||||||
|
this.centerAzimuth = centerAzimuth;
|
||||||
|
this.horizontalFieldOfView = horizontalFieldOfView;
|
||||||
|
this.verticalFieldOfView = horizontalFieldOfView * (height - 1)
|
||||||
|
/ (width - 1);
|
||||||
|
this.height = height;
|
||||||
|
this.maxDistance = maxDistance;
|
||||||
|
this.observerElevation = observerElevation;
|
||||||
|
this.observerPoint = observerPoint;
|
||||||
|
this.width = width;
|
||||||
|
this.dX = horizontalFieldOfView / (width - 1.0);
|
||||||
|
this.dY = verticalFieldOfView / (height - 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an horizontal x value to an azimuth angle
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : the horizontal index to convert
|
||||||
|
* @return azimuth value corresponding to the horizontal pixel
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if x is smaller than zero or larger or equal to the width
|
||||||
|
*/
|
||||||
|
public double azimuthForX(double x) {
|
||||||
|
checkArgument(x >= 0 && x <= width() - 1);
|
||||||
|
double startAngle = angularDistance(horizontalFieldOfView()/2.0,centerAzimuth());
|
||||||
|
//centerAzimuth - (horizontalFieldOfView / 2.0);
|
||||||
|
return canonicalize(startAngle +(x * dX));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an azimuth angle to an horizontal x value
|
||||||
|
*
|
||||||
|
* @param a
|
||||||
|
* : the angle to convert
|
||||||
|
* @return horizontal index of the pixels corresponding to a given azimuth
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the angle is outside the field of view
|
||||||
|
*/
|
||||||
|
public double xForAzimuth(double a) {
|
||||||
|
checkArgument(isInVisibleFieldX(a));
|
||||||
|
double startAngle = angularDistance(horizontalFieldOfView()/2.0,centerAzimuth());
|
||||||
|
return angularDistance(startAngle,a)/ dX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the azimuth is within the field of view
|
||||||
|
*
|
||||||
|
* @param a
|
||||||
|
* : the angle to check
|
||||||
|
* @return true if an azimuth is within the current field of view
|
||||||
|
*/
|
||||||
|
private boolean isInVisibleFieldX(double a) {
|
||||||
|
return abs(angularDistance(centerAzimuth(),a)) <= horizontalFieldOfView() / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a vertical y value to a slope (angle)
|
||||||
|
*
|
||||||
|
* @param y
|
||||||
|
* : the vertical index to convert
|
||||||
|
* @return slope value corresponding to the vertical pixel
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if y is smaller than zero or larger or equal to the width
|
||||||
|
*/
|
||||||
|
public double altitudeForY(double y) {
|
||||||
|
checkArgument(y >= 0 && y <= height() - 1);
|
||||||
|
double startAngle = verticalFieldOfView() / 2.0;
|
||||||
|
return angularDistance(y*dY,startAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a slope (angle) to an vertical y value
|
||||||
|
*
|
||||||
|
* @param a
|
||||||
|
* : the angle to convert
|
||||||
|
* @return vertical index of the pixels corresponding to a given slope
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the angle is outside the field of view
|
||||||
|
*/
|
||||||
|
public double yForAltitude(double a) {
|
||||||
|
checkArgument(isInVisibleFieldY(a));
|
||||||
|
double startAngle = verticalFieldOfView() / 2.0;
|
||||||
|
return angularDistance(a,startAngle) / dY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a vertical angle is within the field of view
|
||||||
|
*
|
||||||
|
* @param a
|
||||||
|
* : angle to check
|
||||||
|
* @return true if a vertical angle is within the current field of view
|
||||||
|
*/
|
||||||
|
private boolean isInVisibleFieldY(double a) {
|
||||||
|
|
||||||
|
return abs(a) <= verticalFieldOfView() / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a 2D index is contained in the image
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @return true, if: 0<=x<height and 0<=y<width
|
||||||
|
*/
|
||||||
|
protected boolean isValidSampleIndex(int x, int y) {
|
||||||
|
return (x < width() && y < height() && x >= 0 && y >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a 2D index in a 1D index
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : horizontal index
|
||||||
|
* @param y
|
||||||
|
* : vertical index
|
||||||
|
* @return the 1D index value
|
||||||
|
*/
|
||||||
|
protected int linearSampleIndex(int x, int y) {
|
||||||
|
return (y * width() + x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the position of the viewer
|
||||||
|
*
|
||||||
|
* @return the viewer GeoPoint
|
||||||
|
*/
|
||||||
|
public GeoPoint observerPosition() {
|
||||||
|
return observerPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the altitude of the viewer
|
||||||
|
*
|
||||||
|
* @return the viewer altitude as an Integer
|
||||||
|
*/
|
||||||
|
public int observerElevation() {
|
||||||
|
return observerElevation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the direction the viewer is facing
|
||||||
|
*
|
||||||
|
* @return the azimuth the viewer is facing
|
||||||
|
*/
|
||||||
|
public double centerAzimuth() {
|
||||||
|
return centerAzimuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* horizontal field of view angle
|
||||||
|
*
|
||||||
|
* @return horizontal field of view
|
||||||
|
*/
|
||||||
|
public double horizontalFieldOfView() {
|
||||||
|
return horizontalFieldOfView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vertical field of view angle
|
||||||
|
*
|
||||||
|
* @return vertical field of view
|
||||||
|
*/
|
||||||
|
public double verticalFieldOfView() {
|
||||||
|
return verticalFieldOfView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximal render distance
|
||||||
|
*
|
||||||
|
* @return the maximal distance
|
||||||
|
*/
|
||||||
|
public int maxDistance() {
|
||||||
|
return maxDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the width of the view
|
||||||
|
*
|
||||||
|
* @return the width of the view
|
||||||
|
*/
|
||||||
|
public int width() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the height of the view
|
||||||
|
*
|
||||||
|
* @return the height of the view
|
||||||
|
*/
|
||||||
|
public int height() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
}
|
112
Alpano/src/ch/epfl/alpano/Preconditions.java
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface commonly used as argument check for exception throwing
|
||||||
|
*
|
||||||
|
* @see IllegalArgumentExcepetion
|
||||||
|
* @see NullPointerException
|
||||||
|
* @see IndexOutOfBoundsException
|
||||||
|
*/
|
||||||
|
public interface Preconditions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error with a message based on a boolean
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
* : boolean
|
||||||
|
* @param message
|
||||||
|
* : string
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* with message if the boolean is false
|
||||||
|
*/
|
||||||
|
public static void checkArgument(boolean b, String message) {
|
||||||
|
if (!b)
|
||||||
|
throw (new IllegalArgumentException(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error based on if a boolean is false
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
* : boolean
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the boolean is false
|
||||||
|
*/
|
||||||
|
public static void checkArgument(boolean b) {
|
||||||
|
if (!b)
|
||||||
|
throw (new IllegalArgumentException());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error with a message if an double is equal to 0. Works with an
|
||||||
|
* Integer too
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* : double
|
||||||
|
* @param message
|
||||||
|
* : string
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* with message if the double is equal to 0
|
||||||
|
*/
|
||||||
|
public static void checkNonNul(double i, String message) {
|
||||||
|
if (i == 0.)
|
||||||
|
throw (new NullPointerException(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error based on if an double is equal to 0. Works with an
|
||||||
|
* Integer too
|
||||||
|
*
|
||||||
|
* @param i
|
||||||
|
* : double
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the d is equal to 0
|
||||||
|
*/
|
||||||
|
public static void checkNonNul(double i) {
|
||||||
|
if (i == 0.)
|
||||||
|
throw (new NullPointerException());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error with a message based on if an Integer is out of bounds
|
||||||
|
*
|
||||||
|
* @param val
|
||||||
|
* : the Integer to check
|
||||||
|
* @param upper
|
||||||
|
* : upper bound
|
||||||
|
* @param lower
|
||||||
|
* : lower bound
|
||||||
|
* @param message
|
||||||
|
* : string
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* with a message if the value is out of bounds
|
||||||
|
*/
|
||||||
|
public static void checkBounds(boolean b,
|
||||||
|
String message) {
|
||||||
|
if (!b)
|
||||||
|
throw (new IndexOutOfBoundsException());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error based on if an Integer is out of bounds
|
||||||
|
*
|
||||||
|
* @param val
|
||||||
|
* : the Integer to check
|
||||||
|
* @param upper
|
||||||
|
* : upper bound
|
||||||
|
* @param lower
|
||||||
|
* : lower bound
|
||||||
|
* @throws IndexOutOfBoundsException
|
||||||
|
* if the value is out of bounds
|
||||||
|
*/
|
||||||
|
public static void checkBounds(boolean b) {
|
||||||
|
|
||||||
|
if (!b)
|
||||||
|
throw (new IndexOutOfBoundsException());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
141
Alpano/src/ch/epfl/alpano/dem/CompositElevationProfile.java
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to build composite Elevation profiles
|
||||||
|
* @see ElevationProfile
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class CompositElevationProfile implements Profile {
|
||||||
|
|
||||||
|
|
||||||
|
private List<ElevationProfile> profiles;
|
||||||
|
private final GeoPoint origin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param c : elevations profiles
|
||||||
|
*/
|
||||||
|
public CompositElevationProfile(
|
||||||
|
ElevationProfile... c) {
|
||||||
|
this.origin = c[0].origin();
|
||||||
|
profiles = Arrays.asList(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param c List of ElevationProfiles
|
||||||
|
*/
|
||||||
|
public CompositElevationProfile(
|
||||||
|
List<ElevationProfile> c) {
|
||||||
|
|
||||||
|
this.origin = c.get(0).origin();
|
||||||
|
profiles = c;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param cDem : Continuous elevation model
|
||||||
|
* @param trekkingSpots
|
||||||
|
*/
|
||||||
|
public CompositElevationProfile(ContinuousElevationModel cDem, List<GeoPoint> trekkingSpots){
|
||||||
|
|
||||||
|
this.profiles = new ArrayList<ElevationProfile>();
|
||||||
|
this.origin = trekkingSpots.get(0);
|
||||||
|
for (int index = 0; index < trekkingSpots.size()-1; index++){
|
||||||
|
GeoPoint current= trekkingSpots.get(index);
|
||||||
|
GeoPoint next = trekkingSpots.get(index+1);
|
||||||
|
if(!current.equals(next) &¤t.distanceTo(next)!=0){
|
||||||
|
ElevationProfile evP= new ElevationProfile(cDem, current,current.azimuthTo(next), current.distanceTo(next));
|
||||||
|
this.profiles.add(evP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the origin
|
||||||
|
* @return origin geopoint
|
||||||
|
*/
|
||||||
|
public GeoPoint origin() {
|
||||||
|
return this.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length
|
||||||
|
*/
|
||||||
|
public double length() {
|
||||||
|
double s = 0;
|
||||||
|
for (ElevationProfile evP : this.profiles) {
|
||||||
|
s += evP.length();
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double elevationAt(double x) {
|
||||||
|
|
||||||
|
|
||||||
|
for (ElevationProfile evP : profiles) {
|
||||||
|
|
||||||
|
if (x - evP.length() < 0) {
|
||||||
|
return evP.elevationAt(x);
|
||||||
|
} else {
|
||||||
|
x -= evP.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] position(double x) {
|
||||||
|
for (ElevationProfile evP : profiles){
|
||||||
|
|
||||||
|
if (x-evP.length() < 0){
|
||||||
|
return evP.position(x);
|
||||||
|
}else {x-=evP.length();}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double slopeAt(double x) {
|
||||||
|
for (ElevationProfile evP : profiles) {
|
||||||
|
|
||||||
|
if (x - evP.length() < 0) {
|
||||||
|
return evP.slopeAt(x);
|
||||||
|
} else {
|
||||||
|
x -= evP.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeoPoint positionAt(double x) {
|
||||||
|
for (ElevationProfile evP : profiles) {
|
||||||
|
|
||||||
|
if (x - evP.length() < 0) {
|
||||||
|
return evP.positionAt(x);
|
||||||
|
} else {
|
||||||
|
x -= evP.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkNonNul;
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class taking multiple DEMs and merging them into one Composite DEM
|
||||||
|
*
|
||||||
|
* @see DiscreteElevationModel
|
||||||
|
*/
|
||||||
|
final class CompositeDiscreteElevationModel implements DiscreteElevationModel {
|
||||||
|
|
||||||
|
private final DiscreteElevationModel dem1, dem2;
|
||||||
|
private final Interval2D extent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a composite discrete elevation model, by uniting two DEMs
|
||||||
|
*
|
||||||
|
* @param dem1
|
||||||
|
* : the first DEM
|
||||||
|
* @param dem2
|
||||||
|
* : the second DEM
|
||||||
|
* @throws NullPointerException
|
||||||
|
* if one of the two DEMs has size 0
|
||||||
|
*/
|
||||||
|
CompositeDiscreteElevationModel(DiscreteElevationModel dem1,
|
||||||
|
DiscreteElevationModel dem2) {
|
||||||
|
checkNonNul(dem1.extent().size());
|
||||||
|
checkNonNul(dem2.extent().size());
|
||||||
|
this.dem1 = dem1;
|
||||||
|
this.dem2 = dem2;
|
||||||
|
this.extent = dem1.extent().union(dem2.extent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
dem1.close();
|
||||||
|
dem2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Interval2D extent() {
|
||||||
|
return extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the elevation of a point in the containing DEM
|
||||||
|
*
|
||||||
|
* @param x:
|
||||||
|
* x coordinate
|
||||||
|
* @param y:
|
||||||
|
* y coordinate
|
||||||
|
* @return the elevation of the point.
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the point is not contained in the composite DEM
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public double elevationSample(int x, int y) {
|
||||||
|
checkArgument(this.extent.contains(x, y));
|
||||||
|
|
||||||
|
if (dem1.extent().contains(x, y))
|
||||||
|
return dem1.elevationSample(x, y);
|
||||||
|
else
|
||||||
|
return dem2.elevationSample(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
125
Alpano/src/ch/epfl/alpano/dem/ContinuousElevationModel.java
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkNonNul;
|
||||||
|
import static ch.epfl.alpano.Distance.EARTH_RADIUS;
|
||||||
|
import static ch.epfl.alpano.Math2.bilerp;
|
||||||
|
import static ch.epfl.alpano.Math2.sq;
|
||||||
|
import static ch.epfl.alpano.dem.DiscreteElevationModel.sampleIndex;
|
||||||
|
import static ch.epfl.alpano.dem.DiscreteElevationModel.SAMPLES_PER_RADIAN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An elevation model defined for every coordinates of real numbers built from a
|
||||||
|
* discontinuous elevation model using bi-dimentional interpolation on known
|
||||||
|
* elevation values
|
||||||
|
*
|
||||||
|
* @see DiscreteElevationModel
|
||||||
|
*/
|
||||||
|
public final class ContinuousElevationModel {
|
||||||
|
|
||||||
|
private DiscreteElevationModel dem;
|
||||||
|
private static final int SAMPLE_DISTANCE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, builds a Continuous elevation model
|
||||||
|
*
|
||||||
|
* @param dem:
|
||||||
|
* a discrete elevation model
|
||||||
|
* @throws NullPointerException
|
||||||
|
* if the dem's size equals zero
|
||||||
|
*/
|
||||||
|
public ContinuousElevationModel(DiscreteElevationModel dem) {
|
||||||
|
checkNonNul(dem.extent().size());
|
||||||
|
this.dem = dem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the elevation at a given point using bilinear interpolation
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* : the GeoPoint from which we want to know the elevation
|
||||||
|
* @return the elevation at the point as a double
|
||||||
|
* @see bilerp(double z00, double z10, double z01, double z11, double x,
|
||||||
|
* double y)
|
||||||
|
*/
|
||||||
|
public double elevationAt(GeoPoint p) {
|
||||||
|
|
||||||
|
double x = sampleIndex(p.longitude());
|
||||||
|
double y = sampleIndex(p.latitude());
|
||||||
|
int x1 = (int) Math.floor(x);
|
||||||
|
int y1 = (int) Math.floor(y);
|
||||||
|
return bilerp(getElevationAt(x1, y1),
|
||||||
|
getElevationAt(x1 + SAMPLE_DISTANCE, y1),
|
||||||
|
getElevationAt(x1, y1 + SAMPLE_DISTANCE),
|
||||||
|
getElevationAt(x1 + SAMPLE_DISTANCE, y1 + SAMPLE_DISTANCE),
|
||||||
|
x - x1, y - y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the elevation at a given point (x,y)
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : the x coordinate of the point
|
||||||
|
* @param y
|
||||||
|
* : the y coordinate of the point
|
||||||
|
* @return the elevation at the given coordinates or 0 if the point is out
|
||||||
|
* of range
|
||||||
|
* @see elevationSample(int x, int y)
|
||||||
|
*/
|
||||||
|
private double getElevationAt(int x, int y) {
|
||||||
|
|
||||||
|
return (dem.extent().contains(x, y)) ? dem.elevationSample(x, y) : 0;
|
||||||
|
/*
|
||||||
|
* if(dem.extent().contains(x, y)){ return dem.elevationSample(x, y); }
|
||||||
|
* return 0;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the angular elevation at a given point (x,y)
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : the x coordinate of the point
|
||||||
|
* @param y
|
||||||
|
* : the y coordinate of the point
|
||||||
|
* @return the slope at the point as a double
|
||||||
|
* @see getElevationAt(int x , int y)
|
||||||
|
* @see slopeAt(GeoPoint p)
|
||||||
|
*/
|
||||||
|
private double theta(int x, int y) {
|
||||||
|
double zA2 = sq(
|
||||||
|
getElevationAt(x + SAMPLE_DISTANCE, y) - getElevationAt(x, y));
|
||||||
|
double zB2 = sq(
|
||||||
|
getElevationAt(x, y + SAMPLE_DISTANCE) - getElevationAt(x, y));
|
||||||
|
|
||||||
|
double d = EARTH_RADIUS / SAMPLES_PER_RADIAN;
|
||||||
|
|
||||||
|
return Math.acos(d / Math.sqrt(zA2 + zB2 + sq(d)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the slope at a given point using bilinear interpolation
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* : the GeoPoint from which we want to know the slope
|
||||||
|
* @return the slope at the point as a double
|
||||||
|
* @see bilerp(double z00, double z10, double z01, double z11, double x,
|
||||||
|
* double y)
|
||||||
|
*/
|
||||||
|
public double slopeAt(GeoPoint p) {
|
||||||
|
double x = sampleIndex(p.longitude());
|
||||||
|
double y = sampleIndex(p.latitude());
|
||||||
|
|
||||||
|
int x1 = (int) Math.floor(x);
|
||||||
|
int y1 = (int) Math.floor(y);
|
||||||
|
|
||||||
|
return bilerp(theta(x1, y1), theta(x1 + SAMPLE_DISTANCE, y1),
|
||||||
|
theta(x1, y1 + SAMPLE_DISTANCE),
|
||||||
|
theta(x1 + SAMPLE_DISTANCE, y1 + SAMPLE_DISTANCE), x - x1,
|
||||||
|
y - y1);
|
||||||
|
}
|
||||||
|
}
|
72
Alpano/src/ch/epfl/alpano/dem/DiscreteElevationModel.java
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface of a DEM
|
||||||
|
*/
|
||||||
|
public interface DiscreteElevationModel extends AutoCloseable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount of elevation samples per degree
|
||||||
|
*/
|
||||||
|
public static final int SAMPLES_PER_DEGREE = 3600;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount of elevation samples per radian
|
||||||
|
*/
|
||||||
|
public static final double SAMPLES_PER_RADIAN = (180.0 / PI)
|
||||||
|
* SAMPLES_PER_DEGREE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of samples for a given angle
|
||||||
|
*
|
||||||
|
* @param angle
|
||||||
|
* : an angle in radian
|
||||||
|
* @return the amount of samples for the angle
|
||||||
|
* @see SAMPLE_PER_RADIAN
|
||||||
|
*/
|
||||||
|
public static double sampleIndex(double angle) {
|
||||||
|
return angle * SAMPLES_PER_RADIAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract function that returns the 2D interval of a DEM
|
||||||
|
*
|
||||||
|
* @return the 2D interval of the DEM
|
||||||
|
*/
|
||||||
|
public Interval2D extent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract function that returns the elevation at a given point (x,y) of a
|
||||||
|
* DEM
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : the x coordinate of the point
|
||||||
|
* @param y
|
||||||
|
* : the y coordinate of the point
|
||||||
|
* @return the elevation at the point as a double
|
||||||
|
*/
|
||||||
|
public double elevationSample(int x, int y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a new composite DEM containing two DEMs
|
||||||
|
*
|
||||||
|
* @param that
|
||||||
|
* : one of the two DEMs
|
||||||
|
* @return a new composite DEM obtained with the union of two DEMs
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the two DEMs can not form a union
|
||||||
|
* @see isUnionableWith(Interval2D that)
|
||||||
|
*/
|
||||||
|
default CompositeDiscreteElevationModel union(DiscreteElevationModel that) {
|
||||||
|
checkArgument(this.extent().isUnionableWith(that.extent()));
|
||||||
|
return new CompositeDiscreteElevationModel(this, that);
|
||||||
|
}
|
||||||
|
}
|
154
Alpano/src/ch/epfl/alpano/dem/ElevationProfile.java
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import static ch.epfl.alpano.Math2.PI2;
|
||||||
|
import static ch.epfl.alpano.Distance.toRadians;
|
||||||
|
import static ch.epfl.alpano.Math2.lerp;
|
||||||
|
import static ch.epfl.alpano.Math2.floorMod;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
import static ch.epfl.alpano.Azimuth.isCanonical;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Computing an elevation profile
|
||||||
|
*
|
||||||
|
* @see ContinuousElevationModel
|
||||||
|
* @see GeoPoint
|
||||||
|
*/
|
||||||
|
public final class ElevationProfile implements Profile{
|
||||||
|
|
||||||
|
private final ContinuousElevationModel elevationModel;
|
||||||
|
private final GeoPoint origin;
|
||||||
|
private final double azimuth;
|
||||||
|
private final double[][] discretePositions;
|
||||||
|
private final double length;
|
||||||
|
private static final double MSTEP = 4096.0; // Amount of steps to do
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of an Elevation profile, computes all the points at the
|
||||||
|
* beginning and saves them in a file
|
||||||
|
*
|
||||||
|
* @param elevationModel
|
||||||
|
* : a ContinuousElevationModel that the ElevationProfile will
|
||||||
|
* use to build itself
|
||||||
|
* @param origin
|
||||||
|
* : origin GeoPoint
|
||||||
|
* @param azimuth
|
||||||
|
* : the azimuth angle to use
|
||||||
|
* @param length
|
||||||
|
* : the distance up to which the profile shall be computed
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* : if length is negative of azimuth is not canonical
|
||||||
|
*/
|
||||||
|
public ElevationProfile(ContinuousElevationModel elevationModel,
|
||||||
|
GeoPoint origin, double azimuth, double length) {
|
||||||
|
|
||||||
|
checkArgument(length > 0);
|
||||||
|
checkArgument(isCanonical(azimuth));
|
||||||
|
requireNonNull(elevationModel);
|
||||||
|
requireNonNull(origin);
|
||||||
|
|
||||||
|
this.azimuth = azimuth;
|
||||||
|
this.origin = origin;
|
||||||
|
this.elevationModel = elevationModel;
|
||||||
|
this.length = length;
|
||||||
|
|
||||||
|
discretePositions = new double[(int) (length / MSTEP) + 2][2];
|
||||||
|
for (int i = 0; i < discretePositions.length; i++) {
|
||||||
|
discretePositions[i] = position(i * MSTEP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double length(){
|
||||||
|
return this.length;
|
||||||
|
}
|
||||||
|
public GeoPoint origin(){
|
||||||
|
return this.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the elevation at a distance x
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @return elevation at the point of a distance x
|
||||||
|
*/
|
||||||
|
public double elevationAt(double x) {
|
||||||
|
checkArgument(x <= length && x >= 0);
|
||||||
|
return elevationModel.elevationAt(positionAt(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the GeoPoint at a distance x
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @return returns the position as a GeoPoint at a distance x
|
||||||
|
*/
|
||||||
|
public GeoPoint positionAt(double x) {
|
||||||
|
checkArgument(x <= length && x >= 0);
|
||||||
|
int offSet = (int) x / (int) MSTEP;
|
||||||
|
double lambda = lerp(discretePositions[offSet][0],
|
||||||
|
discretePositions[offSet + 1][0], x / MSTEP - offSet);
|
||||||
|
double phi = lerp(discretePositions[offSet][1],
|
||||||
|
discretePositions[offSet + 1][1], x / MSTEP - offSet);
|
||||||
|
return new GeoPoint(lambda, phi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rounds the value x depending on the value of mStep
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param mStep
|
||||||
|
* @return relative rounded x value
|
||||||
|
*/
|
||||||
|
public double relativeRound(double x, double mStep) {
|
||||||
|
return Math.round(x / mStep) * mStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the position of a point along the azimuth
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @return coordinates of a point at a distance x of the origin
|
||||||
|
*/
|
||||||
|
public double[] position(double x) {
|
||||||
|
double sinphi0 = Math.sin(origin.latitude());
|
||||||
|
double cosphi0 = Math.cos(origin.latitude());
|
||||||
|
double sinx = Math.sin(toRadians(x));
|
||||||
|
double cosx = Math.cos(toRadians(x));
|
||||||
|
double sina = Math.sin(PI2 - azimuth);
|
||||||
|
double cosa = Math.cos(PI2 - azimuth);
|
||||||
|
|
||||||
|
double phi = Math.asin(sinphi0 * cosx + cosphi0 * sinx * cosa);
|
||||||
|
|
||||||
|
double a = origin.longitude() - Math.asin(sina * sinx / Math.cos(phi))
|
||||||
|
+ Math.PI;
|
||||||
|
double lambda = floorMod(a, PI2) - Math.PI;
|
||||||
|
return new double[] { lambda, phi };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the slope at the distance x
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @return the slope at the distance x
|
||||||
|
*/
|
||||||
|
public double slopeAt(double x) {
|
||||||
|
checkArgument(x <= length && x >= 0);
|
||||||
|
return elevationModel.slopeAt(positionAt(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public double effort(){
|
||||||
|
double de;
|
||||||
|
double ef=length;
|
||||||
|
for(int i =0; i<length-1;++i){
|
||||||
|
de = elevationAt(i+1)-elevationAt(i);
|
||||||
|
|
||||||
|
ef+= 9.81*Math.abs(de);
|
||||||
|
}
|
||||||
|
return ef;
|
||||||
|
}
|
||||||
|
}
|
114
Alpano/src/ch/epfl/alpano/dem/HgtDiscreteElevationModel.java
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ShortBuffer;
|
||||||
|
import java.nio.channels.FileChannel.MapMode;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import ch.epfl.alpano.Interval1D;
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class building a DEM using a HGT file
|
||||||
|
*
|
||||||
|
* @see DiscreteElevationModel
|
||||||
|
*/
|
||||||
|
public class HgtDiscreteElevationModel implements DiscreteElevationModel {
|
||||||
|
|
||||||
|
public static final long HGT_SAMPLES = 12967201;
|
||||||
|
private ShortBuffer b;
|
||||||
|
private FileInputStream s;
|
||||||
|
private Interval2D extent;
|
||||||
|
private static final Pattern P = Pattern
|
||||||
|
.compile("([NS])(\\d{2})([EW])(\\d{3})\\.hgt");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a HGT DEM
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* : The HGT file
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the file is not valid in length
|
||||||
|
* @see DiscreteElevationModel
|
||||||
|
*/
|
||||||
|
public HgtDiscreteElevationModel(File file) {
|
||||||
|
String name = file.getName();
|
||||||
|
Matcher m = checkFileName(name);
|
||||||
|
checkArgument(file.length() == HGT_SAMPLES * 2);
|
||||||
|
|
||||||
|
int latMult = (m.group(1).charAt(0) == 'N' ? 1 : -1)
|
||||||
|
* SAMPLES_PER_DEGREE;
|
||||||
|
int lat = Integer.parseInt(m.group(2));
|
||||||
|
int lonMult = (m.group(3).charAt(0) == 'E' ? 1 : -1)
|
||||||
|
* SAMPLES_PER_DEGREE;
|
||||||
|
int lon = Integer.parseInt(m.group(4));
|
||||||
|
|
||||||
|
Interval1D ex1 = new Interval1D((lon * lonMult),
|
||||||
|
(lon * lonMult + SAMPLES_PER_DEGREE));
|
||||||
|
Interval1D ex2 = new Interval1D((lat * latMult),
|
||||||
|
(lat * latMult + SAMPLES_PER_DEGREE));
|
||||||
|
extent = new Interval2D(ex1, ex2);
|
||||||
|
|
||||||
|
try {
|
||||||
|
s = new FileInputStream(file);
|
||||||
|
b = s.getChannel().map(MapMode.READ_ONLY, 0, file.length())
|
||||||
|
.asShortBuffer();
|
||||||
|
s.getChannel().close();
|
||||||
|
s.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Interval2D extent() {
|
||||||
|
return extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the elevation of a point using the data from a HGT file
|
||||||
|
*
|
||||||
|
* @param x:
|
||||||
|
* x coordinate
|
||||||
|
* @param y:
|
||||||
|
* y coordinate
|
||||||
|
* @return the height of the point in the HGT file at the given coordinates
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public double elevationSample(int x, int y) {
|
||||||
|
checkArgument(this.extent().contains(x, y));
|
||||||
|
return b.get((x - extent.iX().includedFrom())
|
||||||
|
+ (extent.iY().includedTo() - y) * (60 * 60 + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks a String for a HGT file name using regex.
|
||||||
|
*
|
||||||
|
* @param txt
|
||||||
|
* : the file name to check
|
||||||
|
* @returns the Matcher m (String with regex applied)
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the file name is not valid
|
||||||
|
*/
|
||||||
|
private static final Matcher checkFileName(String txt) {
|
||||||
|
Matcher m = P.matcher(txt);
|
||||||
|
|
||||||
|
checkArgument((txt.length() == 11 && m.find()));
|
||||||
|
int lat = Integer.parseInt(m.group(2));
|
||||||
|
int lon = Integer.parseInt(m.group(4));
|
||||||
|
checkArgument(!(lat > 90 || lon > 180));
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
Alpano/src/ch/epfl/alpano/dem/Profile.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface describing an elevation profile
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface Profile {
|
||||||
|
|
||||||
|
|
||||||
|
public double length();
|
||||||
|
public double elevationAt(double x);
|
||||||
|
public GeoPoint positionAt(double x);
|
||||||
|
public double[] position(double x);
|
||||||
|
public double slopeAt(double x);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
736
Alpano/src/ch/epfl/alpano/gui/Alpano.java
Normal file
@ -0,0 +1,736 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import static ch.epfl.alpano.Math2.sq;
|
||||||
|
import static ch.epfl.alpano.PanoramaComputer.NATURAL_VARIATION;
|
||||||
|
import static ch.epfl.alpano.summit.GazetteerParser.readSummitsFrom;
|
||||||
|
import static java.awt.Desktop.getDesktop;
|
||||||
|
import static java.lang.Math.abs;
|
||||||
|
import static java.lang.Math.atan;
|
||||||
|
import static java.lang.Math.toDegrees;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.Azimuth;
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import ch.epfl.alpano.dem.CompositElevationProfile;
|
||||||
|
import ch.epfl.alpano.summit.Summit;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.geometry.HPos;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.canvas.Canvas;
|
||||||
|
import javafx.scene.canvas.GraphicsContext;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ChoiceBox;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Menu;
|
||||||
|
import javafx.scene.control.MenuBar;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.RadioMenuItem;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.TextArea;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.control.TextFormatter;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.ColumnConstraints;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main class used for GUI Controls the program through events
|
||||||
|
*/
|
||||||
|
public class Alpano extends Application {
|
||||||
|
|
||||||
|
private static final String ALPANO_WINDOW_TITLE = "Alpano";
|
||||||
|
private static final PanoramaUserParameters DEFAULT_PARAMS = PredefinedPanoramas.JURA_ALPS.get();
|
||||||
|
private static final String SUMMIT_FILENAME = "alps.txt";
|
||||||
|
private static final List<Summit> SUMMIT_LIST;
|
||||||
|
private static final PanoramaComputerBean pcbean;
|
||||||
|
private static final PanoramaParametersBean ppbean;
|
||||||
|
private static final MapViewBean mvbean;
|
||||||
|
private static final CursorInfo cursor = new CursorInfo();
|
||||||
|
|
||||||
|
static final String REPAINT_NOTICE_TEXT = "CLICK HERE TO REPAINT PANORAMA";
|
||||||
|
static final String UPDATE_NOTICE_TEXT = "CLICK HERE TO UPDATE PANORAMA";
|
||||||
|
|
||||||
|
static {
|
||||||
|
List<Summit> slist;
|
||||||
|
try {
|
||||||
|
slist = readSummitsFrom(new File(SUMMIT_FILENAME));
|
||||||
|
} catch (IOException e) {
|
||||||
|
slist = Collections.emptyList();
|
||||||
|
// No summits found in file, empty list
|
||||||
|
}
|
||||||
|
SUMMIT_LIST = slist;
|
||||||
|
pcbean = new PanoramaComputerBean(SUMMIT_LIST);
|
||||||
|
ppbean = new PanoramaParametersBean(DEFAULT_PARAMS);
|
||||||
|
mvbean = new MapViewBean(pcbean);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Fuction ran at the start of the program
|
||||||
|
* @param args : program arguments
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Application.launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) throws Exception {
|
||||||
|
|
||||||
|
BorderPane root = new BorderPane();
|
||||||
|
root.setCenter(makePanoPane());
|
||||||
|
root.setBottom(makeParamsGrid());
|
||||||
|
root.setTop(makeMenu());
|
||||||
|
Scene scene = new Scene(root, 1100, 700);
|
||||||
|
|
||||||
|
primaryStage.setTitle(ALPANO_WINDOW_TITLE);
|
||||||
|
primaryStage.setScene(scene);
|
||||||
|
primaryStage.show();
|
||||||
|
primaryStage.setOnHidden(e -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e1) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
Platform.exit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a stack pane containing the panogroup and all the notices
|
||||||
|
* @return a stack pane for the panorama group and notices
|
||||||
|
*/
|
||||||
|
private Node makePanoPane() {
|
||||||
|
|
||||||
|
ScrollPane panoScrollPane = new ScrollPane(makePanoGroup());
|
||||||
|
|
||||||
|
return new StackPane(panoScrollPane, makePainterNotice(),
|
||||||
|
makeDownloadNotice(), makeUpdateNotice());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a grid of nodes and binds the input fields to the computable
|
||||||
|
* variables in PanoramaParameterBean
|
||||||
|
*
|
||||||
|
* @return node grid displaying the parameters
|
||||||
|
*/
|
||||||
|
private Node makeParamsGrid() {
|
||||||
|
GridPane grid = new GridPane();
|
||||||
|
|
||||||
|
Map<UserParameter, Label> labels = new EnumMap<UserParameter, Label>(
|
||||||
|
UserParameter.class);
|
||||||
|
Map<UserParameter, Node> fields = new EnumMap<UserParameter, Node>(
|
||||||
|
UserParameter.class);
|
||||||
|
|
||||||
|
int nbParams = 0;
|
||||||
|
for (UserParameter parameter : UserParameter.values()) {
|
||||||
|
Label label = new Label(parameter.label());
|
||||||
|
labels.put(parameter, label);
|
||||||
|
GridPane.setHalignment(label, HPos.RIGHT);
|
||||||
|
fields.put(parameter, makeField(parameter));
|
||||||
|
|
||||||
|
nbParams++;
|
||||||
|
if (nbParams % 3 == 0) {
|
||||||
|
int nbRow = nbParams / 3 - 1;
|
||||||
|
grid.addRow(nbRow,
|
||||||
|
labels.get(UserParameter.values()[3 * nbRow]),
|
||||||
|
fields.get(UserParameter.values()[3 * nbRow]),
|
||||||
|
labels.get(UserParameter.values()[3 * nbRow + 1]),
|
||||||
|
fields.get(UserParameter.values()[3 * nbRow + 1]),
|
||||||
|
labels.get(UserParameter.values()[3 * nbRow + 2]),
|
||||||
|
fields.get(UserParameter.values()[3 * nbRow + 2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grid.addColumn(6, makeCursorInfo());
|
||||||
|
ColumnConstraints colL = new ColumnConstraints();
|
||||||
|
colL.setPrefWidth(150);
|
||||||
|
ColumnConstraints colT = new ColumnConstraints();
|
||||||
|
colT.setPrefWidth(80);
|
||||||
|
ColumnConstraints colA = new ColumnConstraints();
|
||||||
|
colA.setPrefWidth(300);
|
||||||
|
grid.getColumnConstraints().setAll(colL, colT, colL, colT, colL, colT,
|
||||||
|
colA);
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a field for the params grid
|
||||||
|
* @param parameter : parameter of the field
|
||||||
|
* @return the field
|
||||||
|
*/
|
||||||
|
private static Node makeField(UserParameter parameter) {
|
||||||
|
if (!parameter.equals(UserParameter.SUPER_SAMPLING_EXPONENT)) {
|
||||||
|
return makeTextField(parameter);
|
||||||
|
} else {
|
||||||
|
return makeSuperSamplingField(parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a text field for the param
|
||||||
|
* @param parameter : parameter to use
|
||||||
|
* @return a text field
|
||||||
|
*/
|
||||||
|
private static Node makeTextField(UserParameter parameter) {
|
||||||
|
TextField field = new TextField();
|
||||||
|
StringConverter<Integer> stringConverter = new FixedPointStringConverter(
|
||||||
|
parameter.scale());
|
||||||
|
TextFormatter<Integer> formatter = new TextFormatter<>(stringConverter);
|
||||||
|
field.setTextFormatter(formatter);
|
||||||
|
field.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
formatter.valueProperty()
|
||||||
|
.bindBidirectional(ppbean.getProperty(parameter));
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a Choice box for the supersampling parameter
|
||||||
|
* @param parameter : parameter
|
||||||
|
* @return a choice box field for the supersampling parameter
|
||||||
|
*/
|
||||||
|
private static Node makeSuperSamplingField(UserParameter parameter) {
|
||||||
|
ChoiceBox<Integer> field = new ChoiceBox<Integer>(
|
||||||
|
FXCollections.observableArrayList(0, 1, 2));
|
||||||
|
field.setConverter(new LabeledListStringConverter("none", "2x", "4x"));
|
||||||
|
field.valueProperty().bindBidirectional(ppbean.getProperty(parameter));
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a cursor info text area
|
||||||
|
* @return a text area holding the cursor info
|
||||||
|
*/
|
||||||
|
private static Node makeCursorInfo() {
|
||||||
|
TextArea cursorInfo = new TextArea();
|
||||||
|
cursorInfo.setWrapText(true);
|
||||||
|
cursorInfo.textProperty().bind(cursor.textProperty());
|
||||||
|
cursorInfo.setMinWidth(100);
|
||||||
|
cursorInfo.setPrefRowCount(2);
|
||||||
|
cursorInfo.setEditable(false);
|
||||||
|
GridPane.setMargin(cursorInfo, new Insets(0, 0, 0, 20));
|
||||||
|
GridPane.setRowSpan(cursorInfo, 3);
|
||||||
|
return cursorInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a pane holding the update notice
|
||||||
|
* @return a pane when clicked updates the pcbean
|
||||||
|
*/
|
||||||
|
private static Node makeUpdateNotice() {
|
||||||
|
Label updateText = new Label(UPDATE_NOTICE_TEXT);
|
||||||
|
StackPane updateNotice = new StackPane(updateText);
|
||||||
|
updateNotice.setOpacity(0.9);
|
||||||
|
updateNotice.setStyle("-fx-background-color: #FFFFFF;");
|
||||||
|
updateNotice.visibleProperty().bind(ppbean.ParametersProperty()
|
||||||
|
.isNotEqualTo(pcbean.parametersProperty()));
|
||||||
|
updateNotice.setOnMouseClicked((MouseEvent event) -> {
|
||||||
|
pcbean.setPainter(ppbean.painterProperty().get());
|
||||||
|
pcbean.setParameters(ppbean.ParametersProperty().get());
|
||||||
|
});
|
||||||
|
return updateNotice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a pane holding the repaint notice
|
||||||
|
* @return a pane when clicked repaints the panorama
|
||||||
|
*/
|
||||||
|
private static Node makePainterNotice() {
|
||||||
|
Label updateText = new Label(REPAINT_NOTICE_TEXT);
|
||||||
|
StackPane updateNotice = new StackPane(updateText);
|
||||||
|
updateNotice.setOpacity(0.9);
|
||||||
|
updateNotice.setStyle("-fx-background-color: #FFFFFF;");
|
||||||
|
updateNotice.visibleProperty()
|
||||||
|
.bind(ppbean.painterProperty()
|
||||||
|
.isNotEqualTo(pcbean.painterProperty())
|
||||||
|
.and(ppbean.ParametersProperty()
|
||||||
|
.isEqualTo(pcbean.parametersProperty())));
|
||||||
|
updateNotice.setOnMouseClicked((MouseEvent event) -> {
|
||||||
|
pcbean.setPainter(ppbean.painterProperty().get());
|
||||||
|
});
|
||||||
|
return updateNotice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a pane holding the download notice
|
||||||
|
* @return a pane dispkaying the MapzenManager download info
|
||||||
|
*/
|
||||||
|
private static Node makeDownloadNotice() {
|
||||||
|
Label dlText = new Label();
|
||||||
|
StackPane dlNotice = new StackPane(dlText);
|
||||||
|
dlNotice.setOpacity(0.9);
|
||||||
|
dlNotice.setStyle("-fx-background-color: #FFFFFF;");
|
||||||
|
dlNotice.visibleProperty().bind(pcbean.downloadProperty());
|
||||||
|
dlText.textProperty().bind(pcbean.downloadTextProperty());
|
||||||
|
dlText.setAlignment(Pos.CENTER);
|
||||||
|
return dlNotice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a ImageView holding the image of the panorama
|
||||||
|
* @return an ImageView with bound to the panorama image
|
||||||
|
*/
|
||||||
|
private static Node makePanoView() {
|
||||||
|
ImageView panoView = new ImageView();
|
||||||
|
panoView.imageProperty().bind(pcbean.imageProperty());
|
||||||
|
panoView.fitWidthProperty().bind(ppbean.WidthProperty());
|
||||||
|
panoView.setPreserveRatio(true);
|
||||||
|
panoView.setSmooth(true);
|
||||||
|
return panoView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a pane holding the labels of the panorama
|
||||||
|
* @return a pane holding the labels
|
||||||
|
*/
|
||||||
|
private static Node makeLabelsPane() {
|
||||||
|
Pane labelsPane = new Pane();
|
||||||
|
labelsPane.prefWidthProperty().bind(ppbean.WidthProperty());
|
||||||
|
labelsPane.prefHeightProperty().bind(ppbean.HeightProperty());
|
||||||
|
Bindings.bindContent(labelsPane.getChildren(), pcbean.getLabels());
|
||||||
|
return labelsPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes an ImageView holding the map image
|
||||||
|
* @return ImageView holding the map image
|
||||||
|
*/
|
||||||
|
private static ImageView makeMapView() {
|
||||||
|
ImageView panoView = new ImageView();
|
||||||
|
|
||||||
|
panoView.imageProperty().bind(mvbean.mapView());
|
||||||
|
panoView.setFitWidth(720);
|
||||||
|
panoView.setPreserveRatio(true);
|
||||||
|
panoView.setSmooth(true);
|
||||||
|
return panoView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the trekking stage
|
||||||
|
* @param ps : stage
|
||||||
|
* @return the trekking stage
|
||||||
|
*/
|
||||||
|
private static Stage makeTrekkingStage(Stage ps) {
|
||||||
|
|
||||||
|
BorderPane p = new BorderPane();
|
||||||
|
|
||||||
|
///////////////////////////////// makeMapPan
|
||||||
|
|
||||||
|
///////////////////////////////// makeMapGroup
|
||||||
|
|
||||||
|
ImageView im = makeMapView();
|
||||||
|
Pane lines = new Pane();
|
||||||
|
lines.setPrefWidth(720);
|
||||||
|
lines.setPrefHeight(720);
|
||||||
|
StackPane panoGroup = new StackPane(im, lines);
|
||||||
|
Bindings.bindContent(lines.getChildren(), mvbean.tl().get());
|
||||||
|
|
||||||
|
panoGroup.setOnMouseClicked((MouseEvent e) -> {
|
||||||
|
switch(e.getClickCount()){
|
||||||
|
case 1:
|
||||||
|
mvbean.addTs(e.getX(),e.getY());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mvbean.zoom(e.getButton());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
Pane panoScrollPane = new Pane(panoGroup);
|
||||||
|
StackPane mapPane = new StackPane(panoScrollPane);
|
||||||
|
////////////////////////////////
|
||||||
|
Button compute = new Button("Compute");
|
||||||
|
compute.setOnAction(e -> makeElevationProfileStage().show());
|
||||||
|
Button clear = new Button("clear");
|
||||||
|
p.setCenter(mapPane);
|
||||||
|
|
||||||
|
Button save = new Button("Save");
|
||||||
|
|
||||||
|
GridPane grid = new GridPane();
|
||||||
|
grid.add(compute, 0, 0);
|
||||||
|
grid.add(clear, (int) compute.getScaleX(), 0);
|
||||||
|
grid.add(save, 50, 0);
|
||||||
|
|
||||||
|
Button path = new Button("path");
|
||||||
|
path.setOnAction(e -> mvbean.buildEndToEndPath());
|
||||||
|
|
||||||
|
grid.add(path, 100, 0);
|
||||||
|
|
||||||
|
p.setBottom(grid);
|
||||||
|
Stage s = new Stage();
|
||||||
|
|
||||||
|
Scene scene = new Scene(p);
|
||||||
|
|
||||||
|
s.setScene(scene);
|
||||||
|
|
||||||
|
clear.setOnAction(e -> mvbean.clearTs());
|
||||||
|
return s;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a view of the composite elevation profile
|
||||||
|
* @return a ImageView holding the elevation profile
|
||||||
|
*/
|
||||||
|
private static Node makeElevationView() {
|
||||||
|
ImageView elevationView = new ImageView();
|
||||||
|
ArrayList<GeoPoint> pts = new ArrayList<GeoPoint>();
|
||||||
|
pts.addAll(mvbean.cts().get());
|
||||||
|
pts.addAll(mvbean.ts().get());
|
||||||
|
|
||||||
|
elevationView.imageProperty()
|
||||||
|
.set(ElevationProfileRenderer.renderElevation(
|
||||||
|
new CompositElevationProfile(pcbean.cDem().get(),pts)));
|
||||||
|
// MapViewRenderer.trekkingProfile(pcbean.cDem())));
|
||||||
|
//
|
||||||
|
elevationView.setFitWidth(700);
|
||||||
|
elevationView.setPreserveRatio(true);
|
||||||
|
elevationView.setSmooth(true);
|
||||||
|
return elevationView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a label holding the elevation profile max height data
|
||||||
|
* @return a label with the max height
|
||||||
|
*/
|
||||||
|
private static Node makeElevationProfileData() {
|
||||||
|
Label t = new Label();
|
||||||
|
Long d = Math.round(ElevationProfileRenderer.currentMaxHeight.get());
|
||||||
|
t.setText(d.toString() + " m");
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a pane holding the bottom information for the elevation profile
|
||||||
|
* @return a pane for the bottom of the elevation profile
|
||||||
|
*/
|
||||||
|
private static Node makeElevationBottomPane() {
|
||||||
|
|
||||||
|
Text t0 = new Text(-100, 0, "0");
|
||||||
|
|
||||||
|
int d = (int)Math.round(
|
||||||
|
ElevationProfileRenderer.currentElevationProfileLength.get());
|
||||||
|
|
||||||
|
GridPane pp = new GridPane();
|
||||||
|
|
||||||
|
StackPane p = new StackPane();
|
||||||
|
p.setPrefWidth(ElevationProfileRenderer.WIDTH);
|
||||||
|
// p.setPrefHeight(ElevationProfileRenderer.HEIGHT);
|
||||||
|
|
||||||
|
Text t1 = new Text(p.widthProperty().get() - 20, 0,
|
||||||
|
new FixedPointStringConverter(3).toString(d)+ " km");
|
||||||
|
pp.add(t0, 0, 0);
|
||||||
|
pp.add(t1, 1, 0);
|
||||||
|
ColumnConstraints colN = new ColumnConstraints();
|
||||||
|
colN.setMinWidth(700);
|
||||||
|
ColumnConstraints colL = new ColumnConstraints();
|
||||||
|
colL.setPrefWidth(700);
|
||||||
|
pp.getColumnConstraints().setAll(colN, colL);
|
||||||
|
p.getChildren().add(pp);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a pane holding the elevation profile group
|
||||||
|
* @return a stack pane with the elevation group
|
||||||
|
*/
|
||||||
|
private static Node makeElevationPane() {
|
||||||
|
|
||||||
|
Pane scrollPane = new Pane(makeElevationGroup());
|
||||||
|
return new StackPane(scrollPane);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a window holding the elevation profile stuff
|
||||||
|
* @return a stage for the elevation profile
|
||||||
|
*/
|
||||||
|
private static Stage makeElevationProfileStage() {
|
||||||
|
BorderPane p = new BorderPane();
|
||||||
|
p.setCenter(makeElevationPane());
|
||||||
|
p.setLeft(makeElevationProfileData());
|
||||||
|
p.setBottom(makeElevationBottomPane());
|
||||||
|
Stage s = new Stage();
|
||||||
|
|
||||||
|
Scene scene = new Scene(p);
|
||||||
|
|
||||||
|
s.setScene(scene);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a pane holding the elevation profile group
|
||||||
|
* @return a stack pane for the elevation profile
|
||||||
|
*/
|
||||||
|
private static Node makeElevationGroup() {
|
||||||
|
|
||||||
|
StackPane panoGroup = new StackPane(makeElevationView());
|
||||||
|
return panoGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a pane holding the panorama group
|
||||||
|
* @return a stack pane for the panorama group
|
||||||
|
*/
|
||||||
|
private static Node makePanoGroup() {
|
||||||
|
|
||||||
|
StackPane panoGroup = new StackPane(makePanoView(), makeLabelsPane());
|
||||||
|
panoGroup.setOnMouseMoved((MouseEvent e) -> {
|
||||||
|
int x = (int) e.getX();
|
||||||
|
int y = (int) e.getY();
|
||||||
|
cursor.update(x, y);
|
||||||
|
});
|
||||||
|
panoGroup.setOnMouseClicked((MouseEvent e) -> {
|
||||||
|
String qy = String.format((Locale) null, "mlat=%.4f&mlon=%.4f",
|
||||||
|
toDegrees(cursor.lat()), toDegrees(cursor.lon()));// ;
|
||||||
|
String fg = String.format((Locale) null, "map=15/%.4f/%.4f",
|
||||||
|
toDegrees(cursor.lat()), toDegrees(cursor.lon()));
|
||||||
|
URI osmURI;
|
||||||
|
try {
|
||||||
|
osmURI = new URI("http", "www.openstreetmap.org", "/", qy, fg);
|
||||||
|
getDesktop().browse(osmURI);
|
||||||
|
} catch (IOException e1) {
|
||||||
|
// Do nothing
|
||||||
|
} catch (URISyntaxException e1) {
|
||||||
|
// Do Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return panoGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu Button used for the trecking profile
|
||||||
|
* @return a button opening the mapview window
|
||||||
|
*/
|
||||||
|
private static Menu trekkingProfile() {
|
||||||
|
|
||||||
|
Label settingsL = new Label("Trekking Profile");
|
||||||
|
settingsL.setOnMouseClicked(e -> {
|
||||||
|
makeTrekkingStage(null).show();
|
||||||
|
});
|
||||||
|
Menu settings = new Menu();
|
||||||
|
settings.setGraphic(settingsL);
|
||||||
|
settings.setDisable(true);
|
||||||
|
pcbean.cDem().addListener(e -> settings.setDisable(false));
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the menu top bar
|
||||||
|
* @return the menu bar
|
||||||
|
*/
|
||||||
|
private static MenuBar makeMenu() {
|
||||||
|
MenuBar menu = new MenuBar();
|
||||||
|
menu.getMenus().addAll(saveMenu(), predefinedPanoramaMenu(),
|
||||||
|
painterMenu(), trekkingProfile(), quitMenu());
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button that saves the current panorama without labels
|
||||||
|
* @return a button that saves the panorama
|
||||||
|
*/
|
||||||
|
private static Menu saveMenu() {
|
||||||
|
Label saveL = new Label("Save");
|
||||||
|
saveL.setOnMouseClicked(e -> {
|
||||||
|
if (pcbean.getImage() != null) {
|
||||||
|
String name = Integer
|
||||||
|
.toString(pcbean.getParameters().hashCode()) + ".png";
|
||||||
|
try {
|
||||||
|
ImageIO.write(
|
||||||
|
SwingFXUtils.fromFXImage(pcbean.getImage(), null),
|
||||||
|
"png", new File(name));
|
||||||
|
} catch (IOException e1) {
|
||||||
|
e1.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Menu save = new Menu();
|
||||||
|
save.setGraphic(saveL);
|
||||||
|
return save;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu holding all the predefined Panoramas
|
||||||
|
* @return a menu with all predefined panoramas
|
||||||
|
*/
|
||||||
|
private static Menu predefinedPanoramaMenu() {
|
||||||
|
Menu ppMenu = new Menu("Predefined Panorama");
|
||||||
|
for (PredefinedPanoramas pp : PredefinedPanoramas.values()) {
|
||||||
|
MenuItem ppItem = new MenuItem(pp.toString());
|
||||||
|
ppItem.setOnAction(e -> {
|
||||||
|
ppbean.updateProperties(pp.get());
|
||||||
|
});
|
||||||
|
ppMenu.getItems().add(ppItem);
|
||||||
|
}
|
||||||
|
return ppMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu holding all custom painters
|
||||||
|
* @return a menu with all custom painters
|
||||||
|
*/
|
||||||
|
private static Menu painterMenu() {
|
||||||
|
Menu cpMenu = new Menu("Painter");
|
||||||
|
Menu gradItem = new Menu("Gradient Painter");
|
||||||
|
cpMenu.getItems().add(gradItem);
|
||||||
|
ToggleGroup toggleGroup = new ToggleGroup();
|
||||||
|
for (CustomPainters cp : CustomPainters.values()) {
|
||||||
|
|
||||||
|
RadioMenuItem cpItem = new RadioMenuItem(cp.displayText());
|
||||||
|
cpItem.setToggleGroup(toggleGroup);
|
||||||
|
|
||||||
|
cpItem.setOnAction(e -> {
|
||||||
|
ppbean.setPainter(cp);
|
||||||
|
});
|
||||||
|
if (pcbean.painterProperty().equals(cp))
|
||||||
|
cpItem.setSelected(true);
|
||||||
|
|
||||||
|
if (cp.isGradient()) {
|
||||||
|
Canvas cv = new Canvas(20, 20);
|
||||||
|
GraphicsContext gc = cv.getGraphicsContext2D();
|
||||||
|
gc.setFill(cp.gradient().get());
|
||||||
|
gc.fillRect(0, 0, 20, 20);
|
||||||
|
cv.prefHeight(20);
|
||||||
|
cv.setRotate(180);
|
||||||
|
cpItem.setGraphic(cv);
|
||||||
|
|
||||||
|
gradItem.getItems().add(cpItem);
|
||||||
|
} else {
|
||||||
|
cpMenu.getItems().add(cpItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cpMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quit button that closes the program
|
||||||
|
* @return a quit button
|
||||||
|
*/
|
||||||
|
private static Menu quitMenu() {
|
||||||
|
Label quitL = new Label("Quit");
|
||||||
|
quitL.setOnMouseClicked(e -> Platform.exit());
|
||||||
|
Menu quit = new Menu();
|
||||||
|
quit.setGraphic(quitL);
|
||||||
|
return quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to manage the cursor on the panorama pane
|
||||||
|
*/
|
||||||
|
private static final class CursorInfo {
|
||||||
|
private ObjectProperty<String> textProperty = new SimpleObjectProperty<String>();
|
||||||
|
private double lon, lat, dist = Double.POSITIVE_INFINITY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the x,y position of the cursor
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
*/
|
||||||
|
public void update(int x, int y) {
|
||||||
|
int ss = pcbean.getParameters().superSamplingExponent();
|
||||||
|
x *= Math.pow(2, ss);
|
||||||
|
y *= Math.pow(2, ss);
|
||||||
|
if (isValid(x, y)) {
|
||||||
|
lat = pcbean.getPanorama().latitudeAt(x, y);
|
||||||
|
lon = pcbean.getPanorama().longitudeAt(x, y);
|
||||||
|
dist = pcbean.getPanorama().distanceAt(x, y);
|
||||||
|
double alt = pcbean.getPanorama().elevationAt(x, y);
|
||||||
|
GeoPoint point = new GeoPoint(lon, lat);
|
||||||
|
double azi = pcbean.getParameters().panoramaDisplayParameters()
|
||||||
|
.observerPosition().azimuthTo(point);
|
||||||
|
double slope = (alt
|
||||||
|
- pcbean.getParameters().panoramaDisplayParameters()
|
||||||
|
.observerElevation()
|
||||||
|
- (sq(dist) * NATURAL_VARIATION)) / dist;
|
||||||
|
double ele = toDegrees(atan(slope));
|
||||||
|
String slat = lat < 0 ? "S" : "N";
|
||||||
|
String slon = lon < 0 ? "W" : "E";
|
||||||
|
textProperty.set(String.format((Locale) null,
|
||||||
|
"Position : %.4f°%s %.4f°%s \nDistance : %.1f km \nAltitude : %d m \nAzimut : %.1f° (%s) Elévation : %.1f°",
|
||||||
|
abs(toDegrees(lat)), slat, abs(toDegrees(lon)), slon,
|
||||||
|
(dist / 1_000.0), (int) alt, toDegrees(azi),
|
||||||
|
Azimuth.toOctantString(azi, "N", "S", "E", "W"), ele));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the textProperty object to display the cursor information
|
||||||
|
*/
|
||||||
|
public ObjectProperty<String> textProperty() {
|
||||||
|
return textProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the longitude at the cursors position
|
||||||
|
*
|
||||||
|
* @return longitude at cursors last valid position
|
||||||
|
*/
|
||||||
|
public double lon() {
|
||||||
|
return lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the latitude at the cursors position
|
||||||
|
*
|
||||||
|
* @return latitude at cursors last valid position
|
||||||
|
*/
|
||||||
|
public double lat() {
|
||||||
|
return lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if the x,y index is valid (not too far away)
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : x index
|
||||||
|
* @param y
|
||||||
|
* : y index
|
||||||
|
* @return if the position at x,y is valid
|
||||||
|
*/
|
||||||
|
public boolean isValid(int x, int y) {
|
||||||
|
return pcbean.getPanorama() != null
|
||||||
|
&& pcbean.getPanorama().distanceAt(x, y,
|
||||||
|
(float) Double.POSITIVE_INFINITY) < Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
114
Alpano/src/ch/epfl/alpano/gui/ChannelPainter.java
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.Math2;
|
||||||
|
import ch.epfl.alpano.Panorama;
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChannelPainter Interface
|
||||||
|
*/
|
||||||
|
public interface ChannelPainter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value at (x,y)
|
||||||
|
* @param x : x index
|
||||||
|
* @param y : y index
|
||||||
|
* @return value at the point (x,y)
|
||||||
|
*/
|
||||||
|
public float valueAt(int x, int y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the max distance the the neighbors
|
||||||
|
* @param p : panorama to use
|
||||||
|
* @return max distance for a given (x,y)
|
||||||
|
*/
|
||||||
|
public static ChannelPainter maxDistanceToNeighbors(Panorama p){
|
||||||
|
return (x,y)->{
|
||||||
|
float dx1 = p.distanceAt(x-1, y,0);
|
||||||
|
float dx2 = p.distanceAt(x+1, y,0);
|
||||||
|
float dy1 = p.distanceAt(x, y-1,0);
|
||||||
|
float dy2 = p.distanceAt(x, y+1,0);
|
||||||
|
return max(max(dx1,dx2),max(dy1,dy2))-p.distanceAt(x, y);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a value to the current value of a given (x,y)
|
||||||
|
* @param val : value to be added
|
||||||
|
* @return value at (x,y) with added value
|
||||||
|
*/
|
||||||
|
public default ChannelPainter add(double val){
|
||||||
|
return (x,y)->(float)(this.valueAt(x, y)+val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtracts a value to the current value of a given (x,y)
|
||||||
|
* @param val : value to be subtracted
|
||||||
|
* @return value at (x,y) with subtracted value
|
||||||
|
*/
|
||||||
|
public default ChannelPainter sub(double val){
|
||||||
|
return (x,y)->(float)(this.valueAt(x, y)-val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies by a value to the current value of a given (x,y)
|
||||||
|
* @param val : value to be multiplied by
|
||||||
|
* @return value at (x,y) multiplied by value
|
||||||
|
*/
|
||||||
|
public default ChannelPainter mul(double val){
|
||||||
|
return (x,y)->(float)(this.valueAt(x, y)*val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divides by a value to the current value of a given (x,y)
|
||||||
|
* @param val : value to be divided by
|
||||||
|
* @return value at (x,y) divided by value
|
||||||
|
*/
|
||||||
|
public default ChannelPainter div(double val){
|
||||||
|
return (x,y)->(float)(this.valueAt(x, y)/val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a function to the value of a given(x,y)
|
||||||
|
* @param f : the DoubleUnaryOperator function to be used
|
||||||
|
* @return value at (x,y) passed through a function
|
||||||
|
*/
|
||||||
|
public default ChannelPainter map(DoubleUnaryOperator f){
|
||||||
|
return (x,y)->(float) (f.applyAsDouble(this.valueAt(x, y)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inverts the value of a given (x,y)
|
||||||
|
* @return value at (x,y) inverted
|
||||||
|
*/
|
||||||
|
public default ChannelPainter inverted(){
|
||||||
|
return (x,y)->(float) (1-this.valueAt(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clamps the value of a given (x,y)
|
||||||
|
* @return value at (x,y) clamped
|
||||||
|
*/
|
||||||
|
public default ChannelPainter clamped(){
|
||||||
|
return (x,y)->(float) (max(0,min(this.valueAt(x, y),1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cycles the value of a given (x,y)
|
||||||
|
* @return value at (x,y) cycled
|
||||||
|
*/
|
||||||
|
public default ChannelPainter cycling(){
|
||||||
|
return (x,y)->(float) (Math2.floorMod((this.valueAt(x, y)),1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
432
Alpano/src/ch/epfl/alpano/gui/CustomPainters.java
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
import ch.epfl.alpano.Panorama;
|
||||||
|
import javafx.scene.canvas.Canvas;
|
||||||
|
import javafx.scene.canvas.GraphicsContext;
|
||||||
|
import javafx.scene.image.PixelReader;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.paint.CycleMethod;
|
||||||
|
import javafx.scene.paint.LinearGradient;
|
||||||
|
import javafx.scene.paint.Stop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum of custom Painters
|
||||||
|
*/
|
||||||
|
public enum CustomPainters implements Painters{
|
||||||
|
|
||||||
|
DEFAULT("Default", p->Painters.DefaultPainter(p) ),
|
||||||
|
BORDERWB("BW Borders", p->Painters.OutlinePainter(p)),
|
||||||
|
GRADDEF(Gradient.DEFAULT),
|
||||||
|
GRADDEMPOST(Gradient.DEMPOSTER),
|
||||||
|
GRADMAYERS(Gradient.MEYERS),
|
||||||
|
GRADNORDISK(Gradient.NORDISK),
|
||||||
|
GRADWIKI(Gradient.WIKI);
|
||||||
|
|
||||||
|
|
||||||
|
private final String displayText;
|
||||||
|
private final Painters imp;
|
||||||
|
private final Gradient gradient;
|
||||||
|
private final GradientImage gradientIm;
|
||||||
|
|
||||||
|
private CustomPainters(String name, Painters imp){
|
||||||
|
this.displayText = name;
|
||||||
|
this.imp = imp;
|
||||||
|
this.gradient=null;
|
||||||
|
this.gradientIm=null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CustomPainters(Gradient grad){
|
||||||
|
this.displayText = "Gradient "+grad.displayText();
|
||||||
|
this.imp = p->Painters.GradientPainter(p,grad);
|
||||||
|
this.gradient=grad;
|
||||||
|
this.gradientIm = new GradientImage(grad.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the display text of the custom painter
|
||||||
|
*/
|
||||||
|
public String displayText(){
|
||||||
|
return displayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImagePainter painter(Panorama p) {
|
||||||
|
return imp.painter(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms elevation and slope to Color
|
||||||
|
* @param alt : altitude
|
||||||
|
* @param slope : slope
|
||||||
|
* @return Color
|
||||||
|
*/
|
||||||
|
public Color elevationToColor(double alt, double slope) {
|
||||||
|
if(isGradient()){
|
||||||
|
|
||||||
|
double a = Math.max(0,Math.min((alt-10.0)/10_000.0,1));
|
||||||
|
float hue = Painters.AltitudeToHue(gradientIm, a);
|
||||||
|
float sat = Painters.AltitudeToSaturation(gradientIm, a);
|
||||||
|
float bri = Painters.SlopeToBrightness(slope);
|
||||||
|
return Color.hsb(hue, sat, bri);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return true if gradient painter
|
||||||
|
*/
|
||||||
|
public boolean isGradient() {
|
||||||
|
return gradient!=null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return Gradient if gradient painter, else returns null
|
||||||
|
*/
|
||||||
|
public Gradient gradient(){
|
||||||
|
return gradient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional Painters interface
|
||||||
|
*/
|
||||||
|
interface Painters{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximal Height
|
||||||
|
*/
|
||||||
|
final static double HMAX = 10_000.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract function used by the enum to return an image painter
|
||||||
|
* @param p : the panorama
|
||||||
|
* @return an ImagePainter
|
||||||
|
*/
|
||||||
|
public abstract ImagePainter painter(Panorama p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default painter as given by the teacher
|
||||||
|
* @param p : panorama
|
||||||
|
* @return default ImagePainter
|
||||||
|
*/
|
||||||
|
public static ImagePainter DefaultPainter(Panorama p){
|
||||||
|
ChannelPainter d = p::distanceAt;
|
||||||
|
ChannelPainter sl = p::slopeAt;
|
||||||
|
ChannelPainter h = d.div(100000).cycling().mul(360);
|
||||||
|
ChannelPainter s = d.div(200000).clamped().inverted();
|
||||||
|
ChannelPainter b = sl.mul(2).div(Math.PI).inverted().mul(0.7).add(0.3);
|
||||||
|
ChannelPainter o =
|
||||||
|
d.map(dist -> dist == Float.POSITIVE_INFINITY ? 0 : 1);
|
||||||
|
|
||||||
|
return ImagePainter.hsb(h, s, b, o);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outline painter as given by the teacher
|
||||||
|
* Paints the borders in black/grey
|
||||||
|
* @param p : panorama
|
||||||
|
* @return outline ImagePainter
|
||||||
|
*/
|
||||||
|
public static ImagePainter OutlinePainter(Panorama p){
|
||||||
|
ChannelPainter gray =
|
||||||
|
ChannelPainter.maxDistanceToNeighbors(p)
|
||||||
|
.sub(500)
|
||||||
|
.div(4500)
|
||||||
|
.clamped()
|
||||||
|
.inverted();
|
||||||
|
|
||||||
|
ChannelPainter distance = p::distanceAt;
|
||||||
|
ChannelPainter opacity =
|
||||||
|
distance.map(d -> d == Float.POSITIVE_INFINITY ? 0 : 1);
|
||||||
|
|
||||||
|
return ImagePainter.gray(gray, opacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gradient painter, paints using a Gradient
|
||||||
|
* @param p : panorama
|
||||||
|
* @param lgrad : Gradient to use
|
||||||
|
* @return Gradient ImagePainter
|
||||||
|
*/
|
||||||
|
public static ImagePainter GradientPainter(Panorama p, Gradient lgrad){
|
||||||
|
|
||||||
|
GradientImage grad = new GradientImage(lgrad.get());
|
||||||
|
|
||||||
|
ChannelPainter alt = p::elevationAt;
|
||||||
|
|
||||||
|
ChannelPainter d = p::distanceAt;
|
||||||
|
ChannelPainter sl = p::slopeAt;
|
||||||
|
ChannelPainter h = alt.sub(10).div(HMAX).clamped().map(a->{
|
||||||
|
return AltitudeToHue(grad, a);
|
||||||
|
});
|
||||||
|
ChannelPainter s = alt.sub(10).div(HMAX).clamped().map(a->{
|
||||||
|
return AltitudeToSaturation(grad,a);
|
||||||
|
});
|
||||||
|
ChannelPainter b = sl.mul(2).div(Math.PI).inverted().mul(0.7).add(0.3);
|
||||||
|
ChannelPainter o =
|
||||||
|
d.map(dist -> dist == Float.POSITIVE_INFINITY ? 0 : 1);
|
||||||
|
|
||||||
|
return ImagePainter.hsb(h, s, b, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms slope to brightness
|
||||||
|
* @param slope
|
||||||
|
* @return 0-1 brightness value
|
||||||
|
*/
|
||||||
|
static float SlopeToBrightness(double slope){
|
||||||
|
return (float) ((1-(slope*2/Math.PI))*0.7+0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms altitude to saturation
|
||||||
|
* @param grad : GradientImage
|
||||||
|
* @param a : altitude 0-1 value
|
||||||
|
* @return 0-1 saturation value
|
||||||
|
*/
|
||||||
|
static float AltitudeToSaturation(GradientImage grad, double a){
|
||||||
|
float r = (float)(grad.RedAt(a));
|
||||||
|
float g = (float)(grad.GreenAt(a));
|
||||||
|
float b = (float)(grad.BlueAt(a));
|
||||||
|
float cmin = min(min(r,g),b);
|
||||||
|
float cmax = max(max(r,g),b);
|
||||||
|
if(cmax-cmin==0)
|
||||||
|
return 0;
|
||||||
|
else return (cmax-cmin)/(1-Math.abs(cmax+cmin-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms altitude to hue
|
||||||
|
* @param grad : GradientImage
|
||||||
|
* @param a : altitude 0-1 value
|
||||||
|
* @return 0-1 hue value
|
||||||
|
*/
|
||||||
|
static float AltitudeToHue(GradientImage grad, double a){
|
||||||
|
float r = (float)(grad.RedAt(a));
|
||||||
|
float g = (float)(grad.GreenAt(a));
|
||||||
|
float b = (float)(grad.BlueAt(a));
|
||||||
|
float cmin = min(min(r,g),b);
|
||||||
|
float cmax = max(max(r,g),b);
|
||||||
|
float hu = 60f;
|
||||||
|
|
||||||
|
if(cmin==cmax){
|
||||||
|
hu*=0f;
|
||||||
|
}else{
|
||||||
|
if(cmax==r)
|
||||||
|
hu*= (g-b)/(cmax-cmin);
|
||||||
|
else if(cmax==g)
|
||||||
|
hu*= 2f+ ((b-r)/(cmax-cmin));
|
||||||
|
else
|
||||||
|
hu*= 4f+ ((r-g)/(cmax-cmin));
|
||||||
|
if(hu<0)
|
||||||
|
hu+=360;
|
||||||
|
}
|
||||||
|
return Math.round(hu);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to get values from a LinearGardient
|
||||||
|
*/
|
||||||
|
class GradientImage{
|
||||||
|
private final PixelReader ir;
|
||||||
|
/**
|
||||||
|
* Number of steps on the gradient
|
||||||
|
*/
|
||||||
|
private final static int SIZE = 1_000;
|
||||||
|
private final Canvas cv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a gradient image
|
||||||
|
* Creates a snapshot of a canvas containing the gradient
|
||||||
|
* and saves the pixel reader to that image, to read the values
|
||||||
|
* from it when we need them.
|
||||||
|
* @param grad : LinearGradient
|
||||||
|
*/
|
||||||
|
public GradientImage(LinearGradient grad){
|
||||||
|
cv = new Canvas(1,SIZE);
|
||||||
|
GraphicsContext gc = cv.getGraphicsContext2D();
|
||||||
|
|
||||||
|
gc.setFill(grad);
|
||||||
|
gc.fillRect(0, 0, 1, SIZE);
|
||||||
|
ir = cv.snapshot(null, null).getPixelReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the color on the image at a coordinate x
|
||||||
|
* @param x : x coordinate
|
||||||
|
* @return Color at x
|
||||||
|
*/
|
||||||
|
public Color colorAt(double x){
|
||||||
|
return ir.getColor(0,(int)(x*SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Red value at x
|
||||||
|
* @param x : x coordinate
|
||||||
|
* @return red value
|
||||||
|
*/
|
||||||
|
public double RedAt(double x){
|
||||||
|
return ir.getColor(0,(int)(x*SIZE)).getRed();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Gets the Green value at x
|
||||||
|
* @param x : x coordinate
|
||||||
|
* @return green value
|
||||||
|
*/
|
||||||
|
public double GreenAt(double x){
|
||||||
|
return ir.getColor(0,(int)(x*SIZE)).getGreen();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Gets the blue value at x
|
||||||
|
* @param x : x coordinate
|
||||||
|
* @return blue value
|
||||||
|
*/
|
||||||
|
public double BlueAt(double x){
|
||||||
|
return ir.getColor(0,(int)(x*SIZE)).getBlue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the canvas containing the gradient
|
||||||
|
* @return canvas
|
||||||
|
*/
|
||||||
|
public Canvas canvas(){
|
||||||
|
return cv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum defining various LinearGradients
|
||||||
|
*/
|
||||||
|
enum Gradient{
|
||||||
|
|
||||||
|
DEFAULT("DEM 1",new LinearGradient(0,0,0,1,true,CycleMethod.NO_CYCLE,
|
||||||
|
new Stop(0.0,Color.rgb(105, 152, 133)),
|
||||||
|
new Stop(0.005,Color.rgb(118, 169, 146)),
|
||||||
|
new Stop(0.02,Color.rgb(131, 181, 155)),
|
||||||
|
new Stop(0.06,Color.rgb(165, 192, 167)),
|
||||||
|
new Stop(0.1,Color.rgb(211, 201, 179)),
|
||||||
|
new Stop(0.2,Color.rgb(212, 184, 164)),
|
||||||
|
new Stop(0.3,Color.rgb(212, 192, 181)),
|
||||||
|
new Stop(0.4,Color.rgb(214, 209, 206)),
|
||||||
|
new Stop(0.5,Color.rgb(222, 221, 222)),
|
||||||
|
new Stop(0.6,Color.rgb(238, 238, 238)),
|
||||||
|
new Stop(0.7,Color.rgb(245, 245, 245)),
|
||||||
|
new Stop(1.0,Color.rgb(245, 245, 245))
|
||||||
|
)),
|
||||||
|
DEM6B("DEM 2",new LinearGradient(0,0,0,1,true,CycleMethod.NO_CYCLE,
|
||||||
|
new Stop(0.0,Color.rgb(51, 102, 0)),
|
||||||
|
new Stop(0.1,Color.rgb(129, 195, 31)),
|
||||||
|
new Stop(0.2,Color.rgb(255, 255, 204)),
|
||||||
|
new Stop(0.4,Color.rgb(244, 189, 69)),
|
||||||
|
new Stop(0.5,Color.rgb(102, 51, 12)),
|
||||||
|
new Stop(0.6,Color.rgb(102, 51, 0)),
|
||||||
|
new Stop(0.8,Color.rgb(245, 245, 245)),
|
||||||
|
new Stop(1.0,Color.rgb(245, 245, 245))
|
||||||
|
|
||||||
|
)),
|
||||||
|
DEMPOSTER("DEM Poster",new LinearGradient(0,0,0,1,true,CycleMethod.NO_CYCLE,
|
||||||
|
new Stop(0.0,Color.rgb(0, 97, 71)),
|
||||||
|
new Stop(0.05,Color.rgb(16, 122, 47)),
|
||||||
|
new Stop(0.1,Color.rgb(232, 215, 125)),
|
||||||
|
new Stop(0.17,Color.rgb(161, 67, 0)),
|
||||||
|
new Stop(0.21,Color.rgb(158, 0, 0)),
|
||||||
|
new Stop(0.28,Color.rgb(110, 110, 110)),
|
||||||
|
new Stop(0.4,Color.rgb(245, 245, 245)),
|
||||||
|
new Stop(1.0,Color.rgb(245, 245, 245))
|
||||||
|
)),
|
||||||
|
MEYERS("Mayers Encyclopedia",new LinearGradient(0,0,0,1,true,CycleMethod.NO_CYCLE,
|
||||||
|
new Stop(0.0,Color.rgb(200,219, 225)),
|
||||||
|
new Stop(0.001,Color.rgb(178, 202, 153)),
|
||||||
|
new Stop(0.02,Color.rgb(178, 202, 153)),
|
||||||
|
new Stop(0.02,Color.rgb(215, 224, 199)),
|
||||||
|
new Stop(0.05,Color.rgb(215, 224, 199)),
|
||||||
|
new Stop(0.05,Color.rgb(208, 195, 180)),
|
||||||
|
new Stop(0.1,Color.rgb(208, 195, 180)),
|
||||||
|
new Stop(0.1,Color.rgb(155, 115, 93)),
|
||||||
|
new Stop(0.2,Color.rgb(155, 115, 93)),
|
||||||
|
new Stop(0.2,Color.rgb(100, 53, 32)),
|
||||||
|
new Stop(0.3,Color.rgb(100, 53, 32)),
|
||||||
|
new Stop(0.3,Color.rgb(43, 10, 11)),
|
||||||
|
new Stop(0.4,Color.rgb(43, 10, 11)),
|
||||||
|
new Stop(1.0,Color.rgb(245, 245, 245))
|
||||||
|
)),
|
||||||
|
NORDISK("Nordisk Encyclopedia",new LinearGradient(0,0,0,1,true,CycleMethod.NO_CYCLE,
|
||||||
|
new Stop(0.0,Color.rgb(200,219, 225)),
|
||||||
|
new Stop(0.001,Color.rgb(188, 187, 128)),
|
||||||
|
new Stop(0.02,Color.rgb(188, 187, 128)),
|
||||||
|
new Stop(0.02,Color.rgb(207, 199, 153)),
|
||||||
|
new Stop(0.05,Color.rgb(207, 199, 153)),
|
||||||
|
new Stop(0.05,Color.rgb(227, 194, 165)),
|
||||||
|
new Stop(0.1,Color.rgb(227, 194, 165)),
|
||||||
|
new Stop(0.1,Color.rgb(215, 158, 120)),
|
||||||
|
new Stop(0.2,Color.rgb(215, 158, 120)),
|
||||||
|
new Stop(0.2,Color.rgb(213, 130, 85)),
|
||||||
|
new Stop(0.3,Color.rgb(213, 130, 85)),
|
||||||
|
new Stop(0.3,Color.rgb(93, 42, 19)),
|
||||||
|
new Stop(0.4,Color.rgb(93, 42, 19)),
|
||||||
|
new Stop(1.0,Color.rgb(245, 245, 245))
|
||||||
|
)),
|
||||||
|
WIKI("Wikipedia Style",new LinearGradient(0,0,0,1,true,CycleMethod.NO_CYCLE,
|
||||||
|
new Stop(0.0,Color.rgb(216,242, 254)),
|
||||||
|
new Stop(0.001,Color.rgb(172, 208, 165)),
|
||||||
|
new Stop(0.01,Color.rgb(148, 191, 139)),
|
||||||
|
new Stop(0.05,Color.rgb(168, 198, 143)),
|
||||||
|
new Stop(0.1,Color.rgb(189, 204, 150)),
|
||||||
|
new Stop(0.15,Color.rgb(209, 215, 171)),
|
||||||
|
new Stop(0.17,Color.rgb(225, 228, 181)),
|
||||||
|
new Stop(0.2,Color.rgb(239, 235, 192)),
|
||||||
|
new Stop(0.22,Color.rgb(232, 225, 182)),
|
||||||
|
new Stop(0.24,Color.rgb(222, 214, 163)),
|
||||||
|
new Stop(0.26,Color.rgb(211, 202, 130)),
|
||||||
|
new Stop(0.28,Color.rgb(195, 167, 107)),
|
||||||
|
new Stop(0.3,Color.rgb(185, 152, 90)),
|
||||||
|
new Stop(0.35,Color.rgb(170, 135, 83)),
|
||||||
|
new Stop(0.4,Color.rgb(172, 154, 124)),
|
||||||
|
new Stop(0.45,Color.rgb(186, 174, 154)),
|
||||||
|
new Stop(0.5,Color.rgb(202, 195, 184)),
|
||||||
|
new Stop(0.55,Color.rgb(224, 222, 216)),
|
||||||
|
new Stop(0.8,Color.rgb(245, 244, 242)),
|
||||||
|
new Stop(1.0,Color.rgb(245, 245, 245))
|
||||||
|
)),;
|
||||||
|
|
||||||
|
private final String displayText;
|
||||||
|
private final LinearGradient grad;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of the enum
|
||||||
|
* @param name : name of the Gradient
|
||||||
|
* @param grad : LinearGradient
|
||||||
|
*/
|
||||||
|
private Gradient(String name,LinearGradient grad){
|
||||||
|
this.displayText = name;
|
||||||
|
this.grad = grad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the display text of the Gradient
|
||||||
|
*/
|
||||||
|
public String displayText(){
|
||||||
|
return displayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the LinearGradient of the Gradient
|
||||||
|
*/
|
||||||
|
public LinearGradient get(){
|
||||||
|
return grad;
|
||||||
|
}
|
||||||
|
}
|
89
Alpano/src/ch/epfl/alpano/gui/ElevationProfileRenderer.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.dem.CompositElevationProfile;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.scene.image.PixelWriter;
|
||||||
|
import javafx.scene.image.WritableImage;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used to render an elevation profile
|
||||||
|
*/
|
||||||
|
public interface ElevationProfileRenderer {
|
||||||
|
|
||||||
|
final static double MAX_ELEVATION = 1_500;
|
||||||
|
|
||||||
|
final static ObjectProperty<Double> currentMaxHeight = new SimpleObjectProperty<Double>();
|
||||||
|
final static ObjectProperty<Double> currentElevationProfileLength = new SimpleObjectProperty<Double>();
|
||||||
|
|
||||||
|
final static int WIDTH = 800, HEIGHT = 100;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the maximum elevation of a elevationProfile to
|
||||||
|
* dynamically set the max y pixel height
|
||||||
|
* @param evP
|
||||||
|
* @return max height
|
||||||
|
*/
|
||||||
|
public static double maxElevation(CompositElevationProfile evP){
|
||||||
|
|
||||||
|
double max = Integer.MIN_VALUE;
|
||||||
|
for (int x = 0; x < WIDTH; ++x) {
|
||||||
|
double pX = x * (double) evP.length() / (WIDTH - 1);
|
||||||
|
double pY = evP.elevationAt(pX);
|
||||||
|
if (max<pY){
|
||||||
|
max=pY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* builds a black and white image of an elevation profile
|
||||||
|
* @param evP
|
||||||
|
* @return Writable Image
|
||||||
|
*/
|
||||||
|
public static WritableImage renderElevation(CompositElevationProfile evP){
|
||||||
|
|
||||||
|
Color BLACK = Color.BLACK, WHITE = Color.WHITE;
|
||||||
|
|
||||||
|
WritableImage i =
|
||||||
|
new WritableImage(WIDTH, HEIGHT);
|
||||||
|
double maxEl = maxElevation(evP);
|
||||||
|
|
||||||
|
currentMaxHeight.set(maxEl);
|
||||||
|
currentElevationProfileLength.set(evP.length());
|
||||||
|
|
||||||
|
|
||||||
|
PixelWriter w = i.getPixelWriter();
|
||||||
|
for (int x = 0; x < WIDTH; ++x) {
|
||||||
|
double pX = x * (double) evP.length() / (WIDTH - 1);
|
||||||
|
|
||||||
|
double pY = evP.elevationAt(pX);
|
||||||
|
int yL = (int)((pY / maxEl) * (HEIGHT - 1));
|
||||||
|
for (int y = 0; y < HEIGHT; ++y) {
|
||||||
|
Color color = y < yL && y>yL-5 ? BLACK : WHITE;
|
||||||
|
w.setColor(x, HEIGHT - 1 - y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("EVP Effort : "+effort(evP));
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double effort(CompositElevationProfile evP){
|
||||||
|
double effort= evP.length();
|
||||||
|
for (int x = 0; x < evP.length()-1; ++x) {
|
||||||
|
effort+= 9.81*Math.abs(evP.elevationAt(x+1)-evP.elevationAt(x));
|
||||||
|
}
|
||||||
|
return effort;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
53
Alpano/src/ch/epfl/alpano/gui/FixedPointStringConverter.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
import static java.math.BigDecimal.ROUND_HALF_UP;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to convert an integer to a string and a string to an integer.
|
||||||
|
* Allowing us to also set the number of decimal values (defines where to put
|
||||||
|
* the comma)
|
||||||
|
*/
|
||||||
|
public class FixedPointStringConverter extends StringConverter<Integer> {
|
||||||
|
private final int decimalValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a converter.
|
||||||
|
*
|
||||||
|
* @param decimalValues
|
||||||
|
*/
|
||||||
|
public FixedPointStringConverter(int decimalValues) {
|
||||||
|
this.decimalValues = decimalValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer fromString(String string) {
|
||||||
|
|
||||||
|
if (string.length() == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BigDecimal(string)
|
||||||
|
.setScale(this.decimalValues, ROUND_HALF_UP)
|
||||||
|
.movePointRight(this.decimalValues).intValue();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(Integer object) {
|
||||||
|
if (object != null) {
|
||||||
|
BigDecimal value = new BigDecimal(new BigInteger(object.toString()),
|
||||||
|
this.decimalValues);
|
||||||
|
return (value.stripTrailingZeros()).toPlainString();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
82
Alpano/src/ch/epfl/alpano/gui/ImagePainter.java
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import javafx.scene.paint.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface of an image Painter
|
||||||
|
*/
|
||||||
|
public interface ImagePainter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the color at the point (x,y)
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* : x index
|
||||||
|
* @param y
|
||||||
|
* : y index
|
||||||
|
* @return color at (x,y)
|
||||||
|
*/
|
||||||
|
public Color colorAt(int x, int y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the color at (x,y)
|
||||||
|
*
|
||||||
|
* @param hue
|
||||||
|
* : hue channel painter
|
||||||
|
* @param saturation
|
||||||
|
* : saturation channel painter
|
||||||
|
* @param brightness
|
||||||
|
* : brightness channel painter
|
||||||
|
* @param opacity
|
||||||
|
* : opacity channel painter
|
||||||
|
* @return color in hsbo
|
||||||
|
* @see colorAt
|
||||||
|
*/
|
||||||
|
public static ImagePainter hsb(ChannelPainter hue,
|
||||||
|
ChannelPainter saturation, ChannelPainter brightness,
|
||||||
|
ChannelPainter opacity) {
|
||||||
|
return (x, y) -> {
|
||||||
|
return Color.hsb(hue.valueAt(x, y), saturation.valueAt(x, y),
|
||||||
|
brightness.valueAt(x, y), opacity.valueAt(x, y));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the color at (x,y)
|
||||||
|
*
|
||||||
|
* @param grey
|
||||||
|
* : grey channel painter
|
||||||
|
* @param opacity
|
||||||
|
* : opacity channel painter
|
||||||
|
* @return color in grey-scale and opacity
|
||||||
|
* @see ColorAt
|
||||||
|
*/
|
||||||
|
public static ImagePainter gray(ChannelPainter grey,
|
||||||
|
ChannelPainter opacity) {
|
||||||
|
return (x, y) -> {
|
||||||
|
return Color.gray(grey.valueAt(x, y), opacity.valueAt(x, y));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the color at (x,y)
|
||||||
|
*
|
||||||
|
* @param red : red channel painter
|
||||||
|
* @param green : green channel painter
|
||||||
|
* @param blue : blue channel painter
|
||||||
|
* @param opacity
|
||||||
|
* : opacity channel painter
|
||||||
|
* @return color in rgb and opacity
|
||||||
|
* @see ColorAt
|
||||||
|
*/
|
||||||
|
public static ImagePainter rgb(ChannelPainter red, ChannelPainter green,ChannelPainter blue,
|
||||||
|
ChannelPainter opacity) {
|
||||||
|
return (x, y) -> {
|
||||||
|
return Color.rgb((int)red.valueAt(x, y),(int)green.valueAt(x, y),(int)blue.valueAt(x, y), opacity.valueAt(x, y));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to define a list of strings to be converted to integers (index of the string in the list)
|
||||||
|
*/
|
||||||
|
public class LabeledListStringConverter extends StringConverter<Integer>{
|
||||||
|
|
||||||
|
private List<String> listOfString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor saving the string of a list
|
||||||
|
* @param list : list of strings
|
||||||
|
*/
|
||||||
|
public LabeledListStringConverter(String...list ){
|
||||||
|
listOfString = new ArrayList<String>();
|
||||||
|
for (String string:list){
|
||||||
|
this.listOfString.add(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer fromString(String s){
|
||||||
|
return listOfString.indexOf(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(Integer object) {
|
||||||
|
return listOfString.get(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
307
Alpano/src/ch/epfl/alpano/gui/Labelizer.java
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import static ch.epfl.alpano.Math2.firstIntervalContainingRoot;
|
||||||
|
import static ch.epfl.alpano.Math2.sq;
|
||||||
|
import static java.lang.Math.round;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
import static ch.epfl.alpano.PanoramaComputer.NATURAL_VARIATION;
|
||||||
|
import static ch.epfl.alpano.PanoramaComputer.rayToGroundDistance;
|
||||||
|
import ch.epfl.alpano.PanoramaParameters;
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.ElevationProfile;
|
||||||
|
import ch.epfl.alpano.summit.Summit;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.shape.Line;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.transform.Rotate;
|
||||||
|
import javafx.scene.transform.Translate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public class, used to get a list of labels and lines for a given panorama.
|
||||||
|
* Used to display the name of the visible summits.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class Labelizer {
|
||||||
|
|
||||||
|
private static final int OFFSET = 20;
|
||||||
|
private static final int BORDER = 20;
|
||||||
|
private static final int MARGIN = 200;
|
||||||
|
|
||||||
|
private final ContinuousElevationModel elevationModel;
|
||||||
|
private final List<Summit> summits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a labelizer
|
||||||
|
*
|
||||||
|
* @param elevationModel
|
||||||
|
* : a Continuous elevation model
|
||||||
|
* @param summits
|
||||||
|
* : a list of summits
|
||||||
|
* @see ContinuousElevationModel
|
||||||
|
* @see Summits
|
||||||
|
*/
|
||||||
|
public Labelizer(ContinuousElevationModel elevationModel,
|
||||||
|
List<Summit> summits) {
|
||||||
|
this.elevationModel = elevationModel;
|
||||||
|
this.summits = summits;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates labels from a list of Summmits
|
||||||
|
*
|
||||||
|
* @param params
|
||||||
|
* : parameters
|
||||||
|
* @return a list of Nodes containing labels and lines Main function
|
||||||
|
* building a list of labels for given parameters.
|
||||||
|
*/
|
||||||
|
public List<Node> labels(PanoramaParameters params) {
|
||||||
|
List<Node> l = new ArrayList<Node>();
|
||||||
|
List<VisibleSummitData> visibleSummits = visibleSummits(params);
|
||||||
|
int xPixel, yPixel;
|
||||||
|
int lineHeight = 0;
|
||||||
|
boolean inX, inY;
|
||||||
|
boolean lineHeightSet = false;
|
||||||
|
BitSet bitSet = new BitSet(params.width());
|
||||||
|
|
||||||
|
for (VisibleSummitData s : visibleSummits) {
|
||||||
|
xPixel = (int) round(s.xPixel());
|
||||||
|
yPixel = (int) round(s.yPixel());
|
||||||
|
inX = (xPixel >= BORDER && xPixel <= (params.width() - BORDER));
|
||||||
|
inY = (yPixel >= 170);
|
||||||
|
if (inX && inY && !lineHeightSet) {
|
||||||
|
lineHeight = yPixel - 20;
|
||||||
|
lineHeightSet = true;
|
||||||
|
}
|
||||||
|
if (inX && inY && isLabelizable(bitSet, xPixel)) {
|
||||||
|
Text text = new Text();
|
||||||
|
text.getTransforms().addAll(new Translate(xPixel, lineHeight),
|
||||||
|
new Rotate(-60, 0, 0));
|
||||||
|
text.setText(s.summit().name() + " (" + s.summit().elevation()
|
||||||
|
+ " m)");
|
||||||
|
|
||||||
|
Line line = new Line(xPixel, lineHeight, xPixel, yPixel);
|
||||||
|
|
||||||
|
l.add(text);
|
||||||
|
l.add(line);
|
||||||
|
updateBitSet(bitSet, xPixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* changes false entries to a true entries between 2 indexes
|
||||||
|
* @param b
|
||||||
|
* @param xIndex
|
||||||
|
*/
|
||||||
|
private void updateBitSet(BitSet b, int xIndex) {
|
||||||
|
b.set(xIndex, xIndex + OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a given bitset and determines if the set of entries between two indexes are updatable (are false)
|
||||||
|
* @param b
|
||||||
|
* @param xIndex
|
||||||
|
* @return true if the set of entries
|
||||||
|
*/
|
||||||
|
private boolean isLabelizable(BitSet b, int xIndex) {
|
||||||
|
return b.get(xIndex, xIndex + OFFSET).cardinality() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using a list of summits and their information, computes and returns those
|
||||||
|
* who are visible.
|
||||||
|
*
|
||||||
|
* @param params
|
||||||
|
* : the PanoramaParameters
|
||||||
|
* @return a list of visible summits
|
||||||
|
*/
|
||||||
|
private List<VisibleSummitData> visibleSummits(PanoramaParameters params) {
|
||||||
|
List<VisibleSummitData> visible = new ArrayList<VisibleSummitData>();
|
||||||
|
double root;
|
||||||
|
boolean inHFOV, inVFOV, inFOV;
|
||||||
|
ElevationProfile evp;
|
||||||
|
DoubleUnaryOperator func;
|
||||||
|
SummitData summitData;
|
||||||
|
|
||||||
|
for (Summit s : this.summits) {
|
||||||
|
summitData = new SummitData(params, s);
|
||||||
|
|
||||||
|
inHFOV = Math.abs(params.centerAzimuth()
|
||||||
|
- summitData.azimuthToSummit()) < params
|
||||||
|
.horizontalFieldOfView() / 2.;
|
||||||
|
inVFOV = Math.abs(summitData.slope()) < params.verticalFieldOfView()
|
||||||
|
/ 2.;
|
||||||
|
inFOV = summitData.horizontalDistanceToSummit() <= params
|
||||||
|
.maxDistance();
|
||||||
|
if (inHFOV && inVFOV && inFOV) {
|
||||||
|
|
||||||
|
evp = new ElevationProfile(this.elevationModel,
|
||||||
|
params.observerPosition(), summitData.azimuthToSummit(),
|
||||||
|
summitData.horizontalDistanceToSummit());
|
||||||
|
double slope = -((rayToGroundDistance(evp, params.observerElevation(),
|
||||||
|
0).applyAsDouble(summitData.horizontalDistanceToSummit()))/summitData.horizontalDistanceToSummit());
|
||||||
|
func = rayToGroundDistance(evp, params.observerElevation(),
|
||||||
|
slope);
|
||||||
|
|
||||||
|
root = firstIntervalContainingRoot(func, 0,
|
||||||
|
summitData.horizontalDistanceToSummit(), 64.0);
|
||||||
|
|
||||||
|
if (root >= summitData.horizontalDistanceToSummit() - MARGIN) {
|
||||||
|
visible.add(new VisibleSummitData(params, s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visible.sort((s1, s2) -> {
|
||||||
|
return Double.compare(s1.yPixel(), s2.yPixel());
|
||||||
|
});
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enclosed class, used by the labelizer
|
||||||
|
* Gives access to functions using the summit and the parameters
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static class SummitData {
|
||||||
|
private final double azimuthToSummit, slope, variation;
|
||||||
|
private final double horizontalDistanceToSummit,
|
||||||
|
verticalDistanceToSummit;
|
||||||
|
private final PanoramaParameters params;
|
||||||
|
private final Summit summit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a SummitData object
|
||||||
|
* @param params : the panorama parameters
|
||||||
|
* @param s : the summit
|
||||||
|
*/
|
||||||
|
public SummitData(PanoramaParameters params, Summit s) {
|
||||||
|
this.azimuthToSummit = params.observerPosition()
|
||||||
|
.azimuthTo(s.position());
|
||||||
|
|
||||||
|
this.horizontalDistanceToSummit = params.observerPosition()
|
||||||
|
.distanceTo(s.position());
|
||||||
|
this.verticalDistanceToSummit = s.elevation()
|
||||||
|
- params.observerElevation();
|
||||||
|
this.variation = sq(horizontalDistanceToSummit) * NATURAL_VARIATION;
|
||||||
|
this.slope = (verticalDistanceToSummit - variation)
|
||||||
|
/ horizontalDistanceToSummit;
|
||||||
|
this.params = params;
|
||||||
|
this.summit = s;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected function returning the panorama parameters
|
||||||
|
* @return the panorama parameters
|
||||||
|
*/
|
||||||
|
PanoramaParameters params() {
|
||||||
|
return this.params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected function returning the altitude variation
|
||||||
|
* @return the altitude variation
|
||||||
|
* @see NATURAL_VARIATION
|
||||||
|
*/
|
||||||
|
double variation(){
|
||||||
|
return this.variation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the summit
|
||||||
|
* @return summit
|
||||||
|
*/
|
||||||
|
public Summit summit() {
|
||||||
|
return this.summit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azimuth to Summit
|
||||||
|
* @return azimuth to summit (angle)
|
||||||
|
*/
|
||||||
|
public double azimuthToSummit() {
|
||||||
|
return this.azimuthToSummit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal Distance to the summit
|
||||||
|
* @return horizontal distance between user and summit (m)
|
||||||
|
*/
|
||||||
|
public double horizontalDistanceToSummit() {
|
||||||
|
return this.horizontalDistanceToSummit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical Distance to the summit
|
||||||
|
* @return vertical distance between user and summit (m)
|
||||||
|
* (without the variation)
|
||||||
|
*/
|
||||||
|
public double verticalDistanceToSummit() {
|
||||||
|
return this.verticalDistanceToSummit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The slope
|
||||||
|
* @return slope
|
||||||
|
* (vertical distance divided by horizontal distance)
|
||||||
|
*/
|
||||||
|
public double slope() {
|
||||||
|
return this.slope;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A summitData extention for visible summits
|
||||||
|
* Used to get the x index and y index.
|
||||||
|
* @see SummitData
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static class VisibleSummitData extends SummitData {
|
||||||
|
private final double xIndex, yIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a visible summit
|
||||||
|
* @param params : the panorama parameters
|
||||||
|
* @param s : the summit
|
||||||
|
*/
|
||||||
|
public VisibleSummitData(PanoramaParameters params, Summit s) {
|
||||||
|
super(params, s);
|
||||||
|
this.xIndex = params().xForAzimuth(azimuthToSummit());
|
||||||
|
this.yIndex = params().yForAltitude(
|
||||||
|
Math.atan((verticalDistanceToSummit() - variation())
|
||||||
|
/ horizontalDistanceToSummit()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the x index of the summit
|
||||||
|
* @return x index
|
||||||
|
*/
|
||||||
|
public double xPixel() {
|
||||||
|
return this.xIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the y index of the summit
|
||||||
|
* @return y index
|
||||||
|
*/
|
||||||
|
public double yPixel() {
|
||||||
|
return this.yIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
292
Alpano/src/ch/epfl/alpano/gui/MapViewBean.java
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.PixelWriter;
|
||||||
|
import javafx.scene.image.WritableImage;
|
||||||
|
import javafx.scene.input.MouseButton;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.shape.Line;
|
||||||
|
|
||||||
|
public class MapViewBean {
|
||||||
|
|
||||||
|
//private final static int MAXSPOTS = 16;
|
||||||
|
private final static int IMAGE_SIZE = 720;
|
||||||
|
|
||||||
|
private double WIDTH = toRadians(0.5);
|
||||||
|
private double STEP =WIDTH / (IMAGE_SIZE - 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image property
|
||||||
|
*/
|
||||||
|
public final ObjectProperty<Image> mapView= new SimpleObjectProperty<Image>() ;
|
||||||
|
|
||||||
|
private final ObjectProperty<ObservableList<GeoPoint>> ts =
|
||||||
|
new SimpleObjectProperty<ObservableList<GeoPoint>>(FXCollections.observableList(new ArrayList<GeoPoint>()));
|
||||||
|
private final ObjectProperty<ObservableList<GeoPoint>> cts =
|
||||||
|
new SimpleObjectProperty<ObservableList<GeoPoint>>(FXCollections.observableList(new ArrayList<GeoPoint>()));
|
||||||
|
|
||||||
|
private final ObjectProperty<ObservableList<Line>> tl =
|
||||||
|
new SimpleObjectProperty<ObservableList<Line>>(FXCollections.observableList(new ArrayList<Line>()));
|
||||||
|
|
||||||
|
|
||||||
|
private final PanoramaComputerBean pcbean;
|
||||||
|
|
||||||
|
public MapViewBean(PanoramaComputerBean pcbean){
|
||||||
|
this.pcbean = pcbean;
|
||||||
|
|
||||||
|
pcbean.imageProperty().addListener(e->{
|
||||||
|
this.renderPanorama();
|
||||||
|
});
|
||||||
|
|
||||||
|
pcbean.parametersProperty().addListener(e->{
|
||||||
|
this.clearTs();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<ObservableList<GeoPoint>> cts() {
|
||||||
|
return cts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<ObservableList<GeoPoint>> ts() {
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTs(double x, double y){
|
||||||
|
GeoPoint gp = new GeoPoint(
|
||||||
|
pixelToLongitude(x),
|
||||||
|
pixelToLatitude(y));
|
||||||
|
addTs(gp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTs(GeoPoint gp){
|
||||||
|
//if(ts.get().size()<MAXSPOTS){
|
||||||
|
ts.get().add(gp);
|
||||||
|
if (ts.get().size() >= 2) {
|
||||||
|
|
||||||
|
double initX = longitudeToPixel(ts.get()
|
||||||
|
.get(ts.get().size() - 2));
|
||||||
|
double initY = latitudeToPixel(ts.get()
|
||||||
|
.get(ts.get().size() - 2));
|
||||||
|
double endX = longitudeToPixel(ts.get()
|
||||||
|
.get(ts.get().size() - 1));
|
||||||
|
double endY = latitudeToPixel(ts.get()
|
||||||
|
.get(ts.get().size() - 1));
|
||||||
|
Line line = new Line(initX, initY, endX, endY);
|
||||||
|
|
||||||
|
line.setStroke(Color.RED);
|
||||||
|
line.setStrokeWidth(2);
|
||||||
|
tl.get().add(line);
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<ObservableList<Line>> tl() {
|
||||||
|
return tl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearTs(){
|
||||||
|
ts.get().clear();
|
||||||
|
cts.get().clear();
|
||||||
|
tl.get().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls path finding implementation on the start and end point of the "trekkingSpots" array
|
||||||
|
* @param p : the pane used to display the path
|
||||||
|
* @param pcbean: the computer bean used to compute the path
|
||||||
|
*/
|
||||||
|
public void buildEndToEndPath(){
|
||||||
|
if (ts.get().size()>=2){
|
||||||
|
ArrayList<GeoPoint> nts = new ArrayList<>();
|
||||||
|
for(int i = 0;i<ts.get().size()-1;++i){
|
||||||
|
tl.get().remove(tl.get().size()-ts.get().size()+1+i);
|
||||||
|
PatherTheta pt = new PatherTheta(pcbean,this);
|
||||||
|
List<GeoPoint> gps = pt.astar(ts.get().get(i), ts.get().get( i+1));
|
||||||
|
Collections.reverse(gps);
|
||||||
|
for(GeoPoint gp : gps){
|
||||||
|
if(!nts.contains(gp)){
|
||||||
|
nts.add(gp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//clearTs();
|
||||||
|
ts.get().clear();
|
||||||
|
for(GeoPoint ts : nts){
|
||||||
|
addTs(ts);
|
||||||
|
}
|
||||||
|
cts.get().addAll(ts.get());
|
||||||
|
ts.get().clear();
|
||||||
|
ts.get().add(nts.get(nts.size()-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the reference latitude of the map view
|
||||||
|
* @return the corresponding latitude to y=0 on the map view
|
||||||
|
*/
|
||||||
|
private double referenceLat(){
|
||||||
|
return (pcbean.getParameters().observerLatitudeFix()+WIDTH/2.0);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* returns the reference longitude of the map view
|
||||||
|
* @return the corresponding longtitude to x=0 on the map view
|
||||||
|
*/
|
||||||
|
private double referenceLon(){
|
||||||
|
return (pcbean.getParameters().observerLongitudeFix()-WIDTH/2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterates on all elevations on a cDem to return the highest one
|
||||||
|
* this method's purpose is to dynamically set grayscale mapping to a relevant interval of heights
|
||||||
|
* @param cDem
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private double maxElevation(){
|
||||||
|
double max = Integer.MIN_VALUE;
|
||||||
|
for (int x = 0; x < IMAGE_SIZE; ++x) {
|
||||||
|
double lon = referenceLon() + x * STEP;
|
||||||
|
for (int y = 0; y < IMAGE_SIZE; ++y) {
|
||||||
|
double lat = referenceLat() - y * STEP;
|
||||||
|
GeoPoint gp = new GeoPoint(lon, lat);
|
||||||
|
double el = (pcbean.cDem().get().elevationAt(gp));
|
||||||
|
if (max<el){
|
||||||
|
max = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* iterates on all elevations on a cDem to return the lowest one
|
||||||
|
* this method's purpose is to dynamically set grayscale mapping to a relevant interval of heights
|
||||||
|
* @param cDem
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private double minElevation(){
|
||||||
|
double min = Integer.MAX_VALUE;
|
||||||
|
for (int x = 0; x < IMAGE_SIZE; ++x) {
|
||||||
|
double lon = referenceLon() + x * STEP;
|
||||||
|
for (int y = 0; y < IMAGE_SIZE; ++y) {
|
||||||
|
double lat = referenceLat() - y * STEP;
|
||||||
|
GeoPoint gp = new GeoPoint(lon, lat);
|
||||||
|
double el = (pcbean.cDem().get().elevationAt(gp));
|
||||||
|
if (min>el){
|
||||||
|
min = el;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function used to transform a panorama to an image
|
||||||
|
* Paints using a gradient painter if the current selected
|
||||||
|
* painter is a gradient painter, otherwhise uses
|
||||||
|
* black and white elevation painter
|
||||||
|
*
|
||||||
|
* @param p
|
||||||
|
* : the panorama
|
||||||
|
* @param l
|
||||||
|
* : the image painter
|
||||||
|
* @return image
|
||||||
|
*/
|
||||||
|
void renderPanorama() {
|
||||||
|
CustomPainters cp = pcbean.painterProperty().get();
|
||||||
|
WritableImage im = new WritableImage(720, 720);
|
||||||
|
PixelWriter w = im.getPixelWriter();
|
||||||
|
|
||||||
|
double minEl = minElevation();
|
||||||
|
double maxEl = maxElevation();
|
||||||
|
for (int x = 0; x < IMAGE_SIZE; ++x) {
|
||||||
|
double lon = referenceLon() + x * STEP;
|
||||||
|
for (int y = 0; y < IMAGE_SIZE; ++y) {
|
||||||
|
double lat = referenceLat() - y * STEP;
|
||||||
|
GeoPoint gp = new GeoPoint(lon, lat);
|
||||||
|
if(cp.isGradient()){
|
||||||
|
w.setColor(x, y, cp.elevationToColor(pcbean.cDem().get().elevationAt(gp),pcbean.cDem().get().slopeAt(gp)));
|
||||||
|
}else{
|
||||||
|
double el = (pcbean.cDem().get().elevationAt(gp) - minEl)
|
||||||
|
/ (maxEl - minEl);
|
||||||
|
w.setColor(x, y, gray(el));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapView.set(im);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*converts a pixel y value to a longitude;
|
||||||
|
* @param x
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
double pixelToLongitude(double x){
|
||||||
|
return referenceLon() + x * STEP;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* converts a pixel x value to a latitude;
|
||||||
|
* @param y
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
double pixelToLatitude(double y){
|
||||||
|
return referenceLat() - y * STEP;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* converts a longitude to a pixel x value
|
||||||
|
* @param gP
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
double longitudeToPixel(GeoPoint gP){
|
||||||
|
return ((gP.longitude()-referenceLon())/STEP);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* converts a latitude to a pixel y value
|
||||||
|
* @param gP
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
double latitudeToPixel(GeoPoint gP){
|
||||||
|
return ( -(gP.latitude()-referenceLat())/STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* makes the grayScale value of a pixel
|
||||||
|
* @param v1
|
||||||
|
* @return a color
|
||||||
|
*/
|
||||||
|
private static Color gray(double v1) {
|
||||||
|
double v = max(0, min(v1, 1));
|
||||||
|
return new Color(v, v, v, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<Image> mapView() {
|
||||||
|
return this.mapView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void zoom(MouseButton mb){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
216
Alpano/src/ch/epfl/alpano/gui/PanoramaComputerBean.java
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import java.beans.Beans;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import ch.epfl.alpano.Panorama;
|
||||||
|
import ch.epfl.alpano.PanoramaComputer;
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.mapzen.MapzenManager;
|
||||||
|
import ch.epfl.alpano.summit.Summit;
|
||||||
|
import static ch.epfl.alpano.gui.PanoramaRenderer.renderPanorama;
|
||||||
|
import static javafx.application.Platform.runLater;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to link the panorama computing with the Graphical interface (through the PanoramaParametersBean)
|
||||||
|
*/
|
||||||
|
public class PanoramaComputerBean extends Beans {
|
||||||
|
|
||||||
|
private final ObjectProperty<Panorama> panoramaProperty;
|
||||||
|
private final ObjectProperty<PanoramaUserParameters> parametersProperty;
|
||||||
|
private final ObjectProperty<Image> imageProperty;
|
||||||
|
private final ObjectProperty<ObservableList<Node>> labelsProperty;
|
||||||
|
private final ObjectProperty<ContinuousElevationModel> cDEM;
|
||||||
|
private final ObjectProperty<CustomPainters> imp;
|
||||||
|
private final ObjectProperty<Boolean> downloadProperty;
|
||||||
|
private final ObjectProperty<String> downloadTextProperty;
|
||||||
|
private final List<Summit> summits;
|
||||||
|
private final MapzenManager HGTGetter = new MapzenManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a bean for a panoramaComputer
|
||||||
|
* @param summits : a list of summits
|
||||||
|
*/
|
||||||
|
public PanoramaComputerBean(List<Summit> summits){
|
||||||
|
this.cDEM = new SimpleObjectProperty<ContinuousElevationModel>();
|
||||||
|
this.cDEM.bind(HGTGetter.cDEM());
|
||||||
|
this.downloadProperty = new SimpleObjectProperty<Boolean>(false);
|
||||||
|
this.summits = summits;
|
||||||
|
this.parametersProperty = new SimpleObjectProperty<PanoramaUserParameters>(null);
|
||||||
|
ObservableList<Node> oList = FXCollections.observableList(new ArrayList<Node>());
|
||||||
|
this.labelsProperty = new SimpleObjectProperty<ObservableList<Node>>(oList);
|
||||||
|
this.panoramaProperty = new SimpleObjectProperty<Panorama>(null);
|
||||||
|
this.imageProperty = new SimpleObjectProperty<Image>(null);
|
||||||
|
this.imp = new SimpleObjectProperty<CustomPainters>(CustomPainters.GRADDEMPOST);
|
||||||
|
HGTGetter.cDEM().addListener(
|
||||||
|
(b, o, n) -> runLater(this::updatePanorama));
|
||||||
|
this.downloadTextProperty = new SimpleObjectProperty<String>();
|
||||||
|
HGTGetter.text().addListener(
|
||||||
|
(b, o, n) -> runLater(this::updateDownloadText));
|
||||||
|
this.imp.addListener( (b, o, n) -> runLater(this::repaintPanorama));
|
||||||
|
parametersProperty.addListener((b, o, n) -> this.downloadHGT());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the elevation model property object
|
||||||
|
* @return ContinuousElevationModel property object
|
||||||
|
*/
|
||||||
|
ReadOnlyObjectProperty<ContinuousElevationModel> cDem(){
|
||||||
|
return this.cDEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the panorama property object
|
||||||
|
* @return panorama property object
|
||||||
|
*/
|
||||||
|
ReadOnlyObjectProperty<Panorama> panoramaProperty(){
|
||||||
|
return panoramaProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parameters property object
|
||||||
|
* @return parameters property object
|
||||||
|
*/
|
||||||
|
ObjectProperty<PanoramaUserParameters> parametersProperty(){
|
||||||
|
return parametersProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the image property object
|
||||||
|
* @return image property object
|
||||||
|
*/
|
||||||
|
ReadOnlyObjectProperty<Image> imageProperty(){
|
||||||
|
return imageProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the labels property object
|
||||||
|
* @return labels property object
|
||||||
|
*/
|
||||||
|
ReadOnlyObjectProperty<ObservableList<Node>> labelsProperty(){
|
||||||
|
return labelsProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the painter property object
|
||||||
|
* @return Custom Painter property object
|
||||||
|
*/
|
||||||
|
ReadOnlyObjectProperty<CustomPainters> painterProperty() {
|
||||||
|
return imp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the download property object
|
||||||
|
* This object is used to set if Mapzen is currently downloading
|
||||||
|
* @return boolean download property object
|
||||||
|
*/
|
||||||
|
ReadOnlyObjectProperty<Boolean> downloadProperty(){
|
||||||
|
return downloadProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the labels
|
||||||
|
* @return labels
|
||||||
|
*/
|
||||||
|
ObservableList<Node> getLabels(){
|
||||||
|
return labelsProperty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the panorama
|
||||||
|
* @return panorama
|
||||||
|
*/
|
||||||
|
Panorama getPanorama(){
|
||||||
|
return panoramaProperty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parameters
|
||||||
|
* @return parameters
|
||||||
|
*/
|
||||||
|
PanoramaUserParameters getParameters(){
|
||||||
|
return parametersProperty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the parameters
|
||||||
|
* @param newParameters : new PanoramaUserParameters
|
||||||
|
*/
|
||||||
|
void setParameters(PanoramaUserParameters newParameters){
|
||||||
|
this.parametersProperty.set(newParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the custom painter
|
||||||
|
* @param cp : new painter
|
||||||
|
*/
|
||||||
|
void setPainter(CustomPainters cp) {
|
||||||
|
imp.set(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retuns the image
|
||||||
|
* @return image
|
||||||
|
*/
|
||||||
|
Image getImage(){
|
||||||
|
return imageProperty.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text displayed when downloading property object
|
||||||
|
* @return download text property object
|
||||||
|
*/
|
||||||
|
ReadOnlyObjectProperty<String> downloadTextProperty(){
|
||||||
|
return downloadTextProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the download text getting new text from the MapzenManager
|
||||||
|
*/
|
||||||
|
private void updateDownloadText(){
|
||||||
|
this.downloadTextProperty.set(HGTGetter.text().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads the HGT Files
|
||||||
|
*/
|
||||||
|
private void downloadHGT(){
|
||||||
|
this.downloadProperty.set(true);
|
||||||
|
HGTGetter.update(this.getParameters().panoramaDisplayParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repaints the panorama
|
||||||
|
*/
|
||||||
|
private void repaintPanorama(){
|
||||||
|
if(this.getPanorama()!=null)
|
||||||
|
this.imageProperty.set(renderPanorama(this.getPanorama(), imp.get().painter(this.getPanorama())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the new panorama
|
||||||
|
*/
|
||||||
|
private void updatePanorama() {
|
||||||
|
this.downloadProperty.set(false);
|
||||||
|
Panorama panorama = new PanoramaComputer(cDEM.get()).computePanorama(this.getParameters().panoramaComputeParameters());
|
||||||
|
List<Node> newLabels = new Labelizer(cDEM.get(), summits).labels(this.getParameters().panoramaDisplayParameters());
|
||||||
|
this.panoramaProperty.set(panorama);
|
||||||
|
this.imageProperty.set(renderPanorama(panorama, imp.get().painter(panorama)));
|
||||||
|
this.labelsProperty().get().setAll(newLabels);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
214
Alpano/src/ch/epfl/alpano/gui/PanoramaParametersBean.java
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import static javafx.application.Platform.runLater;
|
||||||
|
|
||||||
|
import java.beans.Beans;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to manage the GUI and the Panorama User Parameters
|
||||||
|
*/
|
||||||
|
public class PanoramaParametersBean extends Beans {
|
||||||
|
private ObjectProperty<PanoramaUserParameters> parametersProperty;
|
||||||
|
|
||||||
|
private Map<UserParameter, ObjectProperty<Integer>> bufferParametersProperty = new EnumMap<UserParameter, ObjectProperty<Integer>>(
|
||||||
|
UserParameter.class);
|
||||||
|
private ObjectProperty<CustomPainters> painterProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor initializing the object property for every user parameter and
|
||||||
|
* adds listeners to them
|
||||||
|
*
|
||||||
|
* @param parameters
|
||||||
|
* : initial parameters to use
|
||||||
|
*/
|
||||||
|
public PanoramaParametersBean(PanoramaUserParameters parameters) {
|
||||||
|
|
||||||
|
this.parametersProperty = new SimpleObjectProperty<PanoramaUserParameters>(
|
||||||
|
parameters);
|
||||||
|
|
||||||
|
for (UserParameter param : UserParameter.values()) {
|
||||||
|
ObjectProperty<Integer> property = new SimpleObjectProperty<Integer>(
|
||||||
|
parameters.get(param));
|
||||||
|
property.addListener(
|
||||||
|
(b, o, n) -> runLater(this::synchronizeParameters));
|
||||||
|
bufferParametersProperty.put(param, property);
|
||||||
|
}
|
||||||
|
this.painterProperty = new SimpleObjectProperty<CustomPainters>(CustomPainters.DEFAULT);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronize the parameters by building new PanoramaUserParameters and
|
||||||
|
* using it to update the current values
|
||||||
|
*
|
||||||
|
* @see updateProperties
|
||||||
|
* @see buildNewParameters
|
||||||
|
*/
|
||||||
|
private void synchronizeParameters() {
|
||||||
|
updateProperties(buildNewParameters());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the parameters using the checked parameters
|
||||||
|
* @param parameters
|
||||||
|
*/
|
||||||
|
public void updateProperties(PanoramaUserParameters parameters) {
|
||||||
|
this.parametersProperty.set(parameters);
|
||||||
|
|
||||||
|
for (UserParameter param : UserParameter.values()) {
|
||||||
|
|
||||||
|
this.bufferParametersProperty.get(param).set(parameters.get(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the painter to use
|
||||||
|
* @param cp : CustomPainters
|
||||||
|
*/
|
||||||
|
public void setPainter(CustomPainters cp) {
|
||||||
|
this.painterProperty.set(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute new PanoramaUserParameters using the current ObjectProperty
|
||||||
|
* values and returns it
|
||||||
|
*
|
||||||
|
* @return new PanoramaUserParameters
|
||||||
|
*/
|
||||||
|
private PanoramaUserParameters buildNewParameters() {
|
||||||
|
return new PanoramaUserParameters(
|
||||||
|
ObserverLongitudeProperty().getValue(),
|
||||||
|
|
||||||
|
ObserverLatitudeProperty().getValue(),
|
||||||
|
ObserverElevationProperty().getValue(),
|
||||||
|
CenterAzimuthProperty().getValue(),
|
||||||
|
HorizontalFieldOfViewProperty().getValue(),
|
||||||
|
MaxDistanceProperty().getValue(), WidthProperty().getValue(),
|
||||||
|
HeightProperty().getValue(),
|
||||||
|
SuperSamplingExponentProperty().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the map of UserParameter property objects
|
||||||
|
*
|
||||||
|
* @return the buffer parameter property
|
||||||
|
*/
|
||||||
|
public Map<UserParameter, ObjectProperty<Integer>> bufferParametersProperty() {
|
||||||
|
return this.bufferParametersProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param parameter
|
||||||
|
* : a UserParameter
|
||||||
|
* @return the object property of the parameter
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> getProperty(UserParameter parameter) {
|
||||||
|
return this.bufferParametersProperty.get(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the PanoramaUserParamerer property object
|
||||||
|
*/
|
||||||
|
public ReadOnlyObjectProperty<PanoramaUserParameters> ParametersProperty() {
|
||||||
|
return this.parametersProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the Longitude property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> ObserverLongitudeProperty() {
|
||||||
|
|
||||||
|
return bufferParametersProperty.get(UserParameter.OBSERVER_LONGITUDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the Longitude property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> ObserverLatitudeProperty() {
|
||||||
|
return bufferParametersProperty.get(UserParameter.OBSERVER_LATITUDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the Observer Elevation property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> ObserverElevationProperty() {
|
||||||
|
return bufferParametersProperty.get(UserParameter.OBSERVER_ELEVATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the Center Azimuth property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> CenterAzimuthProperty() {
|
||||||
|
return bufferParametersProperty.get(UserParameter.CENTER_AZIMUTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the Horizontal FOV property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> HorizontalFieldOfViewProperty() {
|
||||||
|
return bufferParametersProperty
|
||||||
|
.get(UserParameter.HORIZONTAL_FIELD_OF_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the Max Distance property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> MaxDistanceProperty() {
|
||||||
|
return bufferParametersProperty.get(UserParameter.MAX_DISTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the Width property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> WidthProperty() {
|
||||||
|
return bufferParametersProperty.get(UserParameter.WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the Height property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> HeightProperty() {
|
||||||
|
return bufferParametersProperty.get(UserParameter.HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the SuperSampling property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<Integer> SuperSamplingExponentProperty() {
|
||||||
|
return bufferParametersProperty
|
||||||
|
.get(UserParameter.SUPER_SAMPLING_EXPONENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the CustomPainters property object
|
||||||
|
*/
|
||||||
|
public ObjectProperty<CustomPainters> painterProperty() {
|
||||||
|
return this.painterProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
Alpano/src/ch/epfl/alpano/gui/PanoramaRenderer.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.Panorama;
|
||||||
|
import javafx.scene.image.*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface used to render a panorama
|
||||||
|
* Draws the image by computing the values pixel by pixel
|
||||||
|
*/
|
||||||
|
public interface PanoramaRenderer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function used to transform a panorama to an image
|
||||||
|
* @param p : the panorama
|
||||||
|
* @param l : the image painter
|
||||||
|
* @return image
|
||||||
|
*/
|
||||||
|
public static Image renderPanorama(Panorama p, ImagePainter l){
|
||||||
|
int height = p.parameters().height();
|
||||||
|
int width = p.parameters().width();
|
||||||
|
WritableImage im = new WritableImage(width,height);
|
||||||
|
PixelWriter w = im.getPixelWriter();
|
||||||
|
for(int x=0; x<width;++x){
|
||||||
|
for(int y=0; y<height;++y){
|
||||||
|
w.setColor(x, y, l.colorAt(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return im;
|
||||||
|
}
|
||||||
|
}
|
288
Alpano/src/ch/epfl/alpano/gui/PanoramaUserParameters.java
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import static java.lang.Math.pow;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import ch.epfl.alpano.PanoramaParameters;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class containing the user parameters for a panorama
|
||||||
|
*/
|
||||||
|
public final class PanoramaUserParameters {
|
||||||
|
private final Map<UserParameter, Integer> userParameters = new EnumMap<>(
|
||||||
|
UserParameter.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of panorama user parameter using the UserParameter enum
|
||||||
|
*
|
||||||
|
* @param userParameters : Map of user parameters
|
||||||
|
* @see UserParameter
|
||||||
|
*/
|
||||||
|
public PanoramaUserParameters(Map<UserParameter, Integer> userParameters) {
|
||||||
|
for (UserParameter key : userParameters.keySet()) {
|
||||||
|
this.userParameters.put(key, key.sanitize(userParameters.get(key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of panorama user parameter using independent values
|
||||||
|
*
|
||||||
|
* @param lon
|
||||||
|
* : longitude
|
||||||
|
* @param lat
|
||||||
|
* : latitude
|
||||||
|
* @param alt
|
||||||
|
* : altitude
|
||||||
|
* @param azimuth
|
||||||
|
* :azimuth
|
||||||
|
* @param fov
|
||||||
|
* : field of view
|
||||||
|
* @param dist
|
||||||
|
* : max distance
|
||||||
|
* @param width
|
||||||
|
* : width
|
||||||
|
* @param height
|
||||||
|
* : height
|
||||||
|
* @param ss
|
||||||
|
* : super sampling
|
||||||
|
*/
|
||||||
|
public PanoramaUserParameters(int lon, int lat, int alt, int azimuth,
|
||||||
|
int fov, int dist, int width, int height, int ss) {
|
||||||
|
this(toEnumMap(lon, lat, alt, azimuth, fov, dist, width, height, ss));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns PanoramaParameters used to compute the panorama
|
||||||
|
*
|
||||||
|
* @return PanoramaParameters
|
||||||
|
*/
|
||||||
|
public PanoramaParameters panoramaComputeParameters() {
|
||||||
|
int width = (int) (pow(2, superSamplingExponent()) * width());
|
||||||
|
int height = (int) (pow(2, superSamplingExponent()) * height());
|
||||||
|
return new PanoramaParameters(
|
||||||
|
new GeoPoint(observerLongitudeFix(), observerLatitudeFix()),
|
||||||
|
observerElevation(), centerAzimuthFix(),
|
||||||
|
horizontalFieldOfViewFix(), maxDistance(), width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes and returns PanoramaParameters used to display the panorama
|
||||||
|
*
|
||||||
|
* @return PanoramaParameters
|
||||||
|
*/
|
||||||
|
public PanoramaParameters panoramaDisplayParameters() {
|
||||||
|
return new PanoramaParameters(
|
||||||
|
new GeoPoint(observerLongitudeFix(), observerLatitudeFix()),
|
||||||
|
observerElevation(), centerAzimuthFix(),
|
||||||
|
horizontalFieldOfViewFix(), maxDistance(), width(), height());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for the value of a user parameter
|
||||||
|
*
|
||||||
|
* @param param
|
||||||
|
* : UserParameter to get
|
||||||
|
* @return the requested parameter value
|
||||||
|
*/
|
||||||
|
public int get(UserParameter param) {
|
||||||
|
return this.userParameters.get(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the observer longitude as interger
|
||||||
|
*
|
||||||
|
* @return longitude of the observer
|
||||||
|
* @see observerLongitudeFix to get the real value
|
||||||
|
*/
|
||||||
|
public int observerLongitude() {
|
||||||
|
return this.get(UserParameter.OBSERVER_LONGITUDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the observer longitude
|
||||||
|
*
|
||||||
|
* @return longitude of the observer
|
||||||
|
* @see observerLongitude to get the int value
|
||||||
|
*/
|
||||||
|
public double observerLongitudeFix() {
|
||||||
|
return toRadians(this.get(UserParameter.OBSERVER_LONGITUDE) / 10000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the observer latitude as integer
|
||||||
|
*
|
||||||
|
* @return latitude of the observer
|
||||||
|
* @see observerLatitudeFix to get the real value
|
||||||
|
*/
|
||||||
|
public int observerLatitude() {
|
||||||
|
return this.get(UserParameter.OBSERVER_LATITUDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the observer latitude
|
||||||
|
*
|
||||||
|
* @return latitude of the observer
|
||||||
|
* @see observerLatitude to get the int value
|
||||||
|
*/
|
||||||
|
public double observerLatitudeFix() {
|
||||||
|
return toRadians(this.get(UserParameter.OBSERVER_LATITUDE) / 10000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the observer altitude
|
||||||
|
*
|
||||||
|
* @return altitude of the observer
|
||||||
|
*/
|
||||||
|
public int observerElevation() {
|
||||||
|
return this.get(UserParameter.OBSERVER_ELEVATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the azimuth as integer
|
||||||
|
*
|
||||||
|
* @return azimuth
|
||||||
|
* @see centerAzimuthFix for the real value
|
||||||
|
*/
|
||||||
|
public int centerAzimuth() {
|
||||||
|
return this.get(UserParameter.CENTER_AZIMUTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the azimuth
|
||||||
|
*
|
||||||
|
* @return azimuth
|
||||||
|
* @see centerAzimuth for the int value
|
||||||
|
*/
|
||||||
|
public double centerAzimuthFix() {
|
||||||
|
return toRadians(this.get(UserParameter.CENTER_AZIMUTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the field of view as integer
|
||||||
|
*
|
||||||
|
* @return field of view
|
||||||
|
* @see horizontalFieldOfViewFix for the real value
|
||||||
|
*/
|
||||||
|
public int horizontalFieldOfView() {
|
||||||
|
return this.get(UserParameter.HORIZONTAL_FIELD_OF_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the field of view as integer
|
||||||
|
*
|
||||||
|
* @return field of view
|
||||||
|
* @see horizontalFieldOfView for the int value
|
||||||
|
*/
|
||||||
|
public double horizontalFieldOfViewFix() {
|
||||||
|
return toRadians(this.get(UserParameter.HORIZONTAL_FIELD_OF_VIEW));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the max distance
|
||||||
|
*
|
||||||
|
* @return max distance
|
||||||
|
*/
|
||||||
|
public int maxDistance() {
|
||||||
|
return this.get(UserParameter.MAX_DISTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the image width
|
||||||
|
*
|
||||||
|
* @return width
|
||||||
|
*/
|
||||||
|
public int width() {
|
||||||
|
return this.get(UserParameter.WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the image height
|
||||||
|
*
|
||||||
|
* @return height
|
||||||
|
*/
|
||||||
|
public int height() {
|
||||||
|
return this.get(UserParameter.HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the supersampling exponent
|
||||||
|
*
|
||||||
|
* @return supersampling exponent
|
||||||
|
*/
|
||||||
|
public int superSamplingExponent() {
|
||||||
|
return this.get(UserParameter.SUPER_SAMPLING_EXPONENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object that) {
|
||||||
|
|
||||||
|
//is that better than an "isinstanceof" ?
|
||||||
|
if( that instanceof PanoramaUserParameters){
|
||||||
|
for(UserParameter parameter : UserParameter.values()){
|
||||||
|
if( this.get(parameter)!= ((PanoramaUserParameters)that).get(parameter)){
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 0;
|
||||||
|
int exponent =0;
|
||||||
|
for(UserParameter parameter : UserParameter.values()){
|
||||||
|
hash += this.get(parameter)*Math.pow(31, exponent);
|
||||||
|
exponent ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( hash <0)
|
||||||
|
throw new IndexOutOfBoundsException("hashCode value of PanoramaUserParameter exceeded Integer.MAXVALUE");
|
||||||
|
return hash;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an EnumMap given some values
|
||||||
|
*
|
||||||
|
* @param lon
|
||||||
|
* @param lat
|
||||||
|
* @param alt
|
||||||
|
* @param azimuth
|
||||||
|
* @param fov
|
||||||
|
* @param dist
|
||||||
|
* @param width
|
||||||
|
* @param height
|
||||||
|
* @param ss
|
||||||
|
* @return EnumMap containing the values using UserParameter keys
|
||||||
|
* @see UserParameter
|
||||||
|
*/
|
||||||
|
private static final EnumMap<UserParameter, Integer> toEnumMap(int lon,
|
||||||
|
int lat, int alt, int azimuth, int fov, int dist, int width,
|
||||||
|
int height, int ss) {
|
||||||
|
EnumMap<UserParameter, Integer> map = new EnumMap<UserParameter, Integer>(
|
||||||
|
UserParameter.class);
|
||||||
|
map.put(UserParameter.OBSERVER_LONGITUDE, lon);
|
||||||
|
map.put(UserParameter.OBSERVER_LATITUDE, lat);
|
||||||
|
map.put(UserParameter.OBSERVER_ELEVATION, alt);
|
||||||
|
map.put(UserParameter.CENTER_AZIMUTH, azimuth);
|
||||||
|
map.put(UserParameter.HORIZONTAL_FIELD_OF_VIEW, fov);
|
||||||
|
map.put(UserParameter.MAX_DISTANCE, dist);
|
||||||
|
map.put(UserParameter.WIDTH, width);
|
||||||
|
map.put(UserParameter.HEIGHT, height);
|
||||||
|
map.put(UserParameter.SUPER_SAMPLING_EXPONENT, ss);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
186
Alpano/src/ch/epfl/alpano/gui/PatherTheta.java
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cédric Heozl
|
||||||
|
* Sciper: 257844
|
||||||
|
* Date: 3 Jun 2017
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import ch.epfl.alpano.dem.ElevationProfile;
|
||||||
|
|
||||||
|
public class PatherTheta {
|
||||||
|
|
||||||
|
private static final double EPSADMISIBLE =1.0;
|
||||||
|
|
||||||
|
private HashMap<PatherPoint,Double> gScore;
|
||||||
|
private HashMap<PatherPoint,Double> fScore;
|
||||||
|
private HashSet<PatherPoint> closedSet;
|
||||||
|
private HashSet<PatherPoint> openSet;
|
||||||
|
private HashMap<PatherPoint,PatherPoint> cameFrom;
|
||||||
|
|
||||||
|
private final PanoramaComputerBean pcbean;
|
||||||
|
private final MapViewBean mvbean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of a path searcher
|
||||||
|
* @param pcbean :the panorama computer bean
|
||||||
|
* @param ppbean m the current panorama parameter bean
|
||||||
|
*/
|
||||||
|
public PatherTheta(PanoramaComputerBean pcbean, MapViewBean mvbean) {
|
||||||
|
this.pcbean = pcbean;
|
||||||
|
this.mvbean = mvbean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<GeoPoint> astar(GeoPoint start, GeoPoint end){
|
||||||
|
PatherPoint orig = new PatherPoint(start);
|
||||||
|
PatherPoint goal = new PatherPoint(end);
|
||||||
|
PatherPoint current;
|
||||||
|
double tentativeScore;
|
||||||
|
closedSet = new HashSet<PatherPoint>();
|
||||||
|
openSet = new HashSet<PatherPoint>();
|
||||||
|
openSet.add(orig);
|
||||||
|
cameFrom = new HashMap<PatherPoint,PatherPoint>();
|
||||||
|
|
||||||
|
gScore = new HashMap<PatherPoint,Double>();
|
||||||
|
gScore.put(orig, 0.0);
|
||||||
|
|
||||||
|
fScore = new HashMap<PatherPoint,Double>();
|
||||||
|
fScore.put(orig, EPSADMISIBLE*orig.distanceTo(goal));
|
||||||
|
|
||||||
|
while(openSet.size()>0){
|
||||||
|
current = getNewCurrent();
|
||||||
|
if(current.equals(goal))
|
||||||
|
return reconstructPath(current);
|
||||||
|
|
||||||
|
openSet.remove(current);
|
||||||
|
closedSet.add(current);
|
||||||
|
|
||||||
|
for(PatherPoint neighbor : current.getNeighbours()){
|
||||||
|
if(closedSet.contains(neighbor))
|
||||||
|
continue;
|
||||||
|
tentativeScore = gScore.getOrDefault(current, Double.MAX_VALUE) + current.effortTo(neighbor);
|
||||||
|
if(!openSet.contains(neighbor))
|
||||||
|
openSet.add(neighbor);
|
||||||
|
else if(tentativeScore >= gScore.getOrDefault(neighbor, Double.MAX_VALUE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cameFrom.put(neighbor, current);
|
||||||
|
gScore.put(neighbor, tentativeScore);
|
||||||
|
fScore.put(neighbor, gScore.get(neighbor)+EPSADMISIBLE*neighbor.distanceTo(goal));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<GeoPoint> reconstructPath(PatherPoint current) {
|
||||||
|
ArrayList<GeoPoint> path = new ArrayList<>();
|
||||||
|
path.add(current.gp());
|
||||||
|
while(cameFrom.containsKey(current)){
|
||||||
|
current = cameFrom.get(current);
|
||||||
|
path.add(current.gp());
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PatherPoint getNewCurrent(){
|
||||||
|
PatherPoint output = null;
|
||||||
|
double minVal = Double.MAX_VALUE;
|
||||||
|
double newVal;
|
||||||
|
for(PatherPoint pp : openSet){
|
||||||
|
newVal = fScore.getOrDefault(pp, Double.MAX_VALUE);
|
||||||
|
if(newVal < minVal){
|
||||||
|
minVal = newVal;
|
||||||
|
output = pp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PatherPoint{
|
||||||
|
|
||||||
|
private final GeoPoint gp;
|
||||||
|
private final int x,y;
|
||||||
|
|
||||||
|
public PatherPoint(GeoPoint gp){
|
||||||
|
this.gp = gp;
|
||||||
|
this.x = (int) mvbean.longitudeToPixel(gp);
|
||||||
|
this.y = (int) mvbean.latitudeToPixel(gp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PatherPoint(int x, int y){
|
||||||
|
double lon = mvbean.pixelToLongitude(x);
|
||||||
|
double lat = mvbean.pixelToLatitude(y);
|
||||||
|
this.gp = new GeoPoint(lon, lat);
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int x(){
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
public int y(){
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeoPoint gp(){
|
||||||
|
return this.gp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean outOfField(){
|
||||||
|
return gp==null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PatherPoint[] getNeighbours(){
|
||||||
|
PatherPoint[] pps = new PatherPoint[8];
|
||||||
|
int i =0;
|
||||||
|
for (int a = -1; a <= 1; a++) {
|
||||||
|
for (int b = -1; b <= 1; b++) {
|
||||||
|
if (!(a == 0 && b == 0)) {
|
||||||
|
pps[i]=new PatherPoint((int)(x()+a),(int)(y()+b));
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double distanceTo(PatherPoint that) {
|
||||||
|
return this.gp().distanceTo(that.gp());
|
||||||
|
}
|
||||||
|
|
||||||
|
public double effortTo(PatherPoint dest) {
|
||||||
|
if(this.equals(dest) || gp.distanceTo(dest.gp())==0){
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
ElevationProfile evp = new ElevationProfile(pcbean.cDem().get(), gp,
|
||||||
|
gp.azimuthTo(dest.gp()),
|
||||||
|
gp.distanceTo(dest.gp()));
|
||||||
|
|
||||||
|
return evp.effort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object that){
|
||||||
|
if(that instanceof PatherPoint){
|
||||||
|
return((this.x == ((PatherPoint)that).x()) && (this.y == ((PatherPoint)that).y()));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode(){
|
||||||
|
return Objects.hash(x(),y());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
54
Alpano/src/ch/epfl/alpano/gui/PredefinedPanoramas.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum of predefined panoramas
|
||||||
|
*/
|
||||||
|
public enum PredefinedPanoramas {
|
||||||
|
NIESEN(7_6500, 46_7300, 600, 180, 110,"Niesen"),
|
||||||
|
JURA_ALPS(6_8087, 47_0085, 1380, 162, 27,"Jura"),
|
||||||
|
MONT_RACINE(6_8200, 47_0200, 1500, 135, 45,"Mont Racine"),
|
||||||
|
FINSTERAARHORN(8_1260, 46_5374, 4300, 205, 20,"Finsteraarhorn"),
|
||||||
|
SAUVABELIN(6_6385, 46_5353, 700, 135, 100,"Sauvabelin"),
|
||||||
|
PELICAN_BEACH(6_5728, 46_5132, 380, 135, 60,"Pelican Beach");
|
||||||
|
|
||||||
|
|
||||||
|
private PanoramaUserParameters pup;
|
||||||
|
private String name;
|
||||||
|
private static final int WIDTH = 2_500;
|
||||||
|
private static final int HEIGHT = 800;
|
||||||
|
private static final int DISTANCE = 300_000;
|
||||||
|
private static final int SS = 0;
|
||||||
|
/**
|
||||||
|
* Function used to build the predefined panoramas
|
||||||
|
* @param lon : longitude
|
||||||
|
* @param lat : latitude
|
||||||
|
* @param alt : altitude
|
||||||
|
* @param azimuth : azimuth
|
||||||
|
* @param fov : field of view
|
||||||
|
* @return panorama parameters
|
||||||
|
* @see PanoramaUserParameters
|
||||||
|
*/
|
||||||
|
PredefinedPanoramas(int lon, int lat,int alt, int azimuth, int fov, String name) {
|
||||||
|
this.name= name;
|
||||||
|
this.pup = new PanoramaUserParameters(lon, lat, alt, azimuth, fov, DISTANCE,WIDTH,
|
||||||
|
HEIGHT, SS);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return the PanoramaUserParameters of the panorama
|
||||||
|
*/
|
||||||
|
public PanoramaUserParameters get(){
|
||||||
|
return pup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
81
Alpano/src/ch/epfl/alpano/gui/UserParameter.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum of the user parameters with bounds
|
||||||
|
*/
|
||||||
|
public enum UserParameter {
|
||||||
|
OBSERVER_LATITUDE(-80_0000, 80_0000, "Latitude (º) ",4),
|
||||||
|
OBSERVER_LONGITUDE(-160_0000, 160_0000, "Longitude (º) ",4),
|
||||||
|
OBSERVER_ELEVATION(0, 10_000, "Altitude (m) ",0),
|
||||||
|
CENTER_AZIMUTH(0, 359, "Azimuth (º) ",0),
|
||||||
|
HORIZONTAL_FIELD_OF_VIEW(1, 360,"Field of view (º) ",0),
|
||||||
|
MAX_DISTANCE(10_000, 600_000,"View distance (km) ", 3),
|
||||||
|
WIDTH(30, 16_000, "Width (px) ",0),
|
||||||
|
HEIGHT(10, 4_000,"Height (px) ", 0),
|
||||||
|
SUPER_SAMPLING_EXPONENT(0,2,"Supersampling ",0);
|
||||||
|
|
||||||
|
private final String label;
|
||||||
|
private final int scale;
|
||||||
|
private final int min;
|
||||||
|
private final int max;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of user parameters
|
||||||
|
*
|
||||||
|
* @param min
|
||||||
|
* : min bound value
|
||||||
|
* @param max
|
||||||
|
* : max bound value
|
||||||
|
* @param name
|
||||||
|
* : display string
|
||||||
|
* @param scale
|
||||||
|
* : scale for the FixedPointStringConverter
|
||||||
|
*/
|
||||||
|
private UserParameter(int min, int max, String label, int scale) {
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
this.label = label;
|
||||||
|
this.scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scale value for the FixedPointStringConverter
|
||||||
|
*
|
||||||
|
* @return scale value
|
||||||
|
*/
|
||||||
|
public int scale() {
|
||||||
|
return this.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the display text
|
||||||
|
*
|
||||||
|
* @return display text
|
||||||
|
*/
|
||||||
|
public String label() {
|
||||||
|
return this.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int min() {
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int max() {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps the entered value in the bounds
|
||||||
|
*
|
||||||
|
* @param val
|
||||||
|
* : value to check
|
||||||
|
* @return value in the bounds
|
||||||
|
*/
|
||||||
|
public int sanitize(int val) {
|
||||||
|
return ((val < min()) ? min() : (val > max() ? max() : val));
|
||||||
|
}
|
||||||
|
}
|
186
Alpano/src/ch/epfl/alpano/mapzen/MapzenGetter.java
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.mapzen;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Observable;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import static ch.epfl.alpano.dem.HgtDiscreteElevationModel.HGT_SAMPLES;
|
||||||
|
import static ch.epfl.alpano.mapzen.MapzenManager.HGT_FILEPATH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MapzenGetter uses the Mapzen web API to download a HGT File.
|
||||||
|
* @see MapzenManager
|
||||||
|
*/
|
||||||
|
public class MapzenGetter extends Observable implements Runnable{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API Key, Ideal would be to let the user set/use his own instead of ours.
|
||||||
|
*/
|
||||||
|
private final static String API_KEY = "mapzen-viEw7G7";
|
||||||
|
private final static String BASE_URL = "https://tile.mapzen.com/mapzen/terrain/v1/skadi/";
|
||||||
|
private final static String ZIP_EXT = ".gz";
|
||||||
|
private final static int MAX_RETRY = 3;
|
||||||
|
private final String fileName;
|
||||||
|
private final String url;
|
||||||
|
private final String filePath;
|
||||||
|
private final String fileZipPath;
|
||||||
|
private boolean checked = false;
|
||||||
|
private int tries = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of a Getter
|
||||||
|
* @param x : longitude
|
||||||
|
* @param y : latitude
|
||||||
|
*/
|
||||||
|
public MapzenGetter(int x, int y) {
|
||||||
|
this.url = getURL(x,y);
|
||||||
|
this.fileName =getFileName(x,y);
|
||||||
|
this.filePath = HGT_FILEPATH+File.separator+this.fileName;
|
||||||
|
this.fileZipPath = this.filePath+ZIP_EXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the getter by checking, downloading, unziping and notifying manager
|
||||||
|
* @throws UncheckedIOException when an error occurs even after 3 retries
|
||||||
|
*/
|
||||||
|
private void getFile(){
|
||||||
|
if(checked || (this.checkFile()==1)){
|
||||||
|
|
||||||
|
try {
|
||||||
|
download();
|
||||||
|
unzip();
|
||||||
|
new File(fileZipPath).delete();
|
||||||
|
setChanged();
|
||||||
|
notifyObservers();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if(tries<MAX_RETRY){
|
||||||
|
++tries;
|
||||||
|
this.checkFile();
|
||||||
|
getFile();
|
||||||
|
}else{
|
||||||
|
throw new UncheckedIOException("Error with a MapzenGetter after 3 tries...",e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads the file from the Mapzen servers
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void download() throws IOException{
|
||||||
|
|
||||||
|
URL url = new URL(this.url);
|
||||||
|
|
||||||
|
BufferedInputStream bis = new BufferedInputStream(url.openStream());
|
||||||
|
FileOutputStream fis;
|
||||||
|
|
||||||
|
fis = new FileOutputStream(this.fileZipPath);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int count=0;
|
||||||
|
while((count = bis.read(buffer,0,1024)) != -1)
|
||||||
|
{
|
||||||
|
fis.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
fis.close();
|
||||||
|
bis.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unzips the file
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void unzip() throws IOException{
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
|
||||||
|
GZIPInputStream gzips = new GZIPInputStream(new FileInputStream(this.fileZipPath));
|
||||||
|
FileOutputStream out = new FileOutputStream(this.filePath);
|
||||||
|
int len;
|
||||||
|
while ((len = gzips.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
gzips.close();
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the url string for a given longitude,latitude
|
||||||
|
* @param x : longitude
|
||||||
|
* @param y : latitude
|
||||||
|
* @return the url
|
||||||
|
*/
|
||||||
|
private static final String getURL(int x, int y){
|
||||||
|
String yStr = ((y>=0)?"N":"S");
|
||||||
|
int newy = ((y>=0)?y:y*-1);
|
||||||
|
return String.format(BASE_URL+"%s%02d/%s.gz?api_key=%s",
|
||||||
|
yStr,newy,getFileName(x,y),API_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file name for a given longitude,latitude
|
||||||
|
* @param x : longitude
|
||||||
|
* @param y : latitude
|
||||||
|
* @return the file name
|
||||||
|
*/
|
||||||
|
public static final String getFileName(int x, int y){
|
||||||
|
String yStr = ((y>=0)?"N":"S");
|
||||||
|
String xStr = ((x>=0)?"E":"W");
|
||||||
|
x = ((x>=0)?x:x*-1);
|
||||||
|
y = ((y>=0)?y:y*-1);
|
||||||
|
return String.format("%s%02d%s%03d.hgt", yStr,y,xStr,x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the file exists and is valid, also checks if the folder is ok
|
||||||
|
* @return 0 if the file exists and is fine, 0 if it needs to be downloaded
|
||||||
|
*/
|
||||||
|
public int checkFile(){
|
||||||
|
File f = new File(filePath);
|
||||||
|
this.checked = true;
|
||||||
|
|
||||||
|
if(f.exists()&&(f.length()==2*HGT_SAMPLES)){
|
||||||
|
return 0;
|
||||||
|
}else{
|
||||||
|
if(f.exists()){
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
f = new File(fileZipPath);
|
||||||
|
if(f.exists()){
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
if(!new File(HGT_FILEPATH).isDirectory())
|
||||||
|
new File(HGT_FILEPATH).mkdirs();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
getFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts this MapzenGetter on a new thread
|
||||||
|
*/
|
||||||
|
public void start(){
|
||||||
|
Thread thread = new Thread(this);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
191
Alpano/src/ch/epfl/alpano/mapzen/MapzenManager.java
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.mapzen;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Observable;
|
||||||
|
import java.util.Observer;
|
||||||
|
import ch.epfl.alpano.PanoramaParameters;
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.DiscreteElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.HgtDiscreteElevationModel;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MapzenManager Manages a list of MapzenGetter, observing them, starting them,...
|
||||||
|
* Used to download any HGT file for the entire earth.
|
||||||
|
* Supports multiple MapzenGetters on their own thread.
|
||||||
|
*/
|
||||||
|
public final class MapzenManager implements Observer{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path the the folder containing the HGT Files
|
||||||
|
*/
|
||||||
|
public static final String HGT_FILEPATH = "HGT";
|
||||||
|
private static final int MULTI_DL = 3;
|
||||||
|
private static final int HGT_RADIUS = 2;
|
||||||
|
private static final String DEFAULT_TEXT = "HGT File Downloader Startings ...";
|
||||||
|
|
||||||
|
private int total;
|
||||||
|
private static ArrayList<MapzenGetter> getters = new ArrayList<MapzenGetter>();
|
||||||
|
private ArrayList<MapzenGetter> gettersDL = new ArrayList<MapzenGetter>();
|
||||||
|
private int latm, latM, lonm, lonM;
|
||||||
|
private static ObjectProperty<ContinuousElevationModel> cDEM = new SimpleObjectProperty<ContinuousElevationModel>();
|
||||||
|
private static ObjectProperty<String> text = new SimpleObjectProperty<String>(DEFAULT_TEXT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the parameters,
|
||||||
|
* adds the required files in an area of 4*4 around the observer
|
||||||
|
*
|
||||||
|
* @param params : the new parameters
|
||||||
|
*/
|
||||||
|
public void update(PanoramaParameters params){
|
||||||
|
|
||||||
|
double lon = Math.toDegrees(params.observerPosition().longitude());
|
||||||
|
double lat = Math.toDegrees(params.observerPosition().latitude());
|
||||||
|
this.lonm = (int)Math.round(lon-HGT_RADIUS);
|
||||||
|
this.lonM = (int)Math.round(lon+HGT_RADIUS);
|
||||||
|
this.latm = (int)Math.round(lat-HGT_RADIUS);
|
||||||
|
this.latM = (int)Math.round(lat+HGT_RADIUS);
|
||||||
|
for(int i = lonm ; i<lonM; ++i){
|
||||||
|
for(int j = latm ; j<latM; ++j){
|
||||||
|
addGetter(new MapzenGetter(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.total = getters.size();
|
||||||
|
this.check();
|
||||||
|
this.dl();
|
||||||
|
if(isDone())
|
||||||
|
cDEM.set(getDEM(lonm, lonM, latm, latM));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a MapzenGetter to the list of getters
|
||||||
|
* @param getter
|
||||||
|
*/
|
||||||
|
private void addGetter(MapzenGetter getter){
|
||||||
|
getter.addObserver(this);
|
||||||
|
getters.add(getter);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Removes a MapzenGetter from the list
|
||||||
|
* @param getter
|
||||||
|
*/
|
||||||
|
public void removeGetter(MapzenGetter getter){
|
||||||
|
getters.remove(getter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks all the MapzenGetters
|
||||||
|
* Removes those who already exists from the download list
|
||||||
|
*/
|
||||||
|
public void check(){
|
||||||
|
ArrayList<MapzenGetter> toRemove = new ArrayList<MapzenGetter>();
|
||||||
|
for(MapzenGetter getter: getters){
|
||||||
|
if(getter.checkFile()==0)
|
||||||
|
toRemove.add(getter);
|
||||||
|
}
|
||||||
|
for(MapzenGetter getter: toRemove){
|
||||||
|
getters.remove(getter);
|
||||||
|
}
|
||||||
|
text.set("EXISTING : "+toRemove.size()+"\nMISSING :"+getters.size()+"\n(total:"+total+")");
|
||||||
|
total = getters.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start download on multiple threads to improve speed
|
||||||
|
*/
|
||||||
|
public void dl(){
|
||||||
|
MapzenGetter gt;
|
||||||
|
if(getters.size()>0){
|
||||||
|
while(gettersDL.size()<=MULTI_DL && getters.size()>0){
|
||||||
|
gt = getters.get(0);
|
||||||
|
getters.remove(gt);
|
||||||
|
gettersDL.add(gt);
|
||||||
|
gt.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return current amount of downloaded files
|
||||||
|
*/
|
||||||
|
public int getProgress(){
|
||||||
|
return total-(getters.size()+gettersDL.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true downloads are done
|
||||||
|
*/
|
||||||
|
public boolean isDone(){
|
||||||
|
return ((getters.size()==0) && (gettersDL.size()==0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Observable o, Object arg) {
|
||||||
|
|
||||||
|
gettersDL.remove((MapzenGetter)o);
|
||||||
|
text.set(String.format((Locale) null,"DOWNLOAD PROGRESS: %d / %d",getProgress(),total));
|
||||||
|
if(getters.size()>0)
|
||||||
|
dl();
|
||||||
|
if(isDone()){
|
||||||
|
text.set(String.format((Locale) null,"DOWNLOAD COMPLETED",getProgress(),total));
|
||||||
|
cDEM.set(getDEM(lonm, lonM, latm, latM));
|
||||||
|
text.set(String.format((Locale) null,DEFAULT_TEXT,getProgress(),total));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return ContinuousElevationModel object property
|
||||||
|
*/
|
||||||
|
public ReadOnlyObjectProperty<ContinuousElevationModel> cDEM(){
|
||||||
|
return cDEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return text object property used to display progress and information
|
||||||
|
*/
|
||||||
|
public ReadOnlyObjectProperty<String> text(){
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ContinuousElevatyionModel using a 2D range of longitude/latitudes
|
||||||
|
* @param lonm : min longitude
|
||||||
|
* @param lonM : max longitude
|
||||||
|
* @param latm : min latitude
|
||||||
|
* @param latM : max latitude
|
||||||
|
* @return a ContinuousElevationModel of the defined area using the downloaded HGT Files
|
||||||
|
*/
|
||||||
|
private final static ContinuousElevationModel getDEM (double lonm, double lonM, double latm, double latM){
|
||||||
|
DiscreteElevationModel DEM1 = null;
|
||||||
|
DiscreteElevationModel DEM2 = null;
|
||||||
|
String fileName;
|
||||||
|
File file;
|
||||||
|
for (int i = (int) lonm; i < lonM; ++i) {
|
||||||
|
for (int j =(int) latm; j < latM; ++j) {
|
||||||
|
fileName = MapzenGetter.getFileName(i,j);
|
||||||
|
file = new File(HGT_FILEPATH+File.separator+fileName);
|
||||||
|
if (DEM1 == null)
|
||||||
|
DEM1 = new HgtDiscreteElevationModel(file);
|
||||||
|
DEM1 = new HgtDiscreteElevationModel(file).union(DEM1);
|
||||||
|
}
|
||||||
|
if (DEM2 == null)
|
||||||
|
DEM2 = DEM1;
|
||||||
|
DEM2 = DEM1.union(DEM2);
|
||||||
|
DEM1 = null;
|
||||||
|
}
|
||||||
|
return new ContinuousElevationModel(DEM2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
128
Alpano/src/ch/epfl/alpano/summit/GazetteerParser.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.summit;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class parsing the data from a file and returns a list of the Summits it
|
||||||
|
* contains
|
||||||
|
*
|
||||||
|
* @see Summit
|
||||||
|
*/
|
||||||
|
public class GazetteerParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regex Pattern of a line in a Gazetter file
|
||||||
|
*/
|
||||||
|
private static final Pattern P = Pattern.compile(
|
||||||
|
"[ ]+(\\d{1,3}):(\\d{1,3}):(\\d{1,3})[ ]+(\\d{1,2}):(\\d{1,2}):(\\d{1,2})[ ]+(\\d{1,4})[ ]+([A-Z]\\d{0,2})[ ]+([A-Z]\\d{1,2})[ ]+([A-Z]{0,2}\\d{0,2})[ ]+(.*)");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor so GazetteerParser can't be instantiated
|
||||||
|
*/
|
||||||
|
private GazetteerParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a List with the Summits data extracted from a Gazetteer file.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* : the file to read
|
||||||
|
* @throws IOException
|
||||||
|
* if there is a problem with reading the file or a line
|
||||||
|
*/
|
||||||
|
public static List<Summit> readSummitsFrom(File file) throws IOException {
|
||||||
|
ArrayList<Summit> output = new ArrayList<Summit>();
|
||||||
|
String line;
|
||||||
|
Matcher m;
|
||||||
|
double lon = 0, lat = 0;
|
||||||
|
try {
|
||||||
|
FileInputStream s = new FileInputStream(file);
|
||||||
|
BufferedReader b = new BufferedReader(
|
||||||
|
new InputStreamReader(s, StandardCharsets.US_ASCII));
|
||||||
|
while ((line = b.readLine()) != null) {
|
||||||
|
m = checkLine(line);
|
||||||
|
lon = CompositeDegreeToRadians(m.group(1), m.group(2),
|
||||||
|
m.group(3));
|
||||||
|
lat = CompositeDegreeToRadians(m.group(4), m.group(5),
|
||||||
|
m.group(6));
|
||||||
|
output.add(new Summit(m.group(11), new GeoPoint(lon, lat),
|
||||||
|
Integer.parseInt(m.group(7))));
|
||||||
|
}
|
||||||
|
b.close();
|
||||||
|
s.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException();
|
||||||
|
} catch (NumberFormatException e){
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks a line using the Regex Pattern P
|
||||||
|
*
|
||||||
|
* @param txt
|
||||||
|
* : the string to analyze
|
||||||
|
* @return Matcher m with the regex applied
|
||||||
|
* @throws IOException
|
||||||
|
* if the line format does not match
|
||||||
|
*/
|
||||||
|
private static final Matcher checkLine(String txt) throws IOException {
|
||||||
|
Matcher m = P.matcher(txt);
|
||||||
|
if (!m.find()) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts separate degrees, minutes, seconds to radians
|
||||||
|
*
|
||||||
|
* @param deg
|
||||||
|
* : the degree value
|
||||||
|
* @param min
|
||||||
|
* : the minute value
|
||||||
|
* @param sec
|
||||||
|
* : the second value
|
||||||
|
* @return the angle in radians
|
||||||
|
*/
|
||||||
|
private static final double CompositeDegreeToRadians(int deg, int min,
|
||||||
|
int sec) {
|
||||||
|
double angle = deg * 1.0 + min / 60.0 + sec / (3600.0);
|
||||||
|
return Math.toRadians(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts separate degrees, minutes, seconds as strings to radians
|
||||||
|
*
|
||||||
|
* @param deg
|
||||||
|
* : the degree value as a string
|
||||||
|
* @param min
|
||||||
|
* : the minute value as a string
|
||||||
|
* @param sec
|
||||||
|
* : the second value as a string
|
||||||
|
* @return angle in radians
|
||||||
|
* @see CompositeDegreeToRadians(int deg, int min, int sec)
|
||||||
|
*/
|
||||||
|
private static final double CompositeDegreeToRadians(String deg, String min,
|
||||||
|
String sec) {
|
||||||
|
return CompositeDegreeToRadians(Integer.parseInt(deg),
|
||||||
|
Integer.parseInt(min), Integer.parseInt(sec));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
71
Alpano/src/ch/epfl/alpano/summit/Summit.java
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Author: Cedric Holzl - Mohamed Khadri
|
||||||
|
* Sciper: 257844 - 261203
|
||||||
|
*/
|
||||||
|
package ch.epfl.alpano.summit;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class containing the information of a summit
|
||||||
|
*/
|
||||||
|
public final class Summit {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final GeoPoint position;
|
||||||
|
private final int elevation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for a Summit
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* : the summit name
|
||||||
|
* @param position
|
||||||
|
* : the summit position
|
||||||
|
* @param elevation
|
||||||
|
* : the summit altitude
|
||||||
|
* @throw NullPointerException if name or position are null
|
||||||
|
*/
|
||||||
|
public Summit(String name, GeoPoint position, int elevation) {
|
||||||
|
Objects.requireNonNull(name);
|
||||||
|
Objects.requireNonNull(position);
|
||||||
|
this.elevation = elevation;
|
||||||
|
this.name = name;
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The summit's name
|
||||||
|
*
|
||||||
|
* @return the name of the summit
|
||||||
|
*/
|
||||||
|
public String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The geoPoint's position
|
||||||
|
*
|
||||||
|
* @return the position of this geoPoint
|
||||||
|
*/
|
||||||
|
public GeoPoint position() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The geoPoint's elevation
|
||||||
|
*
|
||||||
|
* @return the altitude of the summit
|
||||||
|
*/
|
||||||
|
public int elevation() {
|
||||||
|
return elevation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name + " " + position.toString() + " " + elevation;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
167
Alpano/tests/ch/epfl/alpano/AzimuthTest.java
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.alpano.Azimuth.canonicalize;
|
||||||
|
import static ch.epfl.alpano.Azimuth.fromMath;
|
||||||
|
import static ch.epfl.alpano.Azimuth.isCanonical;
|
||||||
|
import static ch.epfl.alpano.Azimuth.toMath;
|
||||||
|
import static ch.epfl.alpano.Azimuth.toOctantString;
|
||||||
|
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
import static java.lang.Math.floorMod;
|
||||||
|
import static java.lang.Math.nextDown;
|
||||||
|
import static java.lang.Math.round;
|
||||||
|
import static java.lang.Math.scalb;
|
||||||
|
import static java.lang.Math.toDegrees;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class AzimuthTest {
|
||||||
|
@Test
|
||||||
|
public void isCanonicalIsTrueFor0() {
|
||||||
|
assertTrue(isCanonical(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isCanonicalIsFalseFor0Pred() {
|
||||||
|
assertFalse(isCanonical(nextDown(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isCanonicalIsTrueFor2PiPred() {
|
||||||
|
assertTrue(isCanonical(nextDown(scalb(PI, 1))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isCanonicalIsFalseFor2Pi() {
|
||||||
|
assertFalse(isCanonical(scalb(PI, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isCanonicalIsTrueForRandomCanonicalAzimuths() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i)
|
||||||
|
assertTrue(isCanonical(rng.nextDouble() * scalb(PI, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canonicalizeCorrectlyCanonicalizesRoundedRandomAngles() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
int aDeg = rng.nextInt(10_000) - 5_000;
|
||||||
|
double aRad = toRadians(aDeg);
|
||||||
|
double canonicalARad = canonicalize(aRad);
|
||||||
|
assertTrue(0 <= canonicalARad && canonicalARad < scalb(PI, 1));
|
||||||
|
int canonicalADeg = (int)round(toDegrees(canonicalARad));
|
||||||
|
if (canonicalADeg == 360)
|
||||||
|
canonicalADeg = 0;
|
||||||
|
assertEquals(floorMod(aDeg, 360), canonicalADeg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toMathCorrectlyHandles0() {
|
||||||
|
assertEquals(0d, toMath(0d), 0d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromMathCorrectlyHandles0() {
|
||||||
|
assertEquals(0d, fromMath(0d), 0d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toMathWorksForKnownValues() {
|
||||||
|
int[] vs = new int[] {
|
||||||
|
0, 0,
|
||||||
|
1,359,
|
||||||
|
2,358,
|
||||||
|
358,2,
|
||||||
|
359,1,
|
||||||
|
90, 270,
|
||||||
|
180, 180,
|
||||||
|
179,181,
|
||||||
|
181,179,
|
||||||
|
270, 90
|
||||||
|
};
|
||||||
|
for (int i = 0; i < vs.length; i += 2) {
|
||||||
|
double a = toMath(toRadians(vs[i]));
|
||||||
|
assertEquals(toRadians(vs[i+1]), a, 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fromMathWorksForKnownValues() {
|
||||||
|
int[] vs = new int[] {
|
||||||
|
0, 0,
|
||||||
|
1,359,
|
||||||
|
2,358,
|
||||||
|
358,2,
|
||||||
|
359,1,
|
||||||
|
90, 270,
|
||||||
|
180, 180,
|
||||||
|
179,181,
|
||||||
|
181,179,
|
||||||
|
270, 90
|
||||||
|
};
|
||||||
|
for (int i = 0; i < vs.length; i += 2) {
|
||||||
|
double a = fromMath(toRadians(vs[i]));
|
||||||
|
assertEquals(toRadians(vs[i+1]), a, 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toMathAndFromMathAreInverseForRandomValues() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double a = rng.nextDouble() * scalb(PI, 1);
|
||||||
|
double a2 = fromMath(toMath(a));
|
||||||
|
assertEquals(a, a2, 1e-10);
|
||||||
|
|
||||||
|
double a3 = toMath(fromMath(a));
|
||||||
|
assertEquals(a, a3, 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = java.lang.IllegalArgumentException.class)
|
||||||
|
public void toMathThrowsFor2Pi() {
|
||||||
|
toMath(scalb(PI, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = java.lang.IllegalArgumentException.class)
|
||||||
|
public void fromMathThrowsFor2Pi() {
|
||||||
|
fromMath(scalb(PI, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void toOctantStringThrowsForNonCanonicalAzimuth() {
|
||||||
|
toOctantString(-1, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toOctantStringCorrectlyCyclesThroughValues() {
|
||||||
|
String n = "north", e = "east", s = "south", w = "west";
|
||||||
|
ArrayList<String> expected = new ArrayList<>();
|
||||||
|
expected.addAll(Collections.nCopies(45, n));
|
||||||
|
expected.addAll(Collections.nCopies(45, n+e));
|
||||||
|
expected.addAll(Collections.nCopies(45, e));
|
||||||
|
expected.addAll(Collections.nCopies(45, s+e));
|
||||||
|
expected.addAll(Collections.nCopies(45, s));
|
||||||
|
expected.addAll(Collections.nCopies(45, s+w));
|
||||||
|
expected.addAll(Collections.nCopies(45, w));
|
||||||
|
expected.addAll(Collections.nCopies(45, n+w));
|
||||||
|
|
||||||
|
for (int aDeg = 0; aDeg < 360; ++aDeg) {
|
||||||
|
double aRad = toRadians(floorMod(aDeg - 22, 360));
|
||||||
|
String os = toOctantString(aRad, n, e, s, w);
|
||||||
|
assertEquals(expected.get(aDeg), os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
Alpano/tests/ch/epfl/alpano/DistanceTest.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
import static java.lang.Math.scalb;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class DistanceTest {
|
||||||
|
private static double EARTH_CIRCUMFERENCE = 40_030_174; // rounded to nearest integer
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toRadiansAndToMetersAreInverseForRandomValues() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double dRad = rng.nextDouble() * scalb(PI, 1);
|
||||||
|
double dRad2 = Distance.toRadians(Distance.toMeters(dRad));
|
||||||
|
assertEquals(dRad, dRad2, 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toMetersIsCorrectForKnownValues() {
|
||||||
|
assertEquals(0, Distance.toMeters(0), 0);
|
||||||
|
assertEquals(EARTH_CIRCUMFERENCE, Distance.toMeters(scalb(PI, 1)), 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toRadiansIsCorrectForKnownValues() {
|
||||||
|
assertEquals(0, Distance.toRadians(0), 0);
|
||||||
|
assertEquals(scalb(PI, 1), Distance.toRadians(EARTH_CIRCUMFERENCE), 1e-5);
|
||||||
|
}
|
||||||
|
}
|
71
Alpano/tests/ch/epfl/alpano/DrawPanorama.java
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.DiscreteElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.HgtDiscreteElevationModel;
|
||||||
|
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
final class DrawPanorama {
|
||||||
|
final static File HGT_FILE = new File("HGT"+File.separatorChar+"N46E007.hgt");
|
||||||
|
|
||||||
|
final static int IMAGE_WIDTH = 500;
|
||||||
|
final static int IMAGE_HEIGHT = 200;
|
||||||
|
|
||||||
|
final static double ORIGIN_LON = toRadians(7.65);
|
||||||
|
final static double ORIGIN_LAT = toRadians(46.73);
|
||||||
|
final static int ELEVATION = 600;
|
||||||
|
final static double CENTER_AZIMUTH = toRadians(180);
|
||||||
|
final static double HORIZONTAL_FOV = toRadians(60);
|
||||||
|
final static int MAX_DISTANCE = 100_000;
|
||||||
|
|
||||||
|
final static PanoramaParameters PARAMS =
|
||||||
|
new PanoramaParameters(new GeoPoint(ORIGIN_LON,
|
||||||
|
ORIGIN_LAT),
|
||||||
|
ELEVATION,
|
||||||
|
CENTER_AZIMUTH,
|
||||||
|
HORIZONTAL_FOV,
|
||||||
|
MAX_DISTANCE,
|
||||||
|
IMAGE_WIDTH,
|
||||||
|
IMAGE_HEIGHT);
|
||||||
|
|
||||||
|
public static void main(String[] as) throws Exception {
|
||||||
|
try (DiscreteElevationModel dDEM =
|
||||||
|
new HgtDiscreteElevationModel(HGT_FILE)) {
|
||||||
|
ContinuousElevationModel cDEM =
|
||||||
|
new ContinuousElevationModel(dDEM);
|
||||||
|
Panorama p = new PanoramaComputer(cDEM)
|
||||||
|
.computePanorama(PARAMS);
|
||||||
|
|
||||||
|
BufferedImage i =
|
||||||
|
new BufferedImage(IMAGE_WIDTH,
|
||||||
|
IMAGE_HEIGHT,
|
||||||
|
BufferedImage.TYPE_INT_RGB);
|
||||||
|
|
||||||
|
for (int x = 0; x < IMAGE_WIDTH; ++x) {
|
||||||
|
for (int y = 0; y < IMAGE_HEIGHT; ++y) {
|
||||||
|
float d = p.distanceAt(x, y);
|
||||||
|
int c = (d == Float.POSITIVE_INFINITY)
|
||||||
|
? 0x87_CE_EB
|
||||||
|
: gray((d - 2_000) / 15_000);
|
||||||
|
i.setRGB(x, y, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageIO.write(i, "png", new File("tests/ch/epfl/alpano/niesen.png"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static int gray(double v) {
|
||||||
|
double clampedV = max(0, min(v, 1));
|
||||||
|
int gray = (int) (255.9999 * clampedV);
|
||||||
|
return (gray << 16) | (gray << 8) | gray;
|
||||||
|
}
|
||||||
|
}
|
66
Alpano/tests/ch/epfl/alpano/DrawPanoramaNew.java
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.DiscreteElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.HgtDiscreteElevationModel;
|
||||||
|
import ch.epfl.alpano.gui.ChannelPainter;
|
||||||
|
import ch.epfl.alpano.gui.ImagePainter;
|
||||||
|
import ch.epfl.alpano.gui.PanoramaRenderer;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
|
||||||
|
final class DrawPanoramaNew {
|
||||||
|
final static File HGT_FILE = new File("HGT"+File.separatorChar+"N46E007.hgt");
|
||||||
|
|
||||||
|
final static int IMAGE_WIDTH = 2_500;
|
||||||
|
final static int IMAGE_HEIGHT = 800;
|
||||||
|
|
||||||
|
final static double ORIGIN_LON = toRadians(7.65);
|
||||||
|
final static double ORIGIN_LAT = toRadians(46.73);
|
||||||
|
final static int ELEVATION = 600;
|
||||||
|
final static double CENTER_AZIMUTH = toRadians(180);
|
||||||
|
final static double HORIZONTAL_FOV = toRadians(110);
|
||||||
|
final static int MAX_DISTANCE = 100_000;
|
||||||
|
|
||||||
|
final static PanoramaParameters PARAMS =
|
||||||
|
new PanoramaParameters(new GeoPoint(ORIGIN_LON,
|
||||||
|
ORIGIN_LAT),
|
||||||
|
ELEVATION,
|
||||||
|
CENTER_AZIMUTH,
|
||||||
|
HORIZONTAL_FOV,
|
||||||
|
MAX_DISTANCE,
|
||||||
|
IMAGE_WIDTH,
|
||||||
|
IMAGE_HEIGHT);
|
||||||
|
|
||||||
|
public static void main(String[] as) throws Exception {
|
||||||
|
try (DiscreteElevationModel dDEM =
|
||||||
|
new HgtDiscreteElevationModel(HGT_FILE)) {
|
||||||
|
ContinuousElevationModel cDEM =
|
||||||
|
new ContinuousElevationModel(dDEM);
|
||||||
|
Panorama p = new PanoramaComputer(cDEM)
|
||||||
|
.computePanorama(PARAMS);
|
||||||
|
|
||||||
|
ChannelPainter d = p::distanceAt;
|
||||||
|
ChannelPainter sl = p::slopeAt;
|
||||||
|
ChannelPainter h = d.div(100000).cycling().mul(360);
|
||||||
|
ChannelPainter s = d.div(200000).clamped().inverted();
|
||||||
|
ChannelPainter b = sl.mul(2).div(Math.PI).inverted().mul(0.7).add(0.3);
|
||||||
|
ChannelPainter o =
|
||||||
|
d.map(dist -> dist == Float.POSITIVE_INFINITY ? 0 : 1);
|
||||||
|
|
||||||
|
ImagePainter l = ImagePainter.hsb(h, s, b, o);
|
||||||
|
|
||||||
|
Image i = PanoramaRenderer.renderPanorama(p, l);
|
||||||
|
ImageIO.write(SwingFXUtils.fromFXImage(i, null),
|
||||||
|
"png",
|
||||||
|
new File("tests/ch/epfl/alpano/niesen-profile.png"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
63
Alpano/tests/ch/epfl/alpano/DrawPanoramaNew2.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.DiscreteElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.HgtDiscreteElevationModel;
|
||||||
|
import ch.epfl.alpano.gui.ChannelPainter;
|
||||||
|
import ch.epfl.alpano.gui.ImagePainter;
|
||||||
|
import ch.epfl.alpano.gui.PanoramaRenderer;
|
||||||
|
import ch.epfl.alpano.gui.PanoramaUserParameters;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import ch.epfl.alpano.gui.PredefinedPanoramas;
|
||||||
|
|
||||||
|
final class DrawPanoramaNew2 {
|
||||||
|
|
||||||
|
public static void main(String[] as) throws Exception {
|
||||||
|
PanoramaUserParameters pano = PredefinedPanoramas.JURA_ALPS.get();
|
||||||
|
|
||||||
|
ContinuousElevationModel cDEM =generatePredefinedCDEM("./HGT/");
|
||||||
|
Panorama p = new PanoramaComputer(cDEM)
|
||||||
|
.computePanorama(pano.panoramaComputeParameters());
|
||||||
|
|
||||||
|
ChannelPainter d = p::distanceAt;
|
||||||
|
ChannelPainter sl = p::slopeAt;
|
||||||
|
ChannelPainter h = d.div(100000).cycling().mul(360);
|
||||||
|
ChannelPainter s = d.div(200000).clamped().inverted();
|
||||||
|
ChannelPainter b = sl.mul(2).div(Math.PI).inverted().mul(0.7).add(0.3);
|
||||||
|
ChannelPainter o =
|
||||||
|
d.map(dist -> dist == Float.POSITIVE_INFINITY ? 0 : 1);
|
||||||
|
|
||||||
|
ImagePainter l = ImagePainter.hsb(h, s, b, o);
|
||||||
|
|
||||||
|
Image i = PanoramaRenderer.renderPanorama(p, l);
|
||||||
|
ImageIO.write(SwingFXUtils.fromFXImage(i, null),
|
||||||
|
"png",
|
||||||
|
new File("tests/ch/epfl/alpano/niesen-profile.png"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ContinuousElevationModel generatePredefinedCDEM(String path){
|
||||||
|
|
||||||
|
DiscreteElevationModel DEM1=null;
|
||||||
|
DiscreteElevationModel DEM2=null;
|
||||||
|
File file;
|
||||||
|
for(int i=6;i<=11;++i){
|
||||||
|
for(int j=45;j<=47;++j){
|
||||||
|
file = new File(String.format((Locale) null, path+"N%02dE%03d.hgt",j,i));
|
||||||
|
if(DEM1==null)
|
||||||
|
DEM1 = new HgtDiscreteElevationModel(file);
|
||||||
|
DEM1 = new HgtDiscreteElevationModel(file).union(DEM1);
|
||||||
|
}
|
||||||
|
if(DEM2==null)
|
||||||
|
DEM2=DEM1;
|
||||||
|
DEM2=DEM1.union(DEM2);
|
||||||
|
DEM1=null;
|
||||||
|
}
|
||||||
|
return new ContinuousElevationModel(DEM2);
|
||||||
|
}
|
||||||
|
}
|
68
Alpano/tests/ch/epfl/alpano/GeoPointTest.java
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class GeoPointTest {
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorLimitTest1(){
|
||||||
|
new GeoPoint(0, 2*Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorLimitTest2(){
|
||||||
|
new GeoPoint(2*Math.PI, 2*Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorLimitTest3(){
|
||||||
|
new GeoPoint(2*Math.PI, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void distanceToTest1(){
|
||||||
|
GeoPoint london = new GeoPoint(Math.toRadians(6.631), Math.toRadians(46.521));
|
||||||
|
GeoPoint moscow = new GeoPoint( Math.toRadians(37.623), Math.toRadians(55.753));
|
||||||
|
assertEquals(2367000, london.distanceTo(moscow), 1000);//1km imprecision
|
||||||
|
assertEquals(0,london.azimuthTo(london),0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void distanceToTest2(){
|
||||||
|
GeoPoint epfl = new GeoPoint(Math.toRadians(6.56730), Math.toRadians(46.51781));
|
||||||
|
GeoPoint eiler = new GeoPoint( Math.toRadians(8.00537), Math.toRadians(46.57756));
|
||||||
|
assertEquals(110294, epfl.distanceTo(eiler), 150);//150M imprecision
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void distanceToTest3(){
|
||||||
|
GeoPoint p1 = new GeoPoint(Math.PI,Math.PI/2.0);
|
||||||
|
GeoPoint p2 = new GeoPoint(Math.PI,Math.PI/2.0);
|
||||||
|
GeoPoint p3 = new GeoPoint(0,Math.PI/2.0);
|
||||||
|
GeoPoint p4 = new GeoPoint(Math.PI/10.0,Math.PI/2.0);
|
||||||
|
assertEquals(p3.distanceTo(p4), p4.distanceTo(p3), 10);
|
||||||
|
assertEquals(p2.distanceTo(p1), p1.distanceTo(p2), 10);
|
||||||
|
assertEquals(p3.distanceTo(p4), p1.distanceTo(p2), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void distanceToTest4(){
|
||||||
|
GeoPoint p1 = new GeoPoint(Math.PI/2.0,Math.PI/2.0);
|
||||||
|
GeoPoint p2 = new GeoPoint(-Math.PI/2.0,Math.PI/2.0);
|
||||||
|
GeoPoint p3 = new GeoPoint(-Math.PI/2.0,Math.PI/2.0);
|
||||||
|
GeoPoint p4 = new GeoPoint(Math.PI/10.0,Math.PI/2.0);
|
||||||
|
assertEquals(p3.distanceTo(p4), p4.distanceTo(p3), 10);
|
||||||
|
assertEquals(p2.distanceTo(p1), p1.distanceTo(p2), 10);
|
||||||
|
assertEquals(p3.distanceTo(p4), p1.distanceTo(p2), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toStringTest(){
|
||||||
|
GeoPoint g = new GeoPoint(Math.toRadians(-7.6543), Math.toRadians(54.3210));
|
||||||
|
assertEquals("(-7.6543,54.3210)", g.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
Alpano/tests/ch/epfl/alpano/GeoPointTestP.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static java.lang.Math.toDegrees;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class GeoPointTestP {
|
||||||
|
private static GeoPoint CORNAVIN = new GeoPoint(toRadians(6.14308), toRadians(46.21023));
|
||||||
|
private static GeoPoint M1_EPFL = new GeoPoint(toRadians(6.56599), toRadians(46.52224));
|
||||||
|
private static GeoPoint FEDERAL_PALACE = new GeoPoint(toRadians(7.44428), toRadians(46.94652));
|
||||||
|
private static GeoPoint SAENTIS = new GeoPoint(toRadians(9.34324), toRadians(47.24942));
|
||||||
|
private static GeoPoint MONTE_TAMARO = new GeoPoint(toRadians(8.86598), toRadians(46.10386));
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void distanceToWorksOnKnownPoints() {
|
||||||
|
assertEquals(226_000, M1_EPFL.distanceTo(SAENTIS), 10);
|
||||||
|
assertEquals( 81_890, M1_EPFL.distanceTo(FEDERAL_PALACE), 10);
|
||||||
|
assertEquals(143_560, FEDERAL_PALACE.distanceTo(MONTE_TAMARO), 10);
|
||||||
|
assertEquals(269_870, SAENTIS.distanceTo(CORNAVIN), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void azimuthToWorksOnKnownPoints() {
|
||||||
|
assertEquals( 68.03, toDegrees(M1_EPFL.azimuthTo(SAENTIS)), 0.01);
|
||||||
|
assertEquals( 54.50, toDegrees(M1_EPFL.azimuthTo(FEDERAL_PALACE)), 0.01);
|
||||||
|
assertEquals(130.23, toDegrees(FEDERAL_PALACE.azimuthTo(MONTE_TAMARO)), 0.01);
|
||||||
|
assertEquals(245.82, toDegrees(SAENTIS.azimuthTo(CORNAVIN)), 0.01);
|
||||||
|
}
|
||||||
|
}
|
257
Alpano/tests/ch/epfl/alpano/Interval1DTest.java
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class Interval1DTest {
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testRandomIncludedFromAndToFails() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
int a,b;
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i){
|
||||||
|
a = rng.nextInt();
|
||||||
|
b = rng.nextInt();
|
||||||
|
if(a>b){
|
||||||
|
new Interval1D(a,b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRandomIncludedFromAndTo() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
Interval1D interval;
|
||||||
|
int a,b;
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i){
|
||||||
|
a = rng.nextInt();
|
||||||
|
b = rng.nextInt();
|
||||||
|
if(a<=b){
|
||||||
|
interval = new Interval1D(a,b);
|
||||||
|
assertEquals(interval.includedFrom(),a,0);
|
||||||
|
assertEquals(interval.includedTo(),b,0);
|
||||||
|
assertEquals(interval.size(),(b-a+1),0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLimitsIncludedFromAndTo() {
|
||||||
|
|
||||||
|
Interval1D interval = new Interval1D(0,0);
|
||||||
|
assertEquals(interval.includedFrom(),0,0);
|
||||||
|
assertEquals(interval.includedTo(),0,0);
|
||||||
|
assertEquals(interval.size(),1,0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContainsTrue() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
Interval1D interval;
|
||||||
|
int a,b,m;
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i){
|
||||||
|
a = rng.nextInt(500);
|
||||||
|
b = rng.nextInt(500);
|
||||||
|
|
||||||
|
if(a<=b){
|
||||||
|
m = (b+a)/2;
|
||||||
|
interval = new Interval1D(a,b);
|
||||||
|
assertTrue(interval.contains(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testContainsFails() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
Interval1D interval;
|
||||||
|
int a,b,c;
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i){
|
||||||
|
a = rng.nextInt(500);
|
||||||
|
b = rng.nextInt(500);
|
||||||
|
c = rng.nextInt(500);
|
||||||
|
if(a<=b && (c<a ||c>b)){
|
||||||
|
interval = new Interval1D(a,b);
|
||||||
|
assertFalse(interval.contains(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeOfIntersectionWith() {
|
||||||
|
|
||||||
|
Interval1D interval1;
|
||||||
|
Interval1D interval2;
|
||||||
|
int num[] = {
|
||||||
|
1,1,0,2,1,
|
||||||
|
1,2,1,5,2,
|
||||||
|
1,4,0,2,2,
|
||||||
|
1,4,0,1,1,
|
||||||
|
2,4,0,1,0
|
||||||
|
};
|
||||||
|
for (int i = 0; i < num.length; i += 5) {
|
||||||
|
interval1 = new Interval1D(num[i],num[i+1]);
|
||||||
|
interval2 = new Interval1D(num[i+2],num[i+3]);
|
||||||
|
assertEquals(interval1.sizeOfIntersectionWith(interval2), interval2.sizeOfIntersectionWith(interval1));
|
||||||
|
assertEquals(interval1.sizeOfIntersectionWith(interval2), num[i+4], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBoundingUnion() {
|
||||||
|
Interval1D interval1;
|
||||||
|
Interval1D interval2;
|
||||||
|
Interval1D interval3;
|
||||||
|
int num[] = {
|
||||||
|
1,1,0,2,0,2,
|
||||||
|
1,1,1,1,1,1,
|
||||||
|
0,0,0,0,0,0,
|
||||||
|
0,0,1,1,0,1,
|
||||||
|
0,1,0,1,0,1,
|
||||||
|
0,5,1,1,0,5,
|
||||||
|
0,4,1,5,0,5,
|
||||||
|
1,5,0,4,0,5,
|
||||||
|
1,4,0,5,0,5,
|
||||||
|
0,0,9,9,0,9
|
||||||
|
};
|
||||||
|
for (int i = 0; i < num.length; i += 6) {
|
||||||
|
interval1 = new Interval1D(num[i],num[i+1]);
|
||||||
|
interval2 = new Interval1D(num[i+2],num[i+3]);
|
||||||
|
interval3 = new Interval1D(num[i+4],num[i+5]);
|
||||||
|
assertTrue(interval1.boundingUnion(interval2).equals(interval3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsUnionableWithTrue() {
|
||||||
|
Interval1D interval1;
|
||||||
|
Interval1D interval2;
|
||||||
|
int num[] = {
|
||||||
|
1,1,0,2,
|
||||||
|
1,2,1,5,
|
||||||
|
1,4,0,2,
|
||||||
|
1,4,0,1,
|
||||||
|
2,4,0,2,
|
||||||
|
0,0,0,1,
|
||||||
|
0,5,5,5,
|
||||||
|
0,1,1,1,
|
||||||
|
-1,1,0,2
|
||||||
|
};
|
||||||
|
for (int i = 0; i < num.length; i += 4) {
|
||||||
|
interval1 = new Interval1D(num[i],num[i+1]);
|
||||||
|
interval2 = new Interval1D(num[i+2],num[i+3]);
|
||||||
|
assertEquals(interval1.isUnionableWith(interval2), interval2.isUnionableWith(interval1));
|
||||||
|
assertTrue(interval1.isUnionableWith(interval2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsUnionableWithFalse() {
|
||||||
|
Interval1D interval1;
|
||||||
|
Interval1D interval2;
|
||||||
|
int num[] = {
|
||||||
|
0,0,2,2,
|
||||||
|
0,0,2,3,
|
||||||
|
0,0,4,4,
|
||||||
|
0,4,6,8,
|
||||||
|
2,2,0,0,
|
||||||
|
};
|
||||||
|
for (int i = 0; i < num.length; i += 4) {
|
||||||
|
interval1 = new Interval1D(num[i],num[i+1]);
|
||||||
|
interval2 = new Interval1D(num[i+2],num[i+3]);
|
||||||
|
assertEquals(interval1.isUnionableWith(interval2), interval2.isUnionableWith(interval1));
|
||||||
|
assertFalse(interval1.isUnionableWith(interval2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnion() {
|
||||||
|
Interval1D interval1;
|
||||||
|
Interval1D interval2;
|
||||||
|
String str;
|
||||||
|
int num[] = {
|
||||||
|
1,1,0,2,0,2,
|
||||||
|
1,1,1,1,1,1,
|
||||||
|
0,0,0,0,0,0,
|
||||||
|
0,1,0,1,0,1,
|
||||||
|
0,1,0,1,0,1,
|
||||||
|
0,5,1,1,0,5,
|
||||||
|
0,4,1,5,0,5,
|
||||||
|
1,5,0,4,0,5,
|
||||||
|
1,4,0,5,0,5,
|
||||||
|
};
|
||||||
|
for (int i = 0; i < num.length; i += 6) {
|
||||||
|
interval1 = new Interval1D(num[i],num[i+1]);
|
||||||
|
interval2 = new Interval1D(num[i+2],num[i+3]);
|
||||||
|
str =("["+num[i+4]+".."+num[i+5]+"]");
|
||||||
|
assertEquals(str,interval1.union(interval2).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testUnionFails() {
|
||||||
|
|
||||||
|
Interval1D interval1 = new Interval1D(0,0);
|
||||||
|
Interval1D interval2 = new Interval1D(2,2);
|
||||||
|
interval1.union(interval2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEqualsObjectTrue() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
Interval1D interval1;
|
||||||
|
Interval1D interval2;
|
||||||
|
int a,b;
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i){
|
||||||
|
a = rng.nextInt(500);
|
||||||
|
b = rng.nextInt(500);
|
||||||
|
|
||||||
|
if(a<=b){
|
||||||
|
interval1 = new Interval1D(a,b);
|
||||||
|
interval2 = new Interval1D(a,b);
|
||||||
|
assertTrue(interval1.equals(interval2));
|
||||||
|
assertEquals(interval1.equals(interval2), interval2.equals(interval1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEqualsObjectFalse() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
Interval1D interval1;
|
||||||
|
String str = "";
|
||||||
|
int a,b;
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i){
|
||||||
|
a = rng.nextInt(500);
|
||||||
|
b = rng.nextInt(500);
|
||||||
|
if(a<=b){
|
||||||
|
interval1 = new Interval1D(a,b);
|
||||||
|
assertFalse(interval1.equals(str));
|
||||||
|
assertEquals(interval1.equals(str), str.equals(interval1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToString() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
Interval1D interval1;
|
||||||
|
int a,b;
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i){
|
||||||
|
a = rng.nextInt(500);
|
||||||
|
b = rng.nextInt(500);
|
||||||
|
if(a<=b){
|
||||||
|
interval1 = new Interval1D(a,b);
|
||||||
|
assertEquals(interval1.toString(),"["+a+".."+b+"]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
161
Alpano/tests/ch/epfl/alpano/Interval1DTestP.java
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.test.ObjectTest.hashCodeIsCompatibleWithEquals;
|
||||||
|
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static java.lang.Math.ceil;
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
import static java.lang.Math.sqrt;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class Interval1DTestP {
|
||||||
|
private static Interval1D i_0_9() { return new Interval1D(0, 9); }
|
||||||
|
private static Interval1D i_0_2() { return new Interval1D(0, 2); }
|
||||||
|
private static Interval1D i_3_5() { return new Interval1D(3, 5); }
|
||||||
|
private static Interval1D i_4_6() { return new Interval1D(4, 6); }
|
||||||
|
private static Interval1D i_6_9() { return new Interval1D(6, 9); }
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsForInvalidBounds() {
|
||||||
|
new Interval1D(1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWorksForSingletonInterval() {
|
||||||
|
new Interval1D(10, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void containsIsTrueOnlyForTheIntervalsElements() {
|
||||||
|
int sqrtIt = (int)ceil(sqrt(RANDOM_ITERATIONS));
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < sqrtIt; ++i) {
|
||||||
|
int a = rng.nextInt(200) - 100;
|
||||||
|
int b = a + rng.nextInt(50);
|
||||||
|
Interval1D interval = new Interval1D(a, b);
|
||||||
|
for (int j = 0; j < sqrtIt; ++j) {
|
||||||
|
int v = rng.nextInt(200) - 100;
|
||||||
|
assertEquals(a <= v && v <= b, interval.contains(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void containsWorksAtTheLimit() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
int max = rng.nextInt(2000);
|
||||||
|
int a = rng.nextInt(max) - 1000;
|
||||||
|
int b = max - 1000;
|
||||||
|
Interval1D interval = new Interval1D(a, b);
|
||||||
|
assertFalse(interval.contains(a - 1));
|
||||||
|
assertTrue(interval.contains(a));
|
||||||
|
assertTrue(interval.contains(b));
|
||||||
|
assertFalse(interval.contains(b + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void sizeWorksOnKnownIntervals() {
|
||||||
|
assertEquals(10, i_0_9().size());
|
||||||
|
assertEquals(3, i_0_2().size());
|
||||||
|
assertEquals(3, i_3_5().size());
|
||||||
|
assertEquals(3, i_4_6().size());
|
||||||
|
assertEquals(4, i_6_9().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sizeOfIntersectionWorksOnNonIntersectingIntervals() {
|
||||||
|
assertEquals(0, i_0_2().sizeOfIntersectionWith(i_3_5()));
|
||||||
|
assertEquals(0, i_0_2().sizeOfIntersectionWith(i_4_6()));
|
||||||
|
assertEquals(0, i_0_2().sizeOfIntersectionWith(i_6_9()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sizeOfIntersectionWorksOnIntersectingIntervals() {
|
||||||
|
assertEquals(3, i_0_2().sizeOfIntersectionWith(i_0_9()));
|
||||||
|
assertEquals(3, i_0_9().sizeOfIntersectionWith(i_0_2()));
|
||||||
|
assertEquals(1, i_4_6().sizeOfIntersectionWith(i_6_9()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void boundingUnionWorksOnKnownIntervals() {
|
||||||
|
assertEquals(0, i_0_2().boundingUnion(i_6_9()).includedFrom());
|
||||||
|
assertEquals(9, i_0_2().boundingUnion(i_6_9()).includedTo());
|
||||||
|
assertEquals(0, i_6_9().boundingUnion(i_0_2()).includedFrom());
|
||||||
|
assertEquals(9, i_6_9().boundingUnion(i_0_2()).includedTo());
|
||||||
|
assertEquals(0, i_0_9().boundingUnion(i_0_9()).includedFrom());
|
||||||
|
assertEquals(9, i_0_9().boundingUnion(i_0_9()).includedTo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isUnionableWithWorksOnKnownIntervals() {
|
||||||
|
// Intersecting intervals
|
||||||
|
assertTrue(i_0_9().isUnionableWith(i_0_9()));
|
||||||
|
assertTrue(i_0_9().isUnionableWith(i_3_5()));
|
||||||
|
assertTrue(i_3_5().isUnionableWith(i_3_5()));
|
||||||
|
assertTrue(i_3_5().isUnionableWith(i_0_9()));
|
||||||
|
assertTrue(i_3_5().isUnionableWith(i_4_6()));
|
||||||
|
assertTrue(i_4_6().isUnionableWith(i_4_6()));
|
||||||
|
assertTrue(i_4_6().isUnionableWith(i_3_5()));
|
||||||
|
|
||||||
|
// Contiguous intervals
|
||||||
|
assertTrue(i_3_5().isUnionableWith(i_6_9()));
|
||||||
|
assertTrue(i_6_9().isUnionableWith(i_3_5()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void unionFailsOnNonUnionableIntervals() {
|
||||||
|
i_0_2().union(i_6_9());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unionWorksWhenOneIntervalContainsTheOther() {
|
||||||
|
assertEquals(i_0_9(), i_0_9().union(i_3_5()));
|
||||||
|
assertEquals(i_0_9(), i_3_5().union(i_0_9()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unionWorksWithASingleInterval() {
|
||||||
|
assertEquals(i_3_5(), i_3_5().union(i_3_5()).union(i_3_5()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unionWorksWhenOneIntervalIsContiguousWithTheOther() {
|
||||||
|
Interval1D i = i_0_2().union(i_6_9().union(i_3_5()));
|
||||||
|
assertEquals(i_0_9(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void equalsIsStructural() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
int a = rng.nextInt(), b = rng.nextInt();
|
||||||
|
Interval1D int1 = new Interval1D(min(a, b), max(a, b));
|
||||||
|
Interval1D int2 = new Interval1D(min(a, b), max(a, b));
|
||||||
|
Interval1D int3 = new Interval1D(min(a, b) + 1, max(a, b) + 1);
|
||||||
|
assertTrue(int1.equals(int2));
|
||||||
|
assertFalse(int1.equals(int3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hashCodeAndEqualsAreCompatible() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
int a = rng.nextInt(), b = rng.nextInt();
|
||||||
|
int c = rng.nextInt(), d = rng.nextInt();
|
||||||
|
Interval1D int1 = new Interval1D(min(a, b), max(a, b));
|
||||||
|
Interval1D int2 = new Interval1D(min(c, d), max(c, d));
|
||||||
|
Interval1D int3 = new Interval1D(min(c, d), max(c, d));
|
||||||
|
assertTrue(hashCodeIsCompatibleWithEquals(int1, int2));
|
||||||
|
assertTrue(hashCodeIsCompatibleWithEquals(int2, int3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
227
Alpano/tests/ch/epfl/alpano/Interval2DTest.java
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
|
public class Interval2DTest {
|
||||||
|
|
||||||
|
@Test(expected= NullPointerException.class)
|
||||||
|
public void testInterval2D() {
|
||||||
|
new Interval2D(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContains() {
|
||||||
|
|
||||||
|
HashMap<int[][], int[]> testValues = new HashMap<int[][], int[]>();
|
||||||
|
|
||||||
|
testValues.put(new int[][] { {0,4}, {0,4} }, new int[] {2,4});
|
||||||
|
testValues.put(new int[][] { {0,0}, {0,0} }, new int[] {0,0});
|
||||||
|
|
||||||
|
|
||||||
|
for (int[][] values : testValues.keySet()){
|
||||||
|
|
||||||
|
assertEquals ( true ,
|
||||||
|
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[0][0], values[0][1]),
|
||||||
|
new Interval1D(values[1][0], values[1][1])).contains(testValues.get(values)[0],
|
||||||
|
testValues.get(values)[1])
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
testValues = new HashMap<int[][], int[]>();
|
||||||
|
|
||||||
|
testValues.put(new int[][] { {0,4}, {0,4} }, new int[] {-1,-1});
|
||||||
|
testValues.put(new int[][] { {0,0}, {0,0} }, new int[] {0,1});
|
||||||
|
|
||||||
|
|
||||||
|
for (int[][] values : testValues.keySet()){
|
||||||
|
|
||||||
|
assertEquals ( false,
|
||||||
|
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[0][0], values[0][1]),
|
||||||
|
new Interval1D(values[1][0], values[1][1])).contains(testValues.get(values)[0],
|
||||||
|
testValues.get(values)[1])
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSize() {
|
||||||
|
HashMap<int[][], Integer> testValues = new HashMap<int[][], Integer>();
|
||||||
|
|
||||||
|
testValues.put(new int[][] { {0,4}, {0,4} }, 25);
|
||||||
|
testValues.put(new int[][] { {0,0}, {0,0} }, 1);
|
||||||
|
|
||||||
|
|
||||||
|
for (int[][] values : testValues.keySet()){
|
||||||
|
|
||||||
|
assertEquals ( (int)testValues.get(values),
|
||||||
|
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[0][0], values[0][1]),
|
||||||
|
new Interval1D(values[1][0], values[1][1])).size()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeOfIntersectionWith() {
|
||||||
|
HashMap<int[][][], Integer> testValues = new HashMap<int[][][], Integer>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
testValues.put( new int[][][] { {{0,5}, {0,5}} , {{4,6},{4,6}} } , 4);
|
||||||
|
testValues.put( new int[][][] { {{0,0}, {0,0}} , {{-40,40},{-40,40}} } , 1);
|
||||||
|
testValues.put( new int[][][] { {{0,5}, {0,5}} , {{10,15},{10,15}} } , 0);
|
||||||
|
|
||||||
|
for (int[][][] values : testValues.keySet()){
|
||||||
|
|
||||||
|
assertEquals ( (int)testValues.get(values),
|
||||||
|
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[0][0][0], values[0][0][1]),
|
||||||
|
new Interval1D(values[0][1][0], values[0][1][1])).sizeOfIntersectionWith(
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[1][0][0], values[1][0][1]),
|
||||||
|
new Interval1D(values[1][1][0], values[1][1][1])))
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBoundingUnion() {
|
||||||
|
HashMap<int[][][], int[][]> testValues = new HashMap<int[][][], int[][]>();
|
||||||
|
|
||||||
|
|
||||||
|
testValues.put( new int[][][] {{{0,5}, {0,5}} , {{4,6},{4,6}} } , new int[][] {{0,6},{0,6}} );
|
||||||
|
testValues.put( new int[][][] {{{0,0}, {0,0}} , {{4,6},{4,6}} } , new int[][] {{0,6},{0,6}} );
|
||||||
|
|
||||||
|
for (int[][][] values : testValues.keySet()){
|
||||||
|
|
||||||
|
assertEquals (true,
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[0][0][0], values[0][0][1]),
|
||||||
|
new Interval1D(values[0][1][0], values[0][1][1])).boundingUnion(
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[1][0][0], values[1][0][1]),
|
||||||
|
new Interval1D(values[1][1][0], values[1][1][1]))).equals(
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(testValues.get(values)[0][0], testValues.get(values)[0][1]),
|
||||||
|
new Interval1D(testValues.get(values)[1][0], testValues.get(values)[1][1]))
|
||||||
|
));
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsUnionableWith() {
|
||||||
|
HashMap<int[][][], Boolean> testValues = new HashMap<int[][][], Boolean>();
|
||||||
|
|
||||||
|
testValues.put( new int[][][] {{{0,5}, {0,5}} , {{4,6},{4,6}} } ,false);
|
||||||
|
testValues.put( new int[][][] {{{0,0}, {0,0}} , {{1,6},{1,6}} } , false );
|
||||||
|
|
||||||
|
for (int[][][] values : testValues.keySet()){
|
||||||
|
|
||||||
|
assertEquals ( true,
|
||||||
|
testValues.get(values) == new Interval2D(
|
||||||
|
new Interval1D(values[0][0][0], values[0][0][1]),
|
||||||
|
new Interval1D(values[0][1][0], values[0][1][1])).isUnionableWith(
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[1][0][0], values[1][0][1]),
|
||||||
|
new Interval1D(values[1][1][0], values[1][1][1]))));
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testUnion() {
|
||||||
|
HashMap<int[][][], int[][]> testValues = new HashMap<int[][][], int[][]>();
|
||||||
|
|
||||||
|
|
||||||
|
testValues.put( new int[][][] {{{0,5}, {0,5}} , {{4,6},{4,6}} } , new int[][] {{4,5},{4,5}} );
|
||||||
|
testValues.put( new int[][][] {{{-100,-100}, {0,0}} , {{0,0},{100,100}} } , new int[][] {{0,0},{0,0}} );
|
||||||
|
testValues.put( new int[][][] {{{-100,100}, {0,0}} , {{0,0},{-100,100}} } , new int[][] {{0,0},{0,0}} );
|
||||||
|
|
||||||
|
for (int[][][] values : testValues.keySet()){
|
||||||
|
|
||||||
|
assertEquals (false,
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[0][0][0], values[0][0][1]),
|
||||||
|
new Interval1D(values[0][1][0], values[0][1][1])).union(
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[1][0][0], values[1][0][1]),
|
||||||
|
new Interval1D(values[1][1][0], values[1][1][1]))).equals(
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(testValues.get(values)[0][0], testValues.get(values)[0][1]),
|
||||||
|
new Interval1D(testValues.get(values)[1][0], testValues.get(values)[1][1]))
|
||||||
|
));
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(0, 0),
|
||||||
|
new Interval1D(0, 0)).union(
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(1, 1),
|
||||||
|
new Interval1D(1, 1)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEqualsObject() {
|
||||||
|
HashMap<int[][][], Boolean> testValues = new HashMap<int[][][], Boolean>();
|
||||||
|
|
||||||
|
testValues.put( new int[][][] {{{0,5}, {0,5}} , {{0,5},{0,5}} } ,true );
|
||||||
|
testValues.put( new int[][][] {{{0,5}, {0,5}} , {{0,5},{0,6}} } , false );
|
||||||
|
|
||||||
|
for (int[][][] values : testValues.keySet()){
|
||||||
|
|
||||||
|
assertEquals ( testValues.get(values) , new Interval2D(
|
||||||
|
new Interval1D(values[0][0][0], values[0][0][1]),
|
||||||
|
new Interval1D(values[0][1][0], values[0][1][1])).equals(
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[1][0][0], values[1][0][1]),
|
||||||
|
new Interval1D(values[1][1][0], values[1][1][1]))) );
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToString() {
|
||||||
|
HashMap<int[][], String> testValues = new HashMap<int[][], String>();
|
||||||
|
|
||||||
|
testValues.put(new int[][] { {0,4}, {0,4} }, "[0..4]x[0..4]");
|
||||||
|
|
||||||
|
|
||||||
|
for (int[][] values : testValues.keySet()){
|
||||||
|
|
||||||
|
assertEquals (testValues.get(values),
|
||||||
|
|
||||||
|
new Interval2D(
|
||||||
|
new Interval1D(values[0][0], values[0][1]),
|
||||||
|
new Interval1D(values[1][0], values[1][1])).toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
209
Alpano/tests/ch/epfl/alpano/Interval2DTestP.java
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.test.ObjectTest.hashCodeIsCompatibleWithEquals;
|
||||||
|
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class Interval2DTestP {
|
||||||
|
private static Interval2D newInterval2D(int x1, int x2, int y1, int y2) {
|
||||||
|
return new Interval2D(new Interval1D(x1, x2), new Interval1D(y1, y2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Interval2D i_0_10_0_10() {
|
||||||
|
return newInterval2D(0, 10, 0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Interval2D i_0_9_0_11() {
|
||||||
|
return newInterval2D(0, 9, 0, 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Interval2D i_0_10_11_20() {
|
||||||
|
return newInterval2D(0, 10, 11, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Interval2D i_0_10_0_20() {
|
||||||
|
return newInterval2D(0, 10, 0, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Interval2D i_11_20_0_10() {
|
||||||
|
return newInterval2D(11, 20, 0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Interval2D i_0_20_0_10() {
|
||||||
|
return newInterval2D(0, 20, 0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Interval2D i_2_2_2_2() {
|
||||||
|
return newInterval2D(2, 2, 2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public void constructorFailsOnInvalidInterval() {
|
||||||
|
new Interval2D(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void containsWorksOnKnownIntervals() {
|
||||||
|
Interval2D i = i_2_2_2_2();
|
||||||
|
for (int x = 1; x <= 3; ++x) {
|
||||||
|
for (int y = 1; y <= 3; ++y) {
|
||||||
|
assertEquals(x == 2 && y == 2, i.contains(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sizeWorksOnKnownIntervals() {
|
||||||
|
assertEquals(1, i_2_2_2_2().size());
|
||||||
|
assertEquals(21 * 11, i_0_20_0_10().size());
|
||||||
|
assertEquals(10 * 11, i_11_20_0_10().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sizeOfIntersectionWorksOnNonIntersectingIntervals() {
|
||||||
|
assertEquals(0, i_2_2_2_2().sizeOfIntersectionWith(i_11_20_0_10()));
|
||||||
|
assertEquals(0, i_11_20_0_10().sizeOfIntersectionWith(i_2_2_2_2()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sizeOfIntersectionWorksOnIntersectingIntervals() {
|
||||||
|
assertEquals(1, i_2_2_2_2().sizeOfIntersectionWith(i_2_2_2_2()));
|
||||||
|
assertEquals(21 * 11, i_0_20_0_10().sizeOfIntersectionWith(i_0_20_0_10()));
|
||||||
|
assertEquals(1, i_2_2_2_2().sizeOfIntersectionWith(i_0_20_0_10()));
|
||||||
|
assertEquals(1, i_0_20_0_10().sizeOfIntersectionWith(i_2_2_2_2()));
|
||||||
|
assertEquals(10 * 11, i_0_10_0_10().sizeOfIntersectionWith(i_0_9_0_11()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void boudingUnionWorksOnKnownIntervals() {
|
||||||
|
assertEquals(i_2_2_2_2(), i_2_2_2_2().boundingUnion(i_2_2_2_2()));
|
||||||
|
|
||||||
|
Interval2D i1 = i_0_10_0_10().boundingUnion(i_0_9_0_11());
|
||||||
|
assertEquals(0, i1.iX().includedFrom());
|
||||||
|
assertEquals(10, i1.iX().includedTo());
|
||||||
|
assertEquals(0, i1.iY().includedFrom());
|
||||||
|
assertEquals(11, i1.iY().includedTo());
|
||||||
|
|
||||||
|
Interval2D i2 = i_2_2_2_2().boundingUnion(i_11_20_0_10());
|
||||||
|
assertEquals(2, i2.iX().includedFrom());
|
||||||
|
assertEquals(20, i2.iX().includedTo());
|
||||||
|
assertEquals(0, i2.iY().includedFrom());
|
||||||
|
assertEquals(10, i2.iY().includedTo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isUnionableWorksOnKnownUnionableIntervals() {
|
||||||
|
assertTrue(i_0_10_0_10().isUnionableWith(i_0_10_0_10()));
|
||||||
|
assertTrue(i_0_10_0_10().isUnionableWith(i_0_10_11_20()));
|
||||||
|
assertTrue(i_0_10_11_20().isUnionableWith(i_0_10_0_10()));
|
||||||
|
assertTrue(i_0_10_0_10().isUnionableWith(i_11_20_0_10()));
|
||||||
|
assertTrue(i_11_20_0_10().isUnionableWith(i_0_10_0_10()));
|
||||||
|
assertTrue(i_0_10_0_10().isUnionableWith(i_2_2_2_2()));
|
||||||
|
assertTrue(i_2_2_2_2().isUnionableWith(i_0_10_0_10()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isUnionableWorksOnKnownNonUnionableIntervals() {
|
||||||
|
assertFalse(i_2_2_2_2().isUnionableWith(i_11_20_0_10()));
|
||||||
|
assertFalse(i_11_20_0_10().isUnionableWith(i_2_2_2_2()));
|
||||||
|
assertFalse(i_0_9_0_11().isUnionableWith(i_0_10_0_10()));
|
||||||
|
assertFalse(i_0_10_0_10().isUnionableWith(i_0_9_0_11()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isUnionableWithIsReflexive() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
Interval2D interval = nextInterval(rng, 500, 1000);
|
||||||
|
assertTrue(interval.isUnionableWith(interval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isUnionableWithIsSymmetric() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
Interval2D i1 = nextInterval(rng, 5, 10);
|
||||||
|
Interval2D i2 = nextInterval(rng, 5, 10);
|
||||||
|
assertTrue(! i1.isUnionableWith(i2) || i2.isUnionableWith(i1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void unionFailsOnNonUnionableIntervals() {
|
||||||
|
i_2_2_2_2().union(i_11_20_0_10());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unionWorksOnASingleInterval() {
|
||||||
|
assertEquals(i_0_10_0_10(), i_0_10_0_10().union(i_0_10_0_10().union(i_0_10_0_10())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unionWorksOnKnownIntervals() {
|
||||||
|
assertEquals(i_0_10_0_10(), i_0_10_0_10().union(i_2_2_2_2()));
|
||||||
|
assertEquals(i_0_10_0_10(), i_2_2_2_2().union(i_0_10_0_10()));
|
||||||
|
|
||||||
|
assertEquals(i_0_10_0_20(), i_0_10_0_10().union(i_0_10_11_20()));
|
||||||
|
assertEquals(i_0_10_0_20(), i_0_10_11_20().union(i_0_10_0_10()));
|
||||||
|
|
||||||
|
assertEquals(i_0_20_0_10(), i_0_10_0_10().union(i_11_20_0_10()));
|
||||||
|
assertEquals(i_0_20_0_10(), i_11_20_0_10().union(i_0_10_0_10()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unionIsCommutative() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
Interval2D i1 = nextInterval(rng, 5, 10);
|
||||||
|
Interval2D i2 = nextInterval(rng, 5, 10);
|
||||||
|
if (i1.isUnionableWith(i2))
|
||||||
|
assertEquals(i1.union(i2), i2.union(i1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void equalsIsStructural() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
int x1 = rng.nextInt(1000) - 500;
|
||||||
|
int x2 = x1 + rng.nextInt(1000);
|
||||||
|
int y1 = rng.nextInt(1000) - 500;
|
||||||
|
int y2 = y1 + rng.nextInt(1000);
|
||||||
|
Interval2D int1 = newInterval2D(x1, x2, y1, y2);
|
||||||
|
Interval2D int2 = newInterval2D(x1, x2, y1, y2);
|
||||||
|
Interval2D int3 = newInterval2D(x1, x2, y1, y2 + 1);
|
||||||
|
assertTrue(int1.equals(int2));
|
||||||
|
assertTrue(int2.equals(int1));
|
||||||
|
assertFalse(int1.equals(int3));
|
||||||
|
assertFalse(int3.equals(int1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hashCodeAndEqualsAreCompatible() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
int a = rng.nextInt(1000) - 500, b = a + rng.nextInt(20);
|
||||||
|
int c = rng.nextInt(1000) - 500, d = c + rng.nextInt(20);
|
||||||
|
Interval2D i1 = newInterval2D(a, b, c, d);
|
||||||
|
Interval2D i1b = newInterval2D(a, b, c, d);
|
||||||
|
Interval2D i2 = newInterval2D(a, b, c, d + 1);
|
||||||
|
assertTrue(hashCodeIsCompatibleWithEquals(i1, i1b));
|
||||||
|
assertTrue(hashCodeIsCompatibleWithEquals(i1, i2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Interval2D nextInterval(Random rng, int maxOffset, int maxSize) {
|
||||||
|
int offsetX = rng.nextInt(maxOffset), offsetY = rng.nextInt(maxOffset);
|
||||||
|
int sizeX = rng.nextInt(maxSize), sizeY = rng.nextInt(maxSize);
|
||||||
|
return newInterval2D(-offsetX, sizeX - offsetX, -offsetY, sizeY - offsetY);
|
||||||
|
}
|
||||||
|
}
|
217
Alpano/tests/ch/epfl/alpano/Math2Test.java
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.alpano.Math2.angularDistance;
|
||||||
|
import static ch.epfl.alpano.Math2.bilerp;
|
||||||
|
import static ch.epfl.alpano.Math2.firstIntervalContainingRoot;
|
||||||
|
import static ch.epfl.alpano.Math2.floorMod;
|
||||||
|
import static ch.epfl.alpano.Math2.haversin;
|
||||||
|
import static ch.epfl.alpano.Math2.improveRoot;
|
||||||
|
import static ch.epfl.alpano.Math2.lerp;
|
||||||
|
import static ch.epfl.alpano.Math2.sq;
|
||||||
|
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
import static java.lang.Math.cos;
|
||||||
|
import static java.lang.Math.floor;
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
import static java.lang.Math.sin;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class Math2Test {
|
||||||
|
@Test
|
||||||
|
public void sqSquaresRandomValues() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double x = rng.nextDouble() * 1_000d - 500d;
|
||||||
|
assertEquals(x * x, sq(x), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void floorModWorksOnRandomValues() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double n = rng.nextDouble() * 1_000d - 500d;
|
||||||
|
double d = 0;
|
||||||
|
while (d == 0)
|
||||||
|
d = rng.nextDouble() * 1_000d - 500d;
|
||||||
|
double q = (int)floor(n / d);
|
||||||
|
double r = floorMod(n, d);
|
||||||
|
assertEquals(n, q * d + r, 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void haversinWorksOnRandomAngles() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double a = nextAngle(rng);
|
||||||
|
double h = (1d - cos(a)) / 2d;
|
||||||
|
assertEquals(h, haversin(a), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void angularDistanceWorksOnKnownAngles() {
|
||||||
|
double data[] = {
|
||||||
|
0, 45, 45,
|
||||||
|
45, 0, -45,
|
||||||
|
0, 179, 179,
|
||||||
|
0, 181, -179,
|
||||||
|
181, 359, 178,
|
||||||
|
181, 2, -179
|
||||||
|
};
|
||||||
|
for (int i = 0; i < data.length; i += 3) {
|
||||||
|
double a1 = toRadians(data[i]);
|
||||||
|
double a2 = toRadians(data[i + 1]);
|
||||||
|
double expectedD = toRadians(data[i + 2]);
|
||||||
|
assertEquals(expectedD, angularDistance(a1, a2), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void angularDistanceIsInExpectedRange() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double a1 = nextAngle(rng);
|
||||||
|
double a2 = nextAngle(rng);
|
||||||
|
double d = angularDistance(a1, a2);
|
||||||
|
assertTrue(-PI <= d && d < PI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void angularDistanceIsSymmetric() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double a1 = nextAngle(rng);
|
||||||
|
double a2 = nextAngle(rng);
|
||||||
|
assertEquals(0, angularDistance(a1, a2) + angularDistance(a2, a1), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lerpIsFirstValueAtStart() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double v1 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
double v2 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
assertEquals(v1, lerp(v1, v2, 0), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lerpIsAverageValueAtMiddle() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double v1 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
double v2 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
assertEquals((v1 + v2) / 2d, lerp(v1, v2, 0.5), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lerpIsSecondValueAtEnd() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double v1 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
double v2 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
assertEquals(v2, lerp(v1, v2, 1), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lerpIsInExpectedRange() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double v1 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
double v2 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
double p = rng.nextDouble();
|
||||||
|
double v = lerp(v1, v2, p);
|
||||||
|
assertTrue(min(v1, v2) <= v && v <= max(v1, v2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bilerpIsInExpectedRange() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double v1 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
double v2 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
double v3 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
double v4 = (rng.nextDouble() - 0.5) * 1000d;
|
||||||
|
double x = rng.nextDouble(), y = rng.nextDouble();
|
||||||
|
double v = bilerp(v1, v2, v3, v4, x, y);
|
||||||
|
assertTrue(min(min(v1, v2), min(v3, v4)) <= v
|
||||||
|
&& v <= max(max(v1, v2), max(v3, v4)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bilerpIsCorrectInCorners() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double v0 = rng.nextDouble(), v1 = rng.nextDouble();
|
||||||
|
double v2 = rng.nextDouble(), v3 = rng.nextDouble();
|
||||||
|
assertEquals(v0, bilerp(v0, v1, v2, v3, 0, 0), 1e-10);
|
||||||
|
assertEquals(v1, bilerp(v1, v1, v2, v3, 1, 0), 1e-10);
|
||||||
|
assertEquals(v2, bilerp(v2, v1, v2, v3, 0, 1), 1e-10);
|
||||||
|
assertEquals(v3, bilerp(v3, v1, v2, v3, 1, 1), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bilerpLerpsAlongSides() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double v0 = rng.nextDouble(), v1 = rng.nextDouble();
|
||||||
|
double v2 = rng.nextDouble(), v3 = rng.nextDouble();
|
||||||
|
assertEquals((v0 + v1)/2d, bilerp(v0, v1, v2, v3, 0.5, 0), 1e-10);
|
||||||
|
assertEquals((v0 + v2)/2d, bilerp(v0, v1, v2, v3, 0, 0.5), 1e-10);
|
||||||
|
assertEquals((v2 + v3)/2d, bilerp(v0, v1, v2, v3, 0.5, 1), 1e-10);
|
||||||
|
assertEquals((v1 + v3)/2d, bilerp(v0, v1, v2, v3, 1, 0.5), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void firstIntervalContainingRootWorksOnSin() {
|
||||||
|
double i1 = firstIntervalContainingRoot(new Sin(), -1d, 1d, 0.1 + 1e-11);
|
||||||
|
assertEquals(-0.1, i1, 1e-10);
|
||||||
|
|
||||||
|
double i2 = firstIntervalContainingRoot(new Sin(), 1, 4, 1);
|
||||||
|
assertEquals(3, i2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void improveRootFailsWhenIntervalDoesNotContainRoot() {
|
||||||
|
improveRoot(new Sin(), 1, 2, 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void improveRootWorksOnSin() {
|
||||||
|
double pi = improveRoot(new Sin(), 3.1, 3.2, 1e-10);
|
||||||
|
assertEquals(PI, pi, 1e-10);
|
||||||
|
|
||||||
|
double mPi = improveRoot(new Sin(), -4, -3.1, 1e-10);
|
||||||
|
assertEquals(-PI, mPi, 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double nextAngle(Random rng) {
|
||||||
|
return rng.nextDouble() * 2d * PI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Sin implements DoubleUnaryOperator {
|
||||||
|
@Override
|
||||||
|
public double applyAsDouble(double x) {
|
||||||
|
return sin(x);
|
||||||
|
}
|
||||||
|
}
|
45
Alpano/tests/ch/epfl/alpano/PanoramaCompTest.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.ElevationProfile;
|
||||||
|
|
||||||
|
public class PanoramaCompTest {
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public void throwsNullPointerUponConstruction(){
|
||||||
|
new PanoramaComputer(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givesTrivialZero(){
|
||||||
|
|
||||||
|
|
||||||
|
double zero =PanoramaComputer.rayToGroundDistance(
|
||||||
|
|
||||||
|
new ElevationProfile( new ContinuousElevationModel(new TestDemForPanoramaComp(new Interval2D(new Interval1D(-100, 100),
|
||||||
|
new Interval1D(-100, 100)))),
|
||||||
|
new GeoPoint(0,0),2.5,100), 0.0, 0.0).applyAsDouble(
|
||||||
|
1);
|
||||||
|
|
||||||
|
assertEquals(0, zero,0.01);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void throwsIllegalArgumentExceptionOnNegValues(){
|
||||||
|
double zero =PanoramaComputer.rayToGroundDistance(
|
||||||
|
|
||||||
|
new ElevationProfile( new ContinuousElevationModel(new TestDemForPanoramaComp(new Interval2D(new Interval1D(-100, 100),
|
||||||
|
new Interval1D(-100, 100)))),
|
||||||
|
new GeoPoint(0,0),0,100), 0.0, 1.0).applyAsDouble(
|
||||||
|
-50);
|
||||||
|
|
||||||
|
assertEquals(0, zero,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
1135
Alpano/tests/ch/epfl/alpano/PanoramaComputerTestP.java
Normal file
95
Alpano/tests/ch/epfl/alpano/PanoramaParametersTest.java
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class PanoramaParametersTest {
|
||||||
|
|
||||||
|
private GeoPoint vp = new GeoPoint(Math.toRadians(6.8087),Math.toRadians(47.0085));
|
||||||
|
private PanoramaParameters pp = new PanoramaParameters(vp, 1380, Math.toRadians(162),
|
||||||
|
Math.toRadians(27), 300, 2500, 800);
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAzimuthForX() {
|
||||||
|
assertEquals(pp.azimuthForX(1),Math.toRadians(148.5108043217287),0.000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testXForAzimuth() {
|
||||||
|
assertEquals(pp.xForAzimuth(pp.azimuthForX(1)),1,0.000000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testXForAzimuthFails() {
|
||||||
|
pp.xForAzimuth(Math.toRadians(27-81.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAltitudeForY() {
|
||||||
|
assertEquals(pp.altitudeForY(0),Math.toRadians(4.316326530612245),0.00000001);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testYForAltitude() {
|
||||||
|
assertEquals(pp.yForAltitude(pp.altitudeForY(1)),1,0.000000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsValidSampleIndexFails1() {
|
||||||
|
assertEquals(pp.isValidSampleIndex(2500, 800),false);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testIsValidSampleIndexFails2() {
|
||||||
|
assertEquals(pp.isValidSampleIndex(-1, -1),false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLinearSampleIndexSucces() {
|
||||||
|
assertEquals(pp.linearSampleIndex(0, 0),0);
|
||||||
|
assertEquals(pp.linearSampleIndex(2500-1, 800-1),(2500*800)-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testObserverPosition() {
|
||||||
|
assertEquals(pp.observerPosition(),vp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testObserverElevation() {
|
||||||
|
assertEquals(pp.observerElevation(),1380,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCenterAzimuth() {
|
||||||
|
assertEquals(pp.centerAzimuth(),Math.toRadians(162),0.00000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHorizontalFieldOfView() {
|
||||||
|
assertEquals(pp.horizontalFieldOfView(),Math.toRadians(27),0.0000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerticalFieldOfView() {
|
||||||
|
assertEquals(pp.verticalFieldOfView(),Math.toRadians(8.63265306122),0.000000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMaxDistance() {
|
||||||
|
assertEquals(pp.maxDistance(),300,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWidth() {
|
||||||
|
assertEquals(pp.width(),2500,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHeight() {
|
||||||
|
assertEquals(pp.height(),800,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
167
Alpano/tests/ch/epfl/alpano/PanoramaParametersTestP.java
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
import static java.lang.Math.floorMod;
|
||||||
|
import static java.lang.Math.nextUp;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class PanoramaParametersTestP {
|
||||||
|
// Default (and valid) arguments for constructor
|
||||||
|
private static GeoPoint O_POS() { return new GeoPoint(toRadians(4), toRadians(4)); }
|
||||||
|
private static int O_EL = 1000;
|
||||||
|
private static double C_AZ = toRadians(180);
|
||||||
|
private static double H_FOV = toRadians(60);
|
||||||
|
private static int MAX_D = 1000;
|
||||||
|
private static int W = 100, H = 100;
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public void constructorFailsWithNullObserverPosition() {
|
||||||
|
new PanoramaParameters(null, O_EL, C_AZ, H_FOV, MAX_D, W, H);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithNonCanonicalAzimuth() {
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, 42d, H_FOV, MAX_D, W, H);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithZeroFieldOfView() {
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, 0, MAX_D, W, H);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithTooLargeOfView() {
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, nextUp(2d * PI), MAX_D, W, H);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithZeroWidth() {
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, H_FOV, MAX_D, 0, H);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithZeroHeight() {
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, H_FOV, MAX_D, W, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithZeroMaxDistance() {
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, H_FOV, 0, W, H);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verticalFieldOfViewIsCorrect() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, toRadians(60), MAX_D, 601, 201);
|
||||||
|
assertEquals(p.verticalFieldOfView(), toRadians(20), 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void azimuthForXFailsForNegativeX() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, H_FOV, MAX_D, W, H);
|
||||||
|
p.azimuthForX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void azimuthForXFailsForTooBigX() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, H_FOV, MAX_D, W, H);
|
||||||
|
p.azimuthForX(W + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void azimuthForXWorksForFullCircle() {
|
||||||
|
int centralAzDeg = 90;
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, toRadians(centralAzDeg), Math2.PI2, MAX_D, 361, 201);
|
||||||
|
for (int azDeg = 0; azDeg < 360; ++azDeg) {
|
||||||
|
double expectedAz = toRadians(floorMod(azDeg - centralAzDeg, 360));
|
||||||
|
double actualAz = p.azimuthForX(azDeg);
|
||||||
|
assertEquals(expectedAz, actualAz, 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void xForAzimuthFailsForTooSmallAzimuth() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, toRadians(10), toRadians(40), MAX_D, W, H);
|
||||||
|
p.xForAzimuth(toRadians(349.99));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void xForAzimuthFailsForTooBigAzimuth() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, toRadians(10), toRadians(40), MAX_D, W, H);
|
||||||
|
p.xForAzimuth(toRadians(50.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void altitudeForYFailsForNegativeY() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, H_FOV, MAX_D, W, H);
|
||||||
|
p.altitudeForY(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void altitueForYFailsForTooBigY() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, H_FOV, MAX_D, W, H);
|
||||||
|
p.altitudeForY(H + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void altitudeForYWorks() {
|
||||||
|
int height = 201;
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, toRadians(60), MAX_D, 601, height);
|
||||||
|
double halfVerticalFOV = toRadians(20) / 2d;
|
||||||
|
double delta = toRadians(0.1);
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
assertEquals(halfVerticalFOV - y * delta, p.altitudeForY(y), 1e-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void yForAltitudeFailsForTooSmallAltitude() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, toRadians(60), MAX_D, 601, 201);
|
||||||
|
p.yForAltitude(toRadians(-10.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void yForAltitudeFailsForTooBigAltitude() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, toRadians(60), MAX_D, 601, 201);
|
||||||
|
p.yForAltitude(toRadians(10.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void azimuthForXAndXForAzimuthAreInverse() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, toRadians(60), MAX_D, 601, 201);
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
int x = 1 + rng.nextInt(600);
|
||||||
|
assertEquals(x, p.xForAzimuth(p.azimuthForX(x)), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void altitudeForYAndYForAltitudeAreInverse() {
|
||||||
|
PanoramaParameters p =
|
||||||
|
new PanoramaParameters(O_POS(), O_EL, C_AZ, toRadians(60), MAX_D, 601, 201);
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
int x = 1 + rng.nextInt(200);
|
||||||
|
assertEquals(x, p.yForAltitude(p.altitudeForY(x)), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
Alpano/tests/ch/epfl/alpano/PanoramaTestP.java
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class PanoramaTestP{
|
||||||
|
private static PanoramaParameters PARAMS() {
|
||||||
|
return new PanoramaParameters(
|
||||||
|
new GeoPoint(toRadians(46), toRadians(6)),
|
||||||
|
1000,
|
||||||
|
toRadians(180),
|
||||||
|
toRadians(60),
|
||||||
|
100_000,
|
||||||
|
9,
|
||||||
|
7);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public void builderFailsWithNullParameters() {
|
||||||
|
new Panorama.Builder(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void builderConstructorCorrectlyInitializesSamples() {
|
||||||
|
Panorama p = new Panorama.Builder(PARAMS()).build();
|
||||||
|
assertEquals(Float.POSITIVE_INFINITY, p.distanceAt(0, 0), 0);
|
||||||
|
assertEquals(0, p.longitudeAt(0, 0), 0);
|
||||||
|
assertEquals(0, p.latitudeAt(0, 0), 0);
|
||||||
|
assertEquals(0, p.elevationAt(0, 0), 0);
|
||||||
|
assertEquals(0, p.slopeAt(0, 0), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IndexOutOfBoundsException.class)
|
||||||
|
public void setDistanceAtFailsWithInvalidIndex() {
|
||||||
|
Panorama.Builder b = new Panorama.Builder(PARAMS());
|
||||||
|
b.setDistanceAt(10, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setLongitudeAtReturnsThis() {
|
||||||
|
Panorama.Builder b = new Panorama.Builder(PARAMS());
|
||||||
|
assertSame(b, b.setLongitudeAt(0, 0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void setSlopeAtFailsAfterBuild() {
|
||||||
|
Panorama.Builder b = new Panorama.Builder(PARAMS());
|
||||||
|
b.build();
|
||||||
|
b.setSlopeAt(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void buildFailsAfterBuild() {
|
||||||
|
Panorama.Builder b = new Panorama.Builder(PARAMS());
|
||||||
|
b.build();
|
||||||
|
b.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parametersReturnsParameters() {
|
||||||
|
PanoramaParameters ps = PARAMS();
|
||||||
|
Panorama p = new Panorama.Builder(ps).build();
|
||||||
|
assertSame(ps, p.parameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void builderSettersWork() {
|
||||||
|
PanoramaParameters ps = PARAMS();
|
||||||
|
float[] values = new float[ps.width() * ps.height()];
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < values.length; ++i)
|
||||||
|
values[i] = rng.nextFloat() + 0.5f;
|
||||||
|
|
||||||
|
Panorama.Builder b = new Panorama.Builder(ps);
|
||||||
|
for (int x = 0; x < ps.width(); ++x) {
|
||||||
|
for (int y = 0; y < ps.height(); ++y) {
|
||||||
|
float v = values[y + x * ps.height()];
|
||||||
|
b.setDistanceAt(x, y, v)
|
||||||
|
.setElevationAt(x, y, v)
|
||||||
|
.setLatitudeAt(x, y, v)
|
||||||
|
.setLongitudeAt(x, y, v)
|
||||||
|
.setSlopeAt(x, y, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Panorama p = b.build();
|
||||||
|
for (int x = 0; x < ps.width(); ++x) {
|
||||||
|
for (int y = 0; y < ps.height(); ++y) {
|
||||||
|
float v = values[y + x * ps.height()];
|
||||||
|
assertEquals(v, p.distanceAt(x, y), 0);
|
||||||
|
assertEquals(v, p.elevationAt(x, y), 0);
|
||||||
|
assertEquals(v, p.latitudeAt(x, y), 0);
|
||||||
|
assertEquals(v, p.longitudeAt(x, y), 0);
|
||||||
|
assertEquals(v, p.slopeAt(x, y), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
Alpano/tests/ch/epfl/alpano/PreconditionsTest.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
import static ch.epfl.alpano.Preconditions.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class PreconditionsTest {
|
||||||
|
////////// checkArgument (1 argument)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkArgument1SucceedsForTrue() {
|
||||||
|
checkArgument(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void checkArgument1ThrowsForFalse() {
|
||||||
|
checkArgument(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////// checkArgument (2 arguments)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkArgument2SucceedsForTrue() {
|
||||||
|
checkArgument(true, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void checkArgument2ThrowsForFalse() {
|
||||||
|
checkArgument(false, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
////////// checkNonNul (1 arguments)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkNonNulSuccess() {
|
||||||
|
checkNonNul(1, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public void checkNonNulThrowsForZero() {
|
||||||
|
checkNonNul(0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
////////// checkNonNul (1 arguments)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
26
Alpano/tests/ch/epfl/alpano/TestDemForPanoramaComp.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ch.epfl.alpano;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import ch.epfl.alpano.dem.DiscreteElevationModel;
|
||||||
|
|
||||||
|
public class TestDemForPanoramaComp implements DiscreteElevationModel {
|
||||||
|
|
||||||
|
private final Interval2D extent;
|
||||||
|
|
||||||
|
public TestDemForPanoramaComp(Interval2D extent) {
|
||||||
|
this.extent = extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Interval2D extent() { return extent; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double elevationSample(int x, int y) {
|
||||||
|
|
||||||
|
return x*x-y*y;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.Interval1D;
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
|
||||||
|
public class CompositeDiscreteElevationModelTest {
|
||||||
|
private final static Interval2D ext1 = new Interval2D(
|
||||||
|
new Interval1D(-100_000, 100_000),
|
||||||
|
new Interval1D(0, 100_000));
|
||||||
|
private final static Interval2D ext2 = new Interval2D(
|
||||||
|
new Interval1D(-100_000, 100_000),
|
||||||
|
new Interval1D(100_001, 200_000));
|
||||||
|
private final static Interval2D ext12 = new Interval2D(
|
||||||
|
new Interval1D(-100_000, 100_000),
|
||||||
|
new Interval1D(0, 200_000));
|
||||||
|
private final static Interval2D ext3 = new Interval2D(
|
||||||
|
new Interval1D(0, 99_999),
|
||||||
|
new Interval1D(0, 100_001));
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void samplesPerRadiansHasCorrectValue() {
|
||||||
|
assertEquals(206264.80624709636, DiscreteElevationModel.SAMPLES_PER_RADIAN, 1e-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sampleIndexWorksOnRandomValues() {
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
int arcSeconds = rng.nextInt(2_000_000) - 1_000_000;
|
||||||
|
double angle = toRadians(arcSeconds / 3_600d);
|
||||||
|
assertEquals(arcSeconds, DiscreteElevationModel.sampleIndex(angle), 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void unionFailsIfExtentsNotUnionable() {
|
||||||
|
ConstantElevationDEM dem1 = new ConstantElevationDEM(ext1, 0);
|
||||||
|
ConstantElevationDEM dem2 = new ConstantElevationDEM(ext3, 0);
|
||||||
|
dem1.union(dem2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
@Test
|
||||||
|
public void extentOfUnionIsUnionOfExtent() {
|
||||||
|
ConstantElevationDEM dem1 = new ConstantElevationDEM(ext1, 0);
|
||||||
|
ConstantElevationDEM dem2 = new ConstantElevationDEM(ext2, 0);
|
||||||
|
DiscreteElevationModel dem12 = dem1.union(dem2);
|
||||||
|
assertEquals(ext12, dem12.extent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void elevationSampleFailsWhenOutsideOfExtent() {
|
||||||
|
ConstantElevationDEM dem1 = new ConstantElevationDEM(ext1, 0);
|
||||||
|
ConstantElevationDEM dem2 = new ConstantElevationDEM(ext2, 0);
|
||||||
|
DiscreteElevationModel dem12 = dem1.union(dem2);
|
||||||
|
dem12.elevationSample(0, 200_001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
@Test
|
||||||
|
public void elevationSampleWorksOnBothSubDEMs() {
|
||||||
|
ConstantElevationDEM dem1 = new ConstantElevationDEM(ext1, 1);
|
||||||
|
ConstantElevationDEM dem2 = new ConstantElevationDEM(ext2, 2);
|
||||||
|
DiscreteElevationModel dem12 = dem1.union(dem2);
|
||||||
|
assertEquals(1, dem12.elevationSample(-100_000, 0), 0);
|
||||||
|
assertEquals(1, dem12.elevationSample(100_000, 0), 0);
|
||||||
|
assertEquals(1, dem12.elevationSample(100_000, 100_000), 0);
|
||||||
|
assertEquals(1, dem12.elevationSample(-100_000, 100_000), 0);
|
||||||
|
assertEquals(2, dem12.elevationSample(-100_000, 100_001), 0);
|
||||||
|
assertEquals(2, dem12.elevationSample(100_000, 100_001), 0);
|
||||||
|
assertEquals(2, dem12.elevationSample(100_000, 200_000), 0);
|
||||||
|
assertEquals(2, dem12.elevationSample(-100_000, 200_000), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
@Test
|
||||||
|
public void closeClosesBothSubDEMs() throws Exception {
|
||||||
|
ConstantElevationDEM dem1 = new ConstantElevationDEM(ext1, 0);
|
||||||
|
ConstantElevationDEM dem2 = new ConstantElevationDEM(ext2, 0);
|
||||||
|
DiscreteElevationModel dem12 = dem1.union(dem2);
|
||||||
|
dem12.close();
|
||||||
|
assertTrue(dem1.isClosed);
|
||||||
|
assertTrue(dem2.isClosed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConstantElevationDEM implements DiscreteElevationModel {
|
||||||
|
private final Interval2D extent;
|
||||||
|
private final double elevation;
|
||||||
|
boolean isClosed = false;
|
||||||
|
|
||||||
|
public ConstantElevationDEM(Interval2D extent, double elevation) {
|
||||||
|
this.extent = extent;
|
||||||
|
this.elevation = elevation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception { isClosed = true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Interval2D extent() { return extent; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double elevationSample(int x, int y) { return elevation; }
|
||||||
|
}
|
@ -0,0 +1,170 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static ch.epfl.test.TestRandomizer.RANDOM_ITERATIONS;
|
||||||
|
import static ch.epfl.test.TestRandomizer.newRandom;
|
||||||
|
import static java.lang.Math.toDegrees;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import ch.epfl.alpano.Interval1D;
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
|
||||||
|
public class ContinuousElevationModelTest {
|
||||||
|
private final static Interval2D EXT_100_100 = new Interval2D(
|
||||||
|
new Interval1D(0, 100),
|
||||||
|
new Interval1D(0, 100));
|
||||||
|
|
||||||
|
private final static Interval2D EXT_13_13 = new Interval2D(
|
||||||
|
new Interval1D(0, 13),
|
||||||
|
new Interval1D(0, 13));
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public void constructorFailsWithNullDEM() {
|
||||||
|
new ContinuousElevationModel(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationAtReturns0OutsideOfExtent() {
|
||||||
|
DiscreteElevationModel dDEM = new ConstantElevationDEM(EXT_100_100, 1000);
|
||||||
|
ContinuousElevationModel cDEM = new ContinuousElevationModel(dDEM);
|
||||||
|
assertEquals(0, cDEM.elevationAt(pointForSampleIndex(101, 0)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationAtReturnsCorrectElevationInsideExtent() {
|
||||||
|
double elevation = 1000;
|
||||||
|
DiscreteElevationModel dDEM = new ConstantElevationDEM(EXT_100_100, elevation);
|
||||||
|
ContinuousElevationModel cDEM = new ContinuousElevationModel(dDEM);
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double x = rng.nextDouble() * 100d, y = rng.nextDouble() * 100d;
|
||||||
|
assertEquals(elevation, cDEM.elevationAt(pointForSampleIndex(x, y)), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationAtInterpolatesJustOutsideExtent() {
|
||||||
|
DiscreteElevationModel dDEM = new ConstantElevationDEM(EXT_100_100, 1000);
|
||||||
|
ContinuousElevationModel cDEM = new ContinuousElevationModel(dDEM);
|
||||||
|
assertEquals(500, cDEM.elevationAt(pointForSampleIndex(100.5, 10)), 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationAtReturnsCorrectInterpolatedElevation() {
|
||||||
|
DiscreteElevationModel dDEM = new ConstantSlopeDEM(EXT_100_100);
|
||||||
|
ContinuousElevationModel cDEM = new ContinuousElevationModel(dDEM);
|
||||||
|
Random rng = new Random();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double x = rng.nextDouble() * 100;
|
||||||
|
double y = rng.nextDouble() * 100;
|
||||||
|
assertEquals((x + y) * ConstantSlopeDEM.INTER_SAMPLE_DISTANCE, cDEM.elevationAt(pointForSampleIndex(x, y)), 1e-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationAtStaysWithinBoundsOnRandomTerrain() {
|
||||||
|
int maxElevation = 1000;
|
||||||
|
DiscreteElevationModel dDEM = new RandomElevationDEM(EXT_13_13, maxElevation);
|
||||||
|
ContinuousElevationModel cDEM = new ContinuousElevationModel(dDEM);
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double x = rng.nextDouble() * dDEM.extent().iX().size();
|
||||||
|
double y = rng.nextDouble() * dDEM.extent().iY().size();
|
||||||
|
double e = cDEM.elevationAt(pointForSampleIndex(x, y));
|
||||||
|
assertTrue(0 <= e && e <= maxElevation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void slopeAtReturnsCorrectInterpolatedSlope() {
|
||||||
|
DiscreteElevationModel dDEM = new ConstantSlopeDEM(EXT_100_100);
|
||||||
|
ContinuousElevationModel cDEM = new ContinuousElevationModel(dDEM);
|
||||||
|
Random rng = new Random();
|
||||||
|
double expectedSlope = Math.acos(1 / Math.sqrt(3));
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double x = 5 + rng.nextDouble() * 90;
|
||||||
|
double y = 5 + rng.nextDouble() * 90;
|
||||||
|
assertEquals(expectedSlope, cDEM.slopeAt(pointForSampleIndex(x, y)), 1e-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void slopeAtStaysWithinBoundsOnRandomTerrain() {
|
||||||
|
int maxElevation = 1000;
|
||||||
|
DiscreteElevationModel dDEM = new RandomElevationDEM(EXT_13_13, maxElevation);
|
||||||
|
ContinuousElevationModel cDEM = new ContinuousElevationModel(dDEM);
|
||||||
|
Random rng = newRandom();
|
||||||
|
for (int i = 0; i < RANDOM_ITERATIONS; ++i) {
|
||||||
|
double x = rng.nextDouble() * dDEM.extent().iX().size();
|
||||||
|
double y = rng.nextDouble() * dDEM.extent().iY().size();
|
||||||
|
double e = toDegrees(cDEM.slopeAt(pointForSampleIndex(x, y)));
|
||||||
|
assertTrue(0 <= e && e < 90);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GeoPoint pointForSampleIndex(double x, double y) {
|
||||||
|
return new GeoPoint(toRadians(x / 3600d), toRadians(y / 3600d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RandomElevationDEM implements DiscreteElevationModel {
|
||||||
|
private final Interval2D extent;
|
||||||
|
private final double[][] elevations;
|
||||||
|
|
||||||
|
public RandomElevationDEM(Interval2D extent, int maxElevation) {
|
||||||
|
this.extent = extent;
|
||||||
|
this.elevations = randomElevations(extent.iX().size(), extent.iY().size(), maxElevation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double[][] randomElevations(int width, int height, int maxElevation) {
|
||||||
|
Random rng = newRandom();
|
||||||
|
double[][] es = new double[width][height];
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
es[x][y] = rng.nextInt(maxElevation + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return es;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Interval2D extent() {
|
||||||
|
return extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double elevationSample(int x, int y) {
|
||||||
|
return elevations[x][y];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception { }
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConstantSlopeDEM implements DiscreteElevationModel {
|
||||||
|
public final static double INTER_SAMPLE_DISTANCE =
|
||||||
|
2d * Math.PI * 6_371_000d / (3600d * 360d);
|
||||||
|
|
||||||
|
private final Interval2D extent;
|
||||||
|
|
||||||
|
public ConstantSlopeDEM(Interval2D extent) {
|
||||||
|
this.extent = extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Interval2D extent() { return extent; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double elevationSample(int x, int y) {
|
||||||
|
return (x + y) * INTER_SAMPLE_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {}
|
||||||
|
}
|
70
Alpano/tests/ch/epfl/alpano/dem/DrawElevationProfile.java
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
|
||||||
|
final class DrawElevationProfile {
|
||||||
|
final static File HGT_FILE = new File("HGT"+File.separatorChar+"N46E006.hgt");
|
||||||
|
final static double MAX_ELEVATION = 1_500;
|
||||||
|
final static int LENGTH = 111_000;
|
||||||
|
final static double AZIMUTH = toRadians(27.97);
|
||||||
|
final static double LONGITUDE = toRadians(6.15432);
|
||||||
|
final static double LATITUDE = toRadians(46.20562);
|
||||||
|
final static int WIDTH = 800, HEIGHT = 100;
|
||||||
|
|
||||||
|
public static void main(String[] as) throws Exception {
|
||||||
|
DiscreteElevationModel dDEM =
|
||||||
|
new HgtDiscreteElevationModel(HGT_FILE);
|
||||||
|
ContinuousElevationModel cDEM =
|
||||||
|
new ContinuousElevationModel(dDEM);
|
||||||
|
GeoPoint o =
|
||||||
|
new GeoPoint(LONGITUDE, LATITUDE);
|
||||||
|
ElevationProfile p1 =
|
||||||
|
new ElevationProfile(cDEM, o, AZIMUTH, (int)(LENGTH/3));
|
||||||
|
GeoPoint gP1 = p1.positionAt((int)LENGTH/3);
|
||||||
|
ElevationProfile p2 =
|
||||||
|
new ElevationProfile(cDEM, p1.positionAt((int)LENGTH/3), AZIMUTH+toRadians(6.0),2*((int)LENGTH/3));
|
||||||
|
GeoPoint gP2 = p2.positionAt(2*((int)LENGTH/3));
|
||||||
|
|
||||||
|
|
||||||
|
ElevationProfile p3 = new ElevationProfile (cDEM,p2.positionAt(2*((int)LENGTH/3)), AZIMUTH, LENGTH);
|
||||||
|
|
||||||
|
|
||||||
|
ArrayList<GeoPoint> trekkingSpots = new ArrayList<GeoPoint>(Arrays.asList(o,gP1,gP2));
|
||||||
|
ArrayList<ElevationProfile> profiles= new ArrayList<ElevationProfile>();
|
||||||
|
for (int index = 0; index < trekkingSpots.size()-1; index++){
|
||||||
|
GeoPoint current= trekkingSpots.get(index);
|
||||||
|
GeoPoint next = trekkingSpots.get(index+1);
|
||||||
|
ElevationProfile evP= new ElevationProfile(cDEM, current,current.azimuthTo(next), current.distanceTo(next));
|
||||||
|
profiles.add(evP);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompositElevationProfile p = new CompositElevationProfile(profiles);
|
||||||
|
|
||||||
|
int BLACK = 0x00_00_00, WHITE = 0xFF_FF_FF;
|
||||||
|
|
||||||
|
BufferedImage i =
|
||||||
|
new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
|
||||||
|
for (int x = 0; x < WIDTH; ++x) {
|
||||||
|
double pX = x * (double) LENGTH / (WIDTH - 1);
|
||||||
|
double pY = p.elevationAt(pX);
|
||||||
|
int yL = (int)((pY / MAX_ELEVATION) * (HEIGHT - 1));
|
||||||
|
for (int y = 0; y < HEIGHT; ++y) {
|
||||||
|
int color = y < yL ? BLACK : WHITE;
|
||||||
|
i.setRGB(x, HEIGHT - 1 - y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dDEM.close();
|
||||||
|
|
||||||
|
ImageIO.write(i, "png", new File("tests/ch/epfl/alpano/dem/profile.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
Alpano/tests/ch/epfl/alpano/dem/DrawHgtDEM.java
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
|
||||||
|
final class DrawHgtDEM {
|
||||||
|
final static File HGT_FILE = new File("HGT"+File.separatorChar+"N46E006.hgt");
|
||||||
|
final static double ORIGIN_LON = toRadians(6.25);
|
||||||
|
final static double ORIGIN_LAT = toRadians(46.25);
|
||||||
|
final static double WIDTH = toRadians(0.5);
|
||||||
|
final static int IMAGE_SIZE = 300;
|
||||||
|
final static double MIN_ELEVATION = 200;
|
||||||
|
final static double MAX_ELEVATION = 1_500;
|
||||||
|
|
||||||
|
public static void main(String[] as) throws Exception {
|
||||||
|
DiscreteElevationModel dDEM =
|
||||||
|
new HgtDiscreteElevationModel(HGT_FILE);
|
||||||
|
ContinuousElevationModel cDEM =
|
||||||
|
new ContinuousElevationModel(dDEM);
|
||||||
|
|
||||||
|
double step = WIDTH / (IMAGE_SIZE - 1);
|
||||||
|
BufferedImage i = new BufferedImage(IMAGE_SIZE,
|
||||||
|
IMAGE_SIZE,
|
||||||
|
BufferedImage.TYPE_INT_RGB);
|
||||||
|
for (int x = 0; x < IMAGE_SIZE; ++x) {
|
||||||
|
double lon = ORIGIN_LON + x * step;
|
||||||
|
for (int y = 0; y < IMAGE_SIZE; ++y) {
|
||||||
|
double lat = ORIGIN_LAT + y * step;
|
||||||
|
GeoPoint p = new GeoPoint(lon, lat);
|
||||||
|
double el =
|
||||||
|
(cDEM.elevationAt(p) - MIN_ELEVATION)
|
||||||
|
/ (MAX_ELEVATION - MIN_ELEVATION);
|
||||||
|
i.setRGB(x, IMAGE_SIZE - 1 - y, gray(el));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dDEM.close();
|
||||||
|
|
||||||
|
|
||||||
|
ImageIO.write(i, "png", new File("tests/ch/epfl/alpano/dem/dem.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void drawDem(ContinuousElevationModel cDem){
|
||||||
|
|
||||||
|
double step = WIDTH / (IMAGE_SIZE - 1);
|
||||||
|
BufferedImage i = new BufferedImage(IMAGE_SIZE,
|
||||||
|
IMAGE_SIZE,
|
||||||
|
BufferedImage.TYPE_INT_RGB);
|
||||||
|
for (int x = 0; x < IMAGE_SIZE; ++x) {
|
||||||
|
double lon = ORIGIN_LON + x * step;
|
||||||
|
for (int y = 0; y < IMAGE_SIZE; ++y) {
|
||||||
|
double lat = ORIGIN_LAT + y * step;
|
||||||
|
GeoPoint p = new GeoPoint(lon, lat);
|
||||||
|
double el =
|
||||||
|
(cDem.elevationAt(p) - MIN_ELEVATION)
|
||||||
|
/ (MAX_ELEVATION - MIN_ELEVATION);
|
||||||
|
i.setRGB(x, IMAGE_SIZE - 1 - y, gray(el));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
ImageIO.write(i, "png", new File("tests/ch/epfl/alpano/dem/dem.png"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int gray(double v) {
|
||||||
|
double clampedV = max(0, min(v, 1));
|
||||||
|
int gray = (int) (255.9999 * clampedV);
|
||||||
|
return (gray << 16) | (gray << 8) | gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
Alpano/tests/ch/epfl/alpano/dem/DrawHgtDEM2.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
|
||||||
|
final class DrawHgtDEM2 {
|
||||||
|
final static double ORIGIN_LON = toRadians(6.0);
|
||||||
|
final static double ORIGIN_LAT = toRadians(45.0);
|
||||||
|
final static double WIDTH = toRadians(0.1);
|
||||||
|
final static int IMAGE_SIZE = 1200;
|
||||||
|
final static double MIN_ELEVATION = 00;
|
||||||
|
final static double MAX_ELEVATION = 500;
|
||||||
|
|
||||||
|
public static void main(String[] as) throws Exception {
|
||||||
|
DiscreteElevationModel dDEM = new HgtDiscreteElevationModel(null);
|
||||||
|
ContinuousElevationModel cDEM =
|
||||||
|
new ContinuousElevationModel(dDEM);
|
||||||
|
|
||||||
|
double step = WIDTH / (IMAGE_SIZE - 1);
|
||||||
|
BufferedImage i = new BufferedImage(IMAGE_SIZE,
|
||||||
|
IMAGE_SIZE, BufferedImage.TYPE_INT_RGB);
|
||||||
|
for (int x = 0; x < IMAGE_SIZE; ++x) {
|
||||||
|
double lon = ORIGIN_LON + x * step;
|
||||||
|
for (int y = 0; y < IMAGE_SIZE; ++y) {
|
||||||
|
double lat = ORIGIN_LAT + y * step;
|
||||||
|
GeoPoint p = new GeoPoint(lon, lat);
|
||||||
|
double el =
|
||||||
|
(cDEM.elevationAt(p) - MIN_ELEVATION)
|
||||||
|
/ (MAX_ELEVATION - MIN_ELEVATION);
|
||||||
|
i.setRGB(x, IMAGE_SIZE - 1 - y, gray(el));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dDEM.close();
|
||||||
|
ImageIO.write(i, "png", new File("tests/ch/epfl/alpano/dem/dem2.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int gray(double v) {
|
||||||
|
double clampedV = max(0, min(v, 1));
|
||||||
|
int gray = (int) (255.9999 * clampedV);
|
||||||
|
return (gray << 16) | (gray << 8) | gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
Alpano/tests/ch/epfl/alpano/dem/ElevationProfileTest.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import java.io.File;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
|
||||||
|
public class ElevationProfileTest {
|
||||||
|
|
||||||
|
final static File HGT_FILE = new File("HGT"+File.separatorChar+"N46E006.hgt");
|
||||||
|
final static double AZIMUTH = toRadians(45.0);
|
||||||
|
final static double LONGITUDE = toRadians(6.0);
|
||||||
|
final static double LATITUDE = toRadians(46.0);
|
||||||
|
final static int LENGTH = 111_000;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testElevationProfile() {
|
||||||
|
DiscreteElevationModel dDEM =
|
||||||
|
new HgtDiscreteElevationModel(HGT_FILE);
|
||||||
|
ContinuousElevationModel cDEM =
|
||||||
|
new ContinuousElevationModel(dDEM);
|
||||||
|
GeoPoint o =
|
||||||
|
new GeoPoint(LONGITUDE, LATITUDE);
|
||||||
|
ElevationProfile p =
|
||||||
|
new ElevationProfile(cDEM, o, AZIMUTH, LENGTH);
|
||||||
|
|
||||||
|
assertEquals(toRadians(6.09385),p.positionAt(10240).longitude(),0.00001);
|
||||||
|
assertEquals(toRadians(46.06508),p.positionAt(10240).latitude(),0.00001);
|
||||||
|
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
@Test
|
||||||
|
public void testElevationAt() {
|
||||||
|
fail("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPositionAt() {
|
||||||
|
fail("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSlopeAt() {
|
||||||
|
fail("Not yet implemented");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
134
Alpano/tests/ch/epfl/alpano/dem/ElevationProfileTestP.java
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import ch.epfl.alpano.Interval1D;
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
|
||||||
|
public class ElevationProfileTestP {
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public void constructorFailsWhenElevationModelIsNull() {
|
||||||
|
new ElevationProfile(null, new GeoPoint(0,0), 0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
|
public void constructorFailsWhenOriginIsNull() {
|
||||||
|
new ElevationProfile(newConstantSlopeDEM(), null, 0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWhenAzimuthIsNotCanonical() {
|
||||||
|
new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(0,0), 6.3, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWhenLengthIsZero() {
|
||||||
|
new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(0,0), 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void elevationAtFailsWhenXIsTooBig() {
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(0,0), 0, 100);
|
||||||
|
p.elevationAt(101);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationAtWorksOnConstantSlopeDEMGoingNorth() {
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(0,0), 0, 100_000);
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
double x = 100d * i;
|
||||||
|
assertEquals(x, p.elevationAt(x), 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationAtWorksOnConstantSlopeDEMGoingSouth() {
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(0,0), PI, 100_000);
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
double x = 100d * i;
|
||||||
|
assertEquals(-x, p.elevationAt(x), 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationAtWorksOnConstantSlopeDEMGoingEast() {
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(0,0), PI/2d, 100_000);
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
double x = 100d * i;
|
||||||
|
assertEquals(x, p.elevationAt(x), 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationAtWorksOnConstantSlopeDEMGoingWest() {
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(0,0), 3d*PI/2d, 100_000);
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
double x = 100d * i;
|
||||||
|
assertEquals(-x, p.elevationAt(x), 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void positionAtFailsWhenXIsTooBig() {
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(0,0), 0, 100);
|
||||||
|
p.positionAt(101);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void positionAtProducesConstantLongitudeWhenGoingNorth() {
|
||||||
|
double lon = toRadians(3);
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(lon,toRadians(40)), 0, 100_000);
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
double x = 500d * i;
|
||||||
|
assertEquals(lon, p.positionAt(x).longitude(), 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void positionAtProducesConstantLongitudeWhenGoingSouth() {
|
||||||
|
double lon = toRadians(3);
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(lon,toRadians(40)), PI, 100_000);
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
double x = 500d * i;
|
||||||
|
assertEquals(lon, p.positionAt(x).longitude(), 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void positionAtProducesConstantLatitudeWhenGoingEast() {
|
||||||
|
double lat = toRadians(40);
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(toRadians(3),lat), PI/2d, 100_000);
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
double x = 500d * i;
|
||||||
|
assertEquals(lat, p.positionAt(x).latitude(), 1e-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void positionAtProducesConstantLatitudeWhenGoingWest() {
|
||||||
|
double lat = toRadians(40);
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(toRadians(3),lat), 3d*PI/2d, 100_000);
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
double x = 500d * i;
|
||||||
|
assertEquals(lat, p.positionAt(x).latitude(), 1e-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void slopeAtFailsWhenXIsNegative() {
|
||||||
|
ElevationProfile p = new ElevationProfile(newConstantSlopeDEM(), new GeoPoint(0,0), 0, 100);
|
||||||
|
p.positionAt(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ContinuousElevationModel newConstantSlopeDEM() {
|
||||||
|
Interval2D extent = new Interval2D(
|
||||||
|
new Interval1D(-10_000, 10_000),
|
||||||
|
new Interval1D(-10_000, 10_000));
|
||||||
|
return new ContinuousElevationModel(new ConstantSlopeDEM(extent));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static ch.epfl.alpano.Preconditions.checkArgument;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class HgtDiscreteElevationModelTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHgtDiscreteElevationModel() {
|
||||||
|
String[] files = {"N45E006.hgt","N45E007.hgt","N45E008.hgt","N45E009.hgt",
|
||||||
|
"N45E010.hgt","N45E011.hgt","N46E006.hgt","N46E007.hgt",
|
||||||
|
"N46E008.hgt","N46E009.hgt","N46E010.hgt","N46E011.hgt",
|
||||||
|
"N47E006.hgt","N47E007.hgt","N47E008.hgt","N47E009.hgt",
|
||||||
|
"N47E010.hgt","N47E011.hgt","N00E000.hgt","S89W120"};
|
||||||
|
File file;
|
||||||
|
for(String txt: files){
|
||||||
|
file = new File("HGT"+File.separatorChar+txt);
|
||||||
|
if(file.exists())
|
||||||
|
new HgtDiscreteElevationModel(file);
|
||||||
|
}
|
||||||
|
assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=IllegalArgumentException.class)
|
||||||
|
public void testHgtDiscreteElevationModelFails() {
|
||||||
|
String[] files = {"N45E180.hgt","N45E007.hgt",};
|
||||||
|
File file;
|
||||||
|
for(String txt: files){
|
||||||
|
file = new File("HGT"+File.separatorChar+txt);
|
||||||
|
new HgtDiscreteElevationModel(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkFileNameSuccess() {
|
||||||
|
assertTrue(checkFileName("S00E000.hgt")!=null);
|
||||||
|
assertTrue(checkFileName("N00E000.hgt")!=null);
|
||||||
|
assertTrue(checkFileName("S00W129.hgt")!=null);
|
||||||
|
assertTrue(checkFileName("N00W000.hgt")!=null);
|
||||||
|
assertTrue(checkFileName("S69E139.hgt")!=null);
|
||||||
|
assertTrue(checkFileName("S90W180.hgt")!=null);
|
||||||
|
assertTrue(checkFileName("N90E180.hgt")!=null);
|
||||||
|
assertTrue(checkFileName("S90W180.hgt")!=null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=IllegalArgumentException.class)
|
||||||
|
public void checkFileNameFails() {
|
||||||
|
checkFileName("E46E006.hgt");
|
||||||
|
checkFileName("N4gE006.hgt");
|
||||||
|
checkFileName("N46E0g6.hgt");
|
||||||
|
checkFileName("N46E006lhgt");
|
||||||
|
checkFileName("N46E006.hGT");
|
||||||
|
checkFileName("N4gE006.hgt");
|
||||||
|
checkFileName("N46E0g6.hgt");
|
||||||
|
checkFileName("N46E006phgt");
|
||||||
|
checkFileName("Q99E006.hGT");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Matcher checkFileName(String txt){
|
||||||
|
final Pattern P = Pattern.compile("([NS])(\\d{2})([EW])(\\d{3})\\.hgt");
|
||||||
|
|
||||||
|
Matcher m = P.matcher(txt);
|
||||||
|
|
||||||
|
checkArgument((txt.length()==11 && m.find()));
|
||||||
|
int lat = Integer.parseInt(m.group(2));
|
||||||
|
int lon = Integer.parseInt(m.group(4));
|
||||||
|
checkArgument(!(lat>90 || lon>180 ));
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,154 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||||
|
import static java.nio.file.StandardOpenOption.CREATE_NEW;
|
||||||
|
import static java.nio.file.StandardOpenOption.READ;
|
||||||
|
import static java.nio.file.StandardOpenOption.WRITE;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ShortBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.FileChannel.MapMode;
|
||||||
|
import java.nio.file.FileVisitResult;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.SimpleFileVisitor;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.Interval1D;
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
|
||||||
|
public class HgtDiscreteElevationModelTestP {
|
||||||
|
private final static long HGT_FILE_SIZE = 3601L * 3601L * 2L;
|
||||||
|
private static Path FAKE_HGT_DIR, FAKE_HGT_FILE;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void createFakeHgtFiles() throws IOException {
|
||||||
|
Path fakeHgtDir = Files.createTempDirectory("hgt");
|
||||||
|
|
||||||
|
Path fakeHgtFile = fakeHgtDir.resolve("empty.hgt");
|
||||||
|
try (FileChannel c = FileChannel.open(fakeHgtFile, CREATE_NEW, READ, WRITE)) {
|
||||||
|
// make sure the empty hgt file has the right size
|
||||||
|
c.map(MapMode.READ_WRITE, 0, HGT_FILE_SIZE).asShortBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
FAKE_HGT_FILE = fakeHgtFile;
|
||||||
|
FAKE_HGT_DIR = fakeHgtDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void deleteFakeHgtFiles() throws IOException {
|
||||||
|
Files.walkFileTree(FAKE_HGT_DIR, new SimpleFileVisitor<Path>() {
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
|
Files.delete(file);
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||||
|
if (exc != null)
|
||||||
|
throw exc;
|
||||||
|
Files.delete(dir);
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithTooShortName() throws Exception {
|
||||||
|
createHgtDemWithFileNamed("N47E010.hg");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithInvalidLatitudeLetter() throws Exception {
|
||||||
|
createHgtDemWithFileNamed("N4xE010.hgt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithInvalidLongitudeLetter() throws Exception {
|
||||||
|
createHgtDemWithFileNamed("N47x010.hgt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithInexistantFile() throws Exception {
|
||||||
|
Path p = FAKE_HGT_DIR.resolve("N40E010.hgt");
|
||||||
|
try (DiscreteElevationModel d = new HgtDiscreteElevationModel(p.toFile())) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void constructorFailsWithEmptyFile() throws Exception {
|
||||||
|
File f = FAKE_HGT_DIR.resolve("N41E010.hgt").toFile();
|
||||||
|
try (FileOutputStream s = new FileOutputStream(f)) {
|
||||||
|
s.write(0);
|
||||||
|
}
|
||||||
|
try (DiscreteElevationModel d = new HgtDiscreteElevationModel(f)) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWorksInEcuador() throws Exception {
|
||||||
|
createHgtDemWithFileNamed("S03W078.hgt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extentMatchesFileName() throws Exception {
|
||||||
|
int[] lons = new int[] { 1, 7 };
|
||||||
|
int[] lats = new int[] { 1, 47 };
|
||||||
|
for (int lon: lons) {
|
||||||
|
for (int lat: lats) {
|
||||||
|
Interval2D expectedExtent = new Interval2D(
|
||||||
|
new Interval1D(lon * 3600, (lon + 1) * 3600),
|
||||||
|
new Interval1D(lat * 3600, (lat + 1) * 3600));
|
||||||
|
String hgtFileName = String.format("N%02dE%03d.hgt", lat, lon);
|
||||||
|
Path p = copyEmptyHgtFileAs(hgtFileName);
|
||||||
|
try (HgtDiscreteElevationModel dem = new HgtDiscreteElevationModel(p.toFile())) {
|
||||||
|
assertEquals(expectedExtent, dem.extent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void elevationSampleFailsForIndexNotInExtent() throws Exception {
|
||||||
|
String hgtFileName = "N02E002.hgt";
|
||||||
|
Path p = copyEmptyHgtFileAs(hgtFileName);
|
||||||
|
try (HgtDiscreteElevationModel dem = new HgtDiscreteElevationModel(p.toFile())) {
|
||||||
|
dem.elevationSample(10, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void elevationSampleIsCorrectInFourCorners() throws Exception {
|
||||||
|
Path p = FAKE_HGT_DIR.resolve("N01E001.hgt");
|
||||||
|
try (FileChannel c = FileChannel.open(p, CREATE_NEW, READ, WRITE)) {
|
||||||
|
ShortBuffer b = c.map(MapMode.READ_WRITE, 0, HGT_FILE_SIZE).asShortBuffer();
|
||||||
|
b.put(0, (short)1);
|
||||||
|
b.put(3600, (short) 2);
|
||||||
|
b.put(3601 * 3600, (short) 3);
|
||||||
|
b.put(3601 * 3601 - 1, (short) 4);
|
||||||
|
}
|
||||||
|
try (HgtDiscreteElevationModel dem = new HgtDiscreteElevationModel(p.toFile())) {
|
||||||
|
assertEquals(0, dem.elevationSample(4000, 4000), 1e-10);
|
||||||
|
assertEquals(1, dem.elevationSample(3600, 7200), 1e-10);
|
||||||
|
assertEquals(2, dem.elevationSample(7200, 7200), 1e-10);
|
||||||
|
assertEquals(3, dem.elevationSample(3600, 3600), 1e-10);
|
||||||
|
assertEquals(4, dem.elevationSample(7200, 3600), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createHgtDemWithFileNamed(String hgtFileName) throws Exception {
|
||||||
|
Path p = copyEmptyHgtFileAs(hgtFileName);
|
||||||
|
try (DiscreteElevationModel d = new HgtDiscreteElevationModel(p.toFile())) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path copyEmptyHgtFileAs(String hgtFileName) throws IOException {
|
||||||
|
return Files.copy(FAKE_HGT_FILE, FAKE_HGT_DIR.resolve(hgtFileName), REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
}
|
26
Alpano/tests/ch/epfl/alpano/dem/MemMapExample.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.nio.ShortBuffer;
|
||||||
|
import java.nio.channels.FileChannel.MapMode;
|
||||||
|
|
||||||
|
public class MemMapExample {
|
||||||
|
public static void main(String[] args)
|
||||||
|
throws IOException {
|
||||||
|
File f = new File("HGT"+File.separatorChar+"N46E007.hgt");
|
||||||
|
long l = f.length();
|
||||||
|
try (FileInputStream s = new FileInputStream(f)) {
|
||||||
|
ShortBuffer b = s.getChannel()
|
||||||
|
.map(MapMode.READ_ONLY, 0, l)
|
||||||
|
.asShortBuffer();
|
||||||
|
|
||||||
|
for (int i = 0; i <= 5; ++i)
|
||||||
|
System.out.println(b.get(i));
|
||||||
|
System.out.println("-----------------");
|
||||||
|
for (int i = 12967195; i < 12967201; ++i)
|
||||||
|
System.out.println(b.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
Alpano/tests/ch/epfl/alpano/dem/TestWavyDEM.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import static java.awt.image.BufferedImage.TYPE_INT_RGB;
|
||||||
|
import static java.lang.Math.*;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.*;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
public final class TestWavyDEM {
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
public static void main(String[] args)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
DiscreteElevationModel dDEM1 =
|
||||||
|
new WavyDEM(new Interval2D(new Interval1D(0, 50),
|
||||||
|
new Interval1D(0, 100)));
|
||||||
|
DiscreteElevationModel dDEM2 =
|
||||||
|
new WavyDEM(new Interval2D(new Interval1D(50, 100),
|
||||||
|
new Interval1D(0, 100)));
|
||||||
|
DiscreteElevationModel dDEM =
|
||||||
|
dDEM1.union(dDEM2);
|
||||||
|
ContinuousElevationModel cDEM =
|
||||||
|
new ContinuousElevationModel(dDEM);
|
||||||
|
|
||||||
|
int size = 300;
|
||||||
|
double scale = (100d / 3600d) / (size - 1);
|
||||||
|
BufferedImage elI =
|
||||||
|
new BufferedImage(size, size, TYPE_INT_RGB);
|
||||||
|
BufferedImage slI =
|
||||||
|
new BufferedImage(size, size, TYPE_INT_RGB);
|
||||||
|
for (int x = 0; x < size; ++x) {
|
||||||
|
for (int y = 0; y < size; ++y) {
|
||||||
|
GeoPoint p = new GeoPoint(toRadians(x * scale),
|
||||||
|
toRadians(y * scale));
|
||||||
|
double el = cDEM.elevationAt(p);
|
||||||
|
elI.setRGB(x, y, gray(el / 1000d));
|
||||||
|
|
||||||
|
double sl = cDEM.slopeAt(p);
|
||||||
|
slI.setRGB(x, y, gray(sl / (PI / 2d)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageIO.write(elI, "png", new File("tests/ch/epfl/alpano/dem/elevation.png"));
|
||||||
|
ImageIO.write(slI, "png", new File("tests/ch/epfl/alpano/dem/slope.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int gray(double v) {
|
||||||
|
double clampedV = max(0, min(v, 1));
|
||||||
|
int gray = (int) (255.9999 * clampedV);
|
||||||
|
return (gray << 16) | (gray << 8) | gray;
|
||||||
|
}
|
||||||
|
}
|
28
Alpano/tests/ch/epfl/alpano/dem/WavyDEM.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package ch.epfl.alpano.dem;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
import static java.lang.Math.sin;
|
||||||
|
import static java.lang.Math.cos;
|
||||||
|
|
||||||
|
final class WavyDEM implements DiscreteElevationModel {
|
||||||
|
private final static double PERIOD = 100, HEIGHT = 1000;
|
||||||
|
private final Interval2D extent;
|
||||||
|
|
||||||
|
public WavyDEM(Interval2D extent) {
|
||||||
|
this.extent = extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Interval2D extent() { return extent; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double elevationSample(int x, int y) {
|
||||||
|
double x1 = PI * 2d * x / PERIOD;
|
||||||
|
double y1 = PI * 2d * y / PERIOD;
|
||||||
|
return (1 + sin(x1) * cos(y1)) / 2d * HEIGHT;
|
||||||
|
}
|
||||||
|
}
|
BIN
Alpano/tests/ch/epfl/alpano/dem/dem.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
Alpano/tests/ch/epfl/alpano/dem/dem2.png
Normal file
After Width: | Height: | Size: 951 KiB |
BIN
Alpano/tests/ch/epfl/alpano/dem/elevation.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
Alpano/tests/ch/epfl/alpano/dem/mapView.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
Alpano/tests/ch/epfl/alpano/dem/mapzen.png
Normal file
After Width: | Height: | Size: 446 KiB |
BIN
Alpano/tests/ch/epfl/alpano/dem/mapzenSlope.png
Normal file
After Width: | Height: | Size: 252 KiB |
BIN
Alpano/tests/ch/epfl/alpano/dem/profile.png
Normal file
After Width: | Height: | Size: 800 B |
BIN
Alpano/tests/ch/epfl/alpano/dem/slope.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
Alpano/tests/ch/epfl/alpano/dem/testImage.png
Normal file
After Width: | Height: | Size: 354 KiB |
31
Alpano/tests/ch/epfl/alpano/gui/BeansUse.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
import static ch.epfl.alpano.gui.PredefinedPanoramas.*;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.gui.PanoramaParametersBean;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
public final class BeansUse extends Application {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) throws Exception {
|
||||||
|
PanoramaParametersBean bean =
|
||||||
|
new PanoramaParametersBean(NIESEN.get());
|
||||||
|
ObjectProperty<Integer> prop =
|
||||||
|
bean.ObserverLatitudeProperty();
|
||||||
|
|
||||||
|
prop.addListener((o, oV, nV) ->
|
||||||
|
System.out.printf(" %d -> %d (%s)%n", oV, nV, o));
|
||||||
|
System.out.println("set to 1");
|
||||||
|
prop.set(1);
|
||||||
|
System.out.println("set to 2");
|
||||||
|
prop.set(2);
|
||||||
|
|
||||||
|
Platform.exit();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class FixedPointStringConverterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFixedPointStringConverter() {
|
||||||
|
//fail("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFromStringString() {
|
||||||
|
FixedPointStringConverter f = new FixedPointStringConverter(4);
|
||||||
|
assertEquals(123457, (int)f.fromString("12.3456789"));
|
||||||
|
FixedPointStringConverter f1 = new FixedPointStringConverter(2);
|
||||||
|
assertEquals("1011", f1.toString(101100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToStringInteger() {
|
||||||
|
FixedPointStringConverter f = new FixedPointStringConverter(1);
|
||||||
|
assertEquals("67.8", f.toString(678));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class LabeledListStringConverterTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLabeledListStringConverter() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFromStringString() {
|
||||||
|
LabeledListStringConverter c = new LabeledListStringConverter("none","2x","4x");
|
||||||
|
System.out.println(c.fromString("2x"));
|
||||||
|
//assertEquals(1,c.fromString("2x"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToStringInteger() {
|
||||||
|
LabeledListStringConverter c = new LabeledListStringConverter("none","2x","4x");
|
||||||
|
|
||||||
|
assertEquals("none",c.toString(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
Alpano/tests/ch/epfl/alpano/gui/LabelizerTest.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package ch.epfl.alpano.gui;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.HgtDiscreteElevationModel;
|
||||||
|
import ch.epfl.alpano.summit.GazetteerParser;
|
||||||
|
import ch.epfl.alpano.summit.Summit;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
|
||||||
|
public class LabelizerTest {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLabels() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
|
||||||
|
List<Summit> listOfSummit = GazetteerParser.readSummitsFrom(new File("HGT/alps.txt"));
|
||||||
|
|
||||||
|
HgtDiscreteElevationModel dDem = new HgtDiscreteElevationModel(new File ("HGT/N46E007.hgt"));
|
||||||
|
ContinuousElevationModel cDem = new ContinuousElevationModel(dDem);
|
||||||
|
Labelizer l = new Labelizer(cDem, listOfSummit);
|
||||||
|
List<Node> n= l.labels(PredefinedPanoramas.NIESEN.get().panoramaComputeParameters());
|
||||||
|
int count =0;
|
||||||
|
for (Node node : n){
|
||||||
|
System.out.println(node);
|
||||||
|
if (node instanceof Text){
|
||||||
|
++count;
|
||||||
|
//System.out.println("node");
|
||||||
|
System.out.println(node.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println(count);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
Alpano/tests/ch/epfl/alpano/niesen-profile.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
Alpano/tests/ch/epfl/alpano/niesen-profileP.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
Alpano/tests/ch/epfl/alpano/niesen.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
Alpano/tests/ch/epfl/alpano/np.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
Alpano/tests/ch/epfl/alpano/out.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
Alpano/tests/ch/epfl/alpano/out2.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
41
Alpano/tests/ch/epfl/alpano/sigcheck/SignatureChecks_02.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package ch.epfl.alpano.sigcheck;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import ch.epfl.alpano.Interval1D;
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
|
||||||
|
final class SignatureChecks_02 {
|
||||||
|
private SignatureChecks_02() {}
|
||||||
|
|
||||||
|
String checkGeoPoint() {
|
||||||
|
double lon = 0, lat = 0;
|
||||||
|
GeoPoint p = new GeoPoint(lon, lat);
|
||||||
|
lon += p.longitude() + p.latitude();
|
||||||
|
double d = p.distanceTo(p);
|
||||||
|
double a = p.azimuthTo(p);
|
||||||
|
return String.valueOf(d) + a;
|
||||||
|
}
|
||||||
|
|
||||||
|
String checkInterval1D() {
|
||||||
|
int a = 0;
|
||||||
|
Interval1D i = new Interval1D(a, a);
|
||||||
|
a = i.includedFrom() + i.includedTo() + i.size() + i.sizeOfIntersectionWith(i);
|
||||||
|
boolean b = i.contains(a)
|
||||||
|
| i.isUnionableWith(i);
|
||||||
|
i = i.union(i.boundingUnion(i));
|
||||||
|
return i.toString() + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
String checkInterval2D() {
|
||||||
|
int a = 0;
|
||||||
|
Interval1D i1 = null;
|
||||||
|
Interval2D i2 = new Interval2D(i1, i1);
|
||||||
|
i1 = i2.iX();
|
||||||
|
i1 = i2.iY();
|
||||||
|
a = i2.size() + i2.sizeOfIntersectionWith(i2);
|
||||||
|
boolean b = i2.contains(a, a)
|
||||||
|
| i2.isUnionableWith(i2);
|
||||||
|
i2 = i2.union(i2.boundingUnion(i2));
|
||||||
|
return i2.toString() + b;
|
||||||
|
}
|
||||||
|
}
|
29
Alpano/tests/ch/epfl/alpano/sigcheck/SignatureChecks_03.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ch.epfl.alpano.sigcheck;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import ch.epfl.alpano.Interval2D;
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.DiscreteElevationModel;
|
||||||
|
|
||||||
|
final class SignatureChecks_03 {
|
||||||
|
private SignatureChecks_03() {}
|
||||||
|
|
||||||
|
String checkDiscElevationModel(DiscreteElevationModel d) throws Exception {
|
||||||
|
double a = DiscreteElevationModel.SAMPLES_PER_DEGREE * DiscreteElevationModel.SAMPLES_PER_RADIAN;
|
||||||
|
a = DiscreteElevationModel.sampleIndex(a);
|
||||||
|
Interval2D e = d.extent();
|
||||||
|
int v = 0;
|
||||||
|
a = d.elevationSample(v, v);
|
||||||
|
d = d.union(d);
|
||||||
|
d.close();
|
||||||
|
return d.toString() + e;
|
||||||
|
}
|
||||||
|
|
||||||
|
String checkContElevationModel() {
|
||||||
|
DiscreteElevationModel md = null;
|
||||||
|
ContinuousElevationModel m = new ContinuousElevationModel(md);
|
||||||
|
GeoPoint p = null;
|
||||||
|
double e = m.elevationAt(p) + m.slopeAt(p);
|
||||||
|
return String.valueOf(e);
|
||||||
|
}
|
||||||
|
}
|
30
Alpano/tests/ch/epfl/alpano/sigcheck/SignatureChecks_04.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package ch.epfl.alpano.sigcheck;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.DiscreteElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.ElevationProfile;
|
||||||
|
import ch.epfl.alpano.dem.HgtDiscreteElevationModel;
|
||||||
|
|
||||||
|
final class SignatureChecks_04 {
|
||||||
|
private SignatureChecks_04() {}
|
||||||
|
|
||||||
|
void checkHgtDiscreteElevationModel() {
|
||||||
|
File f = null;
|
||||||
|
DiscreteElevationModel m = new HgtDiscreteElevationModel(f);
|
||||||
|
System.out.println(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkElevationProfile() {
|
||||||
|
ContinuousElevationModel dem = null;
|
||||||
|
GeoPoint o = null;
|
||||||
|
double d = 0;
|
||||||
|
|
||||||
|
ElevationProfile p = new ElevationProfile(dem, o, d, d);
|
||||||
|
d = p.elevationAt(d);
|
||||||
|
o = p.positionAt(d);
|
||||||
|
d = p.slopeAt(d);
|
||||||
|
}
|
||||||
|
}
|
46
Alpano/tests/ch/epfl/alpano/sigcheck/SignatureChecks_05.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package ch.epfl.alpano.sigcheck;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.GeoPoint;
|
||||||
|
import ch.epfl.alpano.PanoramaParameters;
|
||||||
|
import ch.epfl.alpano.summit.GazetteerParser;
|
||||||
|
import ch.epfl.alpano.summit.Summit;
|
||||||
|
|
||||||
|
final class SignatureChecks_05 {
|
||||||
|
private SignatureChecks_05() {}
|
||||||
|
|
||||||
|
void checkSummit() {
|
||||||
|
GeoPoint p = null;
|
||||||
|
int e = 0;
|
||||||
|
Summit s = new Summit("", p, e);
|
||||||
|
p = s.position();
|
||||||
|
e = s.elevation();
|
||||||
|
System.out.println(s.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkGazetteerParser() throws IOException {
|
||||||
|
File f = null;
|
||||||
|
List<Summit> s = GazetteerParser.readSummitsFrom(f);
|
||||||
|
System.out.println(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkPanoramaParameters() {
|
||||||
|
GeoPoint p = null;
|
||||||
|
int i = 0;
|
||||||
|
double d = 0d;
|
||||||
|
PanoramaParameters pp = new PanoramaParameters(p, i, d, d, i, i, i);
|
||||||
|
p = pp.observerPosition();
|
||||||
|
i = pp.observerElevation();
|
||||||
|
d = pp.centerAzimuth();
|
||||||
|
d = pp.horizontalFieldOfView();
|
||||||
|
d = pp.verticalFieldOfView();
|
||||||
|
i = pp.width();
|
||||||
|
i = pp.height();
|
||||||
|
i = pp.maxDistance();
|
||||||
|
d = pp.xForAzimuth(pp.azimuthForX(d));
|
||||||
|
d = pp.yForAltitude(pp.altitudeForY(d));
|
||||||
|
}
|
||||||
|
}
|
49
Alpano/tests/ch/epfl/alpano/sigcheck/SignatureChecks_06.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package ch.epfl.alpano.sigcheck;
|
||||||
|
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
|
import ch.epfl.alpano.Panorama;
|
||||||
|
import ch.epfl.alpano.PanoramaComputer;
|
||||||
|
import ch.epfl.alpano.PanoramaParameters;
|
||||||
|
import ch.epfl.alpano.dem.ContinuousElevationModel;
|
||||||
|
import ch.epfl.alpano.dem.ElevationProfile;
|
||||||
|
|
||||||
|
final class SignatureChecks_06 {
|
||||||
|
private SignatureChecks_06() {}
|
||||||
|
|
||||||
|
void checkPanorama(Panorama p) {
|
||||||
|
PanoramaParameters pp = p.parameters();
|
||||||
|
int x = 0;
|
||||||
|
float d = p.distanceAt(x, x);
|
||||||
|
d = p.distanceAt(x, x, d);
|
||||||
|
d = p.longitudeAt(x, x);
|
||||||
|
d = p.latitudeAt(x, x);
|
||||||
|
d = p.elevationAt(x, x);
|
||||||
|
d = p.slopeAt(x, x);
|
||||||
|
checkPanoramaBuilder(pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkPanoramaBuilder(PanoramaParameters pp) {
|
||||||
|
Panorama.Builder b = new Panorama.Builder(pp);
|
||||||
|
int x = 0;
|
||||||
|
float d = 0;
|
||||||
|
b.setDistanceAt(x, x, d);
|
||||||
|
b.setLongitudeAt(x, x, d);
|
||||||
|
b.setLatitudeAt(x, x, d);
|
||||||
|
b.setElevationAt(x, x, d);
|
||||||
|
b.setSlopeAt(x, x, d);
|
||||||
|
Panorama p = b.build();
|
||||||
|
checkPanorama(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkPanoramaComputer(PanoramaParameters pp) {
|
||||||
|
ContinuousElevationModel d = null;
|
||||||
|
PanoramaComputer pc = new PanoramaComputer(d);
|
||||||
|
Panorama p = pc.computePanorama(pp);
|
||||||
|
checkPanorama(p);
|
||||||
|
double y = 0;
|
||||||
|
ElevationProfile pr = null;
|
||||||
|
DoubleUnaryOperator o = PanoramaComputer.rayToGroundDistance(pr, y, y);
|
||||||
|
System.out.println(o);
|
||||||
|
}
|
||||||
|
}
|