Compare commits

..

1 commit

Author SHA1 Message Date
3070cc3576 feat: Refactor to compute sunrise and sunset 2023-09-11 22:12:00 +02:00
9 changed files with 551 additions and 124 deletions

55
nbactions.xml Normal file
View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<actions>
<action>
<actionName>run</actionName>
<packagings>
<packaging>jar</packaging>
</packagings>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:3.0.0:exec</goal>
</goals>
<properties>
<exec.vmArgs></exec.vmArgs>
<exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
<exec.appArgs></exec.appArgs>
<exec.mainClass>ch.inf3.horizoncut.cli.Cli</exec.mainClass>
<exec.executable>java</exec.executable>
</properties>
</action>
<action>
<actionName>debug</actionName>
<packagings>
<packaging>jar</packaging>
</packagings>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:3.0.0:exec</goal>
</goals>
<properties>
<exec.vmArgs>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}</exec.vmArgs>
<exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
<exec.appArgs></exec.appArgs>
<exec.mainClass>ch.inf3.horizoncut.cli.Cli</exec.mainClass>
<exec.executable>java</exec.executable>
<jpda.listen>true</jpda.listen>
</properties>
</action>
<action>
<actionName>profile</actionName>
<packagings>
<packaging>jar</packaging>
</packagings>
<goals>
<goal>process-classes</goal>
<goal>org.codehaus.mojo:exec-maven-plugin:3.0.0:exec</goal>
</goals>
<properties>
<exec.vmArgs></exec.vmArgs>
<exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
<exec.mainClass>ch.inf3.horizoncut.cli.Cli</exec.mainClass>
<exec.executable>java</exec.executable>
<exec.appArgs></exec.appArgs>
</properties>
</action>
</actions>

View file

@ -81,5 +81,10 @@
<artifactId>gson</artifactId>
<version>2.8.7</version>
</dependency>
<dependency>
<groupId>net.e175.klaus</groupId>
<artifactId>solarpositioning</artifactId>
<version>0.1.10</version>
</dependency>
</dependencies>
</project>

View file

@ -47,11 +47,12 @@ public class Cli {
Tile atOrigin = tileMap.getByLatLon(eyePosition.gridLat, eyePosition.gridLon);
double eyeHeight = atOrigin.elevation(eyePosition.row, eyePosition.col);
eyePosition.setHeight(eyeHeight);
double minHorizAngle = 107; //107; // 135 ~Brunegghorn
double maxHorizAngle = 226; //226; // 197 ~Combin de Valsorey
DisplayCalculator dc = new DisplayCalculator(width, height, minHorizAngle, maxHorizAngle, eyePosition, eyeHeight);
DisplayCalculator dc = new DisplayCalculator(width, height, minHorizAngle, maxHorizAngle, eyePosition);
int maxDistance = 100_000;
int minDistance = 500;
@ -76,9 +77,7 @@ public class Cli {
final NavigableMap<Integer, DistanceLayer> dLayers = new TreeMap<>();
for (int d = maxDistance; d > minDistance; d -= SLICE_DISTANCE) {
final int cDist = d;
double[] data = computeAtDistance(tileMap, cDist, dc);
dLayers.put(d, new DistanceLayer(d - SLICE_DISTANCE, d, data));
dLayers.put(d, new DistanceLayer(DistanceLayer.atDistance(d - SLICE_DISTANCE, d, tileMap, dc)));
}
long layersEnd = System.currentTimeMillis();
@ -93,23 +92,23 @@ public class Cli {
.reduce((a, b) -> a.mergeWith(b))
.get();
LOG.info("Horizon is from {} to {}", horizonLayer.nearDistance, horizonLayer.farDistance);
LOG.info("Horizon is from {} to {}", horizonLayer.getNearDistance(), horizonLayer.getFarDistance());
Integer fKey = dLayers.firstKey();
Integer lKey = dLayers.ceilingKey(horizonLayer.farDistance);
Integer lKey = dLayers.ceilingKey(horizonLayer.getFarDistance());
LOG.info("Split horizon from {} ({}) to {} ({})", fKey, fKey, lKey, horizonLayer.farDistance);
LOG.info("Split horizon from {} ({}) to {} ({})", fKey, fKey, lKey, horizonLayer.getFarDistance());
final NavigableMap<Integer, DistanceLayer> hLayers = dLayers.subMap(fKey, true, lKey, true);
// We recompute the min and max distances after the cleanup of layers that
// are not useful for the display.
maxDistance = hLayers.values().stream()
.map(l -> l.farDistance)
.map(l -> l.getFarDistance())
.max(Integer::compareTo)
.get();
minDistance = hLayers.values().stream()
.map(l -> l.nearDistance)
.map(l -> l.getNearDistance())
.min(Integer::compareTo)
.get();
@ -126,12 +125,12 @@ public class Cli {
// point.
Predicate<ObservedFeature> isVisibleFilter = (feature) -> {
int x = dc.getXAtBearing(feature.bearing);
return DistanceLayer.isVisible(hLayers.values(), feature.distance, x, feature.visibleAngle);
int x = dc.getXAtBearing(feature.getBearing());
return DistanceLayer.isVisible(hLayers.values(), feature.getDistance(), x, feature.getAngle());
};
TreeMap<Integer, ObservedFeature> labeledFeatures = allFeatures.stream()
.filter(f -> dc.isDisplayed(f.bearing, f.visibleAngle))
.filter(f -> dc.isDisplayed(f.getBearing(), f.getAngle()))
.filter(isVisibleFilter)
.collect(Collectors.toMap(f -> f.getObservedX(), f -> f, (a, b) -> Feature.highest(a, b), TreeMap::new));
@ -156,7 +155,7 @@ public class Cli {
}
for (ObservedFeature feature : labeledFeatures.values()) {
var featureLayer = hLayers.floorEntry(feature.distance);
var featureLayer = hLayers.floorEntry(feature.getDistance());
if (featureLayer == null) {
LOG.info("Unable to find layer for feature {}", feature);
continue;
@ -206,7 +205,7 @@ public class Cli {
.collect(Collectors.toList());
LOG.info("Merging layers {} and {} ({}) would generate {} feature score: {}",
cLayer.nearDistance, nLayer.nearDistance, bothScore,
cLayer.getNearDistance(), nLayer.getNearDistance(), bothScore,
mergeScore, continueMerge);
if (!missingFeatures.isEmpty()) {
@ -236,7 +235,7 @@ public class Cli {
// We loop in descending order to avoid writing further points on top
// of nearer points.
for (DistanceLayer layer : sortedLayers) {
float dPos = (float) ((maxDistance - layer.nearDistance) / (maxDistance * 1.0f));
float dPos = (float) ((maxDistance - layer.getNearDistance()) / (maxDistance * 1.0f));
Color layerColor = Color.getHSBColor(dPos * 1f, 1.0f, 1.0f);
g.setColor(layerColor);
@ -244,7 +243,9 @@ public class Cli {
Polygon p = new Polygon();
p.addPoint(0, height);
for (int x = 0; x < width; x++) {
double visibleAngle = layer.data[x];
double bearing = dc.getBearingAtX(x);
double visibleAngle = layer.getAtBearing(bearing);
int y = dc.getYAtAngle(visibleAngle);
p.addPoint(x, y);
@ -254,15 +255,15 @@ public class Cli {
g.drawPolygon(p);
for (ObservedFeature feature : layer.getFeatures()) {
if (!dc.isDisplayed(feature.bearing, feature.visibleAngle)) {
LOG.debug("Feature {} is not visible at bearing {} and angle {}", feature.name, feature.bearing, feature.visibleAngle);
if (!dc.isDisplayed(feature.getBearing(), feature.getAngle())) {
LOG.debug("Feature {} is not visible at bearing {} and angle {}", feature.name, feature.getBearing(), feature.getAngle());
continue;
}
int x = dc.getXAtBearing(feature.bearing);
int y = dc.getYAtAngle(feature.visibleAngle);
int x = dc.getXAtBearing(feature.getBearing());
int y = dc.getYAtAngle(feature.getAngle());
boolean isVisible = DistanceLayer.isVisible(hLayers.values(), feature.distance, x, feature.visibleAngle);
boolean isVisible = DistanceLayer.isVisible(hLayers.values(), feature.getDistance(), x, feature.getAngle());
if (!isVisible) {
LOG.debug("Feature {} is hidden behind terrain", feature.name);
continue;
@ -283,22 +284,22 @@ public class Cli {
g.setFont(font);
for (ObservedFeature feature : labeledFeatures.values()) {
if (!dc.isDisplayed(feature.bearing, feature.visibleAngle)) {
LOG.debug("Feature {} is not visible at bearing {} and angle {}", feature.name, feature.bearing, feature.visibleAngle);
if (!dc.isDisplayed(feature.getBearing(), feature.getAngle())) {
LOG.debug("Feature {} is not visible at bearing {} and angle {}", feature.name, feature.getBearing(), feature.getAngle());
continue;
}
int x = dc.getXAtBearing(feature.bearing);
int x = dc.getXAtBearing(feature.getBearing());
boolean isVisible = DistanceLayer.isVisible(hLayers.values(), feature.distance, x, feature.visibleAngle);
boolean isVisible = DistanceLayer.isVisible(hLayers.values(), feature.getDistance(), x, feature.getAngle());
if (!isVisible) {
LOG.debug("Feature {} is not visible, hidden behind terrain.", feature.name);
continue;
}
int y = dc.getYAtAngle(feature.visibleAngle);
int y = dc.getYAtAngle(feature.getAngle());
LOG.debug("Peak {} is at bearing {} ({}x{})", feature.name, feature.bearing, x, y);
LOG.debug("Peak {} is at bearing {} ({}x{})", feature.name, feature.getBearing(), x, y);
String title = String.format("%s (%.0f)", feature.name, feature.getScore());
@ -334,24 +335,4 @@ public class Cli {
}
}
static final double[] computeAtDistance(TileMap tileMap, int d, DisplayCalculator dc) {
double[] output = new double[dc.getWidth()];
for (int x = 0; x < dc.getWidth(); x += 1) {
double bearing = dc.getBearingAtX(x);
Position target = Position.get(dc.getEyePosition(), Math.toRadians(bearing), d);
Tile atPos = tileMap.getByLatLon(target.gridLat, target.gridLon);
double elevation = atPos.elevation(target.row, target.col);
int dMeters = dc.getEyePosition().distanceToMeters(target);
double visibleAngle = dc.calculateVisibilityAngle(elevation, dMeters);
output[x] = visibleAngle;
}
return output;
}
}

View file

@ -0,0 +1,259 @@
package ch.inf3.horizoncut.cli;
import ch.inf3.horizoncut.data.BasicLayer;
import ch.inf3.horizoncut.data.DisplayCalculator;
import ch.inf3.horizoncut.data.Position;
import ch.inf3.horizoncut.data.Tile;
import ch.inf3.horizoncut.data.TileMap;
import ch.inf3.horizoncut.data.TileReader;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.NavigableMap;
import java.util.TreeMap;
import javax.imageio.ImageIO;
import net.e175.klaus.solarpositioning.AzimuthZenithAngle;
import net.e175.klaus.solarpositioning.DeltaT;
import net.e175.klaus.solarpositioning.SPA;
import net.e175.klaus.solarpositioning.SunriseTransitSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Cli2 {
private static final Logger LOG = LoggerFactory.getLogger(Cli2.class);
private static final int SLICE_DISTANCE = 50;
public static void main(String[] args) throws Exception {
long initStart = System.currentTimeMillis();
Position eyePosition = new Position(46.28747, 7.44159);
TileMap tileMap = new TileReader("/home/valdor/Downloads/SRTM1/").readHGTFiles(eyePosition, 2);
final int width = 7200;
final int height = 1000;
Tile atOrigin = tileMap.getByLatLon(eyePosition.gridLat, eyePosition.gridLon);
double eyeHeight = atOrigin.elevation(eyePosition.row, eyePosition.col) + 5;
eyePosition.setHeight(eyeHeight);
double minHorizAngle = 0;
double maxHorizAngle = 359;
DisplayCalculator dc = new DisplayCalculator(width, height, minHorizAngle, maxHorizAngle, eyePosition);
int maxDistance = 100_000;
int minDistance = 100;
long initStop = System.currentTimeMillis();
LOG.info("Initialized system for computations from {}@{}m with distances of {} to {} ({} ms)",
eyePosition.getLatLon(), eyeHeight, minDistance, maxDistance, initStop - initStart);
long layersStart = System.currentTimeMillis();
final NavigableMap<Integer, BasicLayer> dLayers = new TreeMap<>();
for (int d = maxDistance; d > minDistance; d -= SLICE_DISTANCE) {
dLayers.put(d, BasicLayer.atDistance(d - SLICE_DISTANCE, d, tileMap, dc));
}
long layersEnd = System.currentTimeMillis();
LOG.info("Computed {} layers ({} ms)", dLayers.size(), layersEnd - layersStart);
long flattenStart = System.currentTimeMillis();
BasicLayer horizonLayer = dLayers.values().stream()
.reduce((a, b) -> a.mergeWith(b))
.get();
long flattenEnd = System.currentTimeMillis();
LOG.info("Merged horizon from {} layers in {} ms - {} to {}", dLayers.size(),
flattenEnd - flattenStart,
horizonLayer.getNearDistance(), horizonLayer.getFarDistance());
computeSunVisible(horizonLayer, eyePosition);
long sunStart = System.currentTimeMillis();
AzimuthZenithAngle[] sunPosition = new AzimuthZenithAngle[24];
for (int i = 0; i < 24; i += 1) {
ZonedDateTime dateTime = ZonedDateTime.now()
.withHour(i)
.withMinute(0);
// replace SPA with Grena3 as needed
AzimuthZenithAngle position = SPA.calculateSolarPosition(
dateTime,
eyePosition.getLatitude(), // latitude (degrees)
eyePosition.getLongitude(), // longitude (degrees)
eyePosition.getHeight(), // elevation (m)
DeltaT.estimate(dateTime.toLocalDate())); // delta T (s)
sunPosition[i] = position;
}
ZonedDateTime dt = ZonedDateTime.now();
SunriseTransitSet res = SPA.calculateSunriseTransitSet(dt,
eyePosition.getLatitude(),
eyePosition.getLongitude(),
DeltaT.estimate(dt.toLocalDate()));
LOG.info("Transits are {}", res);
long sunEnd = System.currentTimeMillis();
LOG.info("Computed sun position in {}ms", sunEnd - sunStart);
long renderStart = System.currentTimeMillis();
dc.setMinMaxVertAngle(80, 180);
BufferedImage bufferedImg = new BufferedImage(width, height + 1, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bufferedImg.getGraphics();
g.setColor(Color.RED);
Polygon p = new Polygon();
p.addPoint(0, height);
for (int x = 0; x < width; x++) {
double bearing = dc.getBearingAtX(x);
double visibleAngle = horizonLayer.getAtBearing(bearing);
int y = dc.getYAtAngle(visibleAngle);
p.addPoint(x, y);
}
p.addPoint(width, height);
g.drawPolygon(p);
for (int i = 0; i < 360; i += 10) {
if (i % 90 == 0) {
g.setColor(Color.WHITE);
} else {
g.setColor(Color.GRAY);
}
int x = dc.getXAtBearing(i);
g.drawLine(x, 0, x, height);
g.drawString(i + "", x + 5, 20);
}
for (double i = dc.getMinHorizAngle(); i < dc.getMaxHorizAngle(); i += 5) {
if (i % 90 == 0) {
g.setColor(Color.WHITE);
} else {
g.setColor(Color.GRAY);
}
int y = dc.getYAtAngle(i);
g.drawLine(0, y, width, y);
g.drawString(i + "", 20, y + 5);
}
g.setColor(Color.YELLOW);
for (int i = 0; i < 24; i++){
AzimuthZenithAngle sp = sunPosition[i];
int x = dc.getXAtBearing(sp.getAzimuth());
int y = dc.getYAtAngle(180 - sp.getZenithAngle());
g.fillOval(x - 10, y - 10, 20, 20);
g.drawString(String.format("%02d:00", i), x - 40, y);
}
long renderEnd = System.currentTimeMillis();
LOG.info("Rendered panorama ({} ms)", renderEnd - renderStart);
g.dispose();
try {
String imageName = String.format("image-%05d.png", 1);
File outputfile = new File("/home/valdor/", imageName);
outputfile.createNewFile();
ImageIO.write(bufferedImg, "png", outputfile);
} catch (IOException ex) {
LOG.error("Exception while writing image", ex);
}
}
public static AzimuthZenithAngle computeSunAtTime(Position eyePosition, ZonedDateTime dateTime) {
return SPA.calculateSolarPosition(
dateTime,
eyePosition.getLatitude(), // latitude (degrees)
eyePosition.getLongitude(), // longitude (degrees)
eyePosition.getHeight(), // elevation (m)
DeltaT.estimate(dateTime.toLocalDate())); // delta T (s)
}
public static ZonedDateTime computeSunVisible(BasicLayer dl, Position eyePosition, ZonedDateTime beforeDate, ZonedDateTime afterDate) {
long rangeDiffSeconds = afterDate.toEpochSecond() - beforeDate.toEpochSecond();
if (rangeDiffSeconds < 60) {
return beforeDate;
}
ZonedDateTime middleDate = beforeDate.plusSeconds(rangeDiffSeconds / 2);
AzimuthZenithAngle beforePosition = computeSunAtTime(eyePosition, beforeDate);
AzimuthZenithAngle middlePosition = computeSunAtTime(eyePosition, middleDate);
AzimuthZenithAngle afterPosition = computeSunAtTime(eyePosition, afterDate);
boolean sunBeforeVisible = isSunVisible(dl, beforePosition);
boolean sunMiddleVisible = isSunVisible(dl, middlePosition);
boolean sunAfterVisible = isSunVisible(dl, afterPosition);
if (sunBeforeVisible && sunAfterVisible) {
throw new IllegalArgumentException("Sun is visible both before and after. Invalid dates");
}
if (sunBeforeVisible) {
if (sunMiddleVisible) {
return computeSunVisible(dl, eyePosition, middleDate, afterDate);
} else {
return computeSunVisible(dl, eyePosition, beforeDate, middleDate);
}
} else {
if (sunMiddleVisible) {
return computeSunVisible(dl, eyePosition, beforeDate, middleDate);
} else {
return computeSunVisible(dl, eyePosition, middleDate, afterDate);
}
}
}
public static void computeSunVisible(BasicLayer dl, Position eyePosition) {
ZonedDateTime sunriseDate = computeSunVisible(dl, eyePosition,
ZonedDateTime.now()
.withHour(3)
.withMinute(0)
.withSecond(0),
ZonedDateTime.now()
.withHour(12)
.withMinute(0)
.withSecond(0));
LOG.info("Found sunrise at {}", sunriseDate);
ZonedDateTime sunsetDate = computeSunVisible(dl, eyePosition,
ZonedDateTime.now()
.withHour(15)
.withMinute(0),
ZonedDateTime.now()
.withHour(23)
.withMinute(0));
LOG.info("Found sunset at {}", sunsetDate);
}
public static boolean isSunVisible(BasicLayer dl, AzimuthZenithAngle sp) {
double sunBearing = sp.getAzimuth();
double sunAngle = 180 - sp.getZenithAngle();
double terrainAngle = dl.getAtBearing(sunBearing);
return sunAngle >= terrainAngle;
}
}

View file

@ -0,0 +1,153 @@
package ch.inf3.horizoncut.data;
import java.util.Arrays;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BasicLayer implements Comparable<BasicLayer>, Cloneable {
private static final Logger LOG = LoggerFactory.getLogger(BasicLayer.class);
protected final int nearDistance;
protected final int farDistance;
protected final double[] data;
protected final DisplayCalculator dc;
protected BasicLayer(int nearDistance, int farDistance, double[] data, DisplayCalculator dc) {
this.nearDistance = nearDistance;
this.farDistance = farDistance;
this.data = data;
this.dc = dc;
}
public static BasicLayer atDistance(int nearDistance, int farDistance, TileMap tileMap, DisplayCalculator dc) {
double[] output = new double[dc.getWidth()];
for (int x = 0; x < dc.getWidth(); x += 1) {
double bearing = dc.getBearingAtX(x);
Position target = Position.get(dc.getEyePosition(), Math.toRadians(bearing), nearDistance);
Tile atPos = tileMap.getByLatLon(target.gridLat, target.gridLon);
double elevation = atPos.elevation(target.row, target.col);
int dMeters = dc.getEyePosition().distanceToMeters(target);
double visibleAngle = dc.calculateVisibilityAngle(elevation, dMeters);
output[x] = visibleAngle;
}
return new BasicLayer(nearDistance, farDistance, output, dc);
}
public int getNearDistance() {
return nearDistance;
}
public int getFarDistance() {
return farDistance;
}
public double getAtBearing(double bearing) {
int x = dc.getXAtBearing(bearing);
return data[x];
}
@Override
public int compareTo(BasicLayer other) {
return Integer.compare(this.nearDistance, other.nearDistance);
}
@Override
protected BasicLayer clone() {
double[] cloneData = Arrays.copyOf(data, data.length);
return new BasicLayer(nearDistance, farDistance, cloneData, dc);
}
public BasicLayer mergeWith(BasicLayer other) {
BasicLayer near = this.compareTo(other) <= 0 ? this : other;
BasicLayer far = this.compareTo(other) > 0 ? this : other;
double[] output = new double[near.data.length];
int countNear = 0;
int countFar = 0;
for (int d = 0; d < near.data.length; d += 1) {
double yNear = near.data[d];
double yFar = far.data[d];
if (yNear >= yFar) {
output[d] = yNear;
countNear += 1;
} else {
output[d] = yFar;
countFar += 1;
}
}
int tolerance = near.data.length / 100;
int outputNearDistance = (countNear > tolerance) ? near.nearDistance : far.nearDistance;
int outputFarDistance = (countFar > tolerance) ? far.farDistance : near.farDistance;
LOG.debug("Merging {}-{} ({}) with {}-{} ({}) {}",
near.nearDistance, near.farDistance, countNear,
far.nearDistance, far.farDistance, countFar,
tolerance);
return new BasicLayer(outputNearDistance, outputFarDistance, output, dc);
}
/**
* Returns true if the given layer is always lower than the current layer.
*
* @param other
* @return
*/
public boolean isLower(BasicLayer other) {
for (int d = 0; d < this.data.length; d += 1) {
double yThis = this.data[d];
double yOther = other.data[d];
if (yThis == 0 || yOther == 0) {
continue;
}
if (yOther >= yThis) {
return false;
}
}
return true;
}
public double getMax() {
return Arrays.stream(this.data)
.boxed()
.filter(e -> !e.isNaN())
.max(Double::compareTo)
.orElse(0.0);
}
public static boolean isVisible(Collection<? extends BasicLayer> sortedLayers, int distance, int x, double visibleAngle) {
for (BasicLayer l : sortedLayers) {
if (l.farDistance > (distance - 200)) {
return true;
}
double c = l.data[x];
if (c >= visibleAngle) {
return false;
}
}
return true;
}
}

View file

@ -11,12 +11,11 @@ public class DisplayCalculator {
final double maxHorizAngle;
final Position eyePosition;
final double eyeHeight;
double minVertAngle;
double maxVertAngle;
public DisplayCalculator(int width, int height, double minHorizAngle, double maxHorizAngle, Position eyePosition, double eyeHeight) {
public DisplayCalculator(int width, int height, double minHorizAngle, double maxHorizAngle, Position eyePosition) {
this.width = width;
this.height = height;
@ -24,7 +23,6 @@ public class DisplayCalculator {
this.maxHorizAngle = maxHorizAngle;
this.eyePosition = eyePosition;
this.eyeHeight = eyeHeight;
}
public int getWidth() {
@ -44,7 +42,7 @@ public class DisplayCalculator {
}
public double getEyeHeight() {
return eyeHeight;
return eyePosition.getHeight();
}
public Position getEyePosition() {
@ -89,6 +87,11 @@ public class DisplayCalculator {
return y;
}
public void setMinMaxVertAngle(double minVertAngle, double maxVertAngle) {
this.minVertAngle = minVertAngle;
this.maxVertAngle = maxVertAngle;
}
public void updateMinMaxVertAngle(Collection<DistanceLayer> nLayers) {
// We dont want to compte the min angle to avoid looking at the ground
@ -101,14 +104,14 @@ public class DisplayCalculator {
}
public double calculateVisibilityAngle(double targetVisibleHeight, int targetDistance) {
if (eyeHeight == targetVisibleHeight) {
if (eyePosition.getHeight() == targetVisibleHeight) {
return 90.0;
} else if (eyeHeight < targetVisibleHeight) {
double op = targetVisibleHeight - eyeHeight;
} else if (eyePosition.getHeight() < targetVisibleHeight) {
double op = targetVisibleHeight - eyePosition.getHeight();
double ad = targetDistance;
return Math.toDegrees(Math.atan(op / ad)) + 90;
} else {
return Math.toDegrees(Math.atan(targetDistance / (eyeHeight - targetVisibleHeight)));
return Math.toDegrees(Math.atan(targetDistance / (eyePosition.getHeight() - targetVisibleHeight)));
}
}
}

View file

@ -10,36 +10,32 @@ import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DistanceLayer implements Comparable<DistanceLayer>, Cloneable {
public class DistanceLayer extends BasicLayer {
private static final Logger LOG = LoggerFactory.getLogger(DistanceLayer.class);
public final int nearDistance;
public final int farDistance;
public final double[] data;
public final Collection<ObservedFeature> horizonFeatures;
public final Collection<ObservedFeature> nonHorizonFeatures;
public DistanceLayer(int nearDistance, int farDistance, double[] data) {
this(nearDistance, farDistance, data, new ArrayList<>(), new ArrayList<>());
public DistanceLayer(int nearDistance, int farDistance, double[] data, DisplayCalculator dc) {
this(nearDistance, farDistance, data, new ArrayList<>(), new ArrayList<>(), dc);
}
private DistanceLayer(int nearDistance, int farDistance, double[] data,
Collection<ObservedFeature> horizonFeatures, Collection<ObservedFeature> nonHorizonFeatures) {
this.nearDistance = nearDistance;
this.farDistance = farDistance;
this.data = data;
Collection<ObservedFeature> horizonFeatures, Collection<ObservedFeature> nonHorizonFeatures,
DisplayCalculator dc) {
super(nearDistance, farDistance, data, dc);
this.horizonFeatures = horizonFeatures;
this.nonHorizonFeatures = nonHorizonFeatures;
}
@Override
public int compareTo(DistanceLayer other) {
return Integer.compare(this.nearDistance, other.nearDistance);
public DistanceLayer(BasicLayer atDistance) {
this(atDistance.nearDistance, atDistance.farDistance,
Arrays.copyOf(atDistance.data, atDistance.data.length),
atDistance.dc);
}
@Override
protected DistanceLayer clone() {
double[] cloneData = Arrays.copyOf(data, data.length);
@ -47,7 +43,7 @@ public class DistanceLayer implements Comparable<DistanceLayer>, Cloneable {
Collection<ObservedFeature> horizonFeatures = new ArrayList<>(this.horizonFeatures);
Collection<ObservedFeature> nonHorizonFeatures = new ArrayList<>(this.nonHorizonFeatures);
return new DistanceLayer(nearDistance, farDistance, cloneData, horizonFeatures, nonHorizonFeatures);
return new DistanceLayer(nearDistance, farDistance, cloneData, horizonFeatures, nonHorizonFeatures, dc);
}
public DistanceLayer mergeWith(DistanceLayer other) {
@ -90,30 +86,7 @@ public class DistanceLayer implements Comparable<DistanceLayer>, Cloneable {
outNonHorizonFeatures.addAll(this.nonHorizonFeatures);
outNonHorizonFeatures.addAll(other.nonHorizonFeatures);
return new DistanceLayer(outputNearDistance, outputFarDistance, output, outHorizonFeatures, outNonHorizonFeatures);
}
/**
* Returns true if the given layer is always lower than the current layer.
*
* @param other
* @return
*/
public boolean isLower(DistanceLayer other) {
for (int d = 0; d < this.data.length; d += 1) {
double yThis = this.data[d];
double yOther = other.data[d];
if (yThis == 0 || yOther == 0) {
continue;
}
if (yOther >= yThis) {
return false;
}
}
return true;
return new DistanceLayer(outputNearDistance, outputFarDistance, output, outHorizonFeatures, outNonHorizonFeatures, dc);
}
public void addFeature(ObservedFeature feature) {
@ -140,7 +113,7 @@ public class DistanceLayer implements Comparable<DistanceLayer>, Cloneable {
private static boolean isAtHorizon(ObservedFeature feature, double[] data) {
int xFeature = feature.getObservedX();
double angleFeature = feature.visibleAngle;
double angleFeature = feature.getAngle();
double angleThis = data[xFeature];
@ -155,28 +128,4 @@ public class DistanceLayer implements Comparable<DistanceLayer>, Cloneable {
return this.horizonFeatures.size();
}
public double getMax() {
return Arrays.stream(this.data)
.boxed()
.filter(e -> !e.isNaN())
.max(Double::compareTo)
.orElse(0.0);
}
public static boolean isVisible(Collection<DistanceLayer> sortedLayers, int distance, int x, double visibleAngle) {
for (DistanceLayer l : sortedLayers) {
if (l.farDistance > (distance - 200)) {
return true;
}
double c = l.data[x];
if (c >= visibleAngle) {
return false;
}
}
return true;
}
}

View file

@ -2,9 +2,9 @@ package ch.inf3.horizoncut.data;
public class ObservedFeature extends Feature implements Comparable<ObservedFeature> {
public final int distance;
public final double visibleAngle;
public final double bearing;
private final int distance;
private final double visibleAngle;
private final double bearing;
private final DisplayCalculator observator;
@ -31,4 +31,16 @@ public class ObservedFeature extends Feature implements Comparable<ObservedFeatu
public int getObservedY() {
return observator.getYAtAngle(visibleAngle);
}
public double getBearing() {
return this.bearing;
}
public double getAngle() {
return this.visibleAngle;
}
public int getDistance() {
return distance;
}
}

View file

@ -13,6 +13,8 @@ public class Position {
private final double lat;
private final double lon;
private double height;
private static final Logger LOG = LoggerFactory.getLogger(Position.class);
@ -119,6 +121,14 @@ public class Position {
public String getLatLon() {
return String.format("(%.4f, %.4f)", getLatitude(), getLongitude());
}
public void setHeight(double height) {
this.height = height;
}
public double getHeight() {
return this.height;
}
@Override
public int hashCode() {
@ -160,7 +170,7 @@ public class Position {
@Override
public String toString() {
return "Position[gridLat=" + gridLat + ", gridLon=" + gridLon + ", col=" + col + ", row=" + row + "]";
return "Position[gridLat=" + gridLat + ", gridLon=" + gridLon + ", col=" + col + ", row=" + row + ", height=" + height + "]";
}
}