Disabled external gits
							
								
								
									
										1
									
								
								Alpano
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
							
								
								
									
										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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||