feat: Refactor to use TreeMap directly for layers
This commit is contained in:
parent
764688e58a
commit
6d541bbe49
2 changed files with 44 additions and 30 deletions
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.NavigableMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -72,58 +73,61 @@ public class Cli {
|
||||||
eyePosition.getLatLon(), eyeHeight, minDistance, maxDistance, initStop - initStart);
|
eyePosition.getLatLon(), eyeHeight, minDistance, maxDistance, initStop - initStart);
|
||||||
|
|
||||||
long layersStart = System.currentTimeMillis();
|
long layersStart = System.currentTimeMillis();
|
||||||
Collection<DistanceLayer> reversedLayers = new ArrayList<>(maxDistance);
|
final NavigableMap<Integer, DistanceLayer> dLayers = new TreeMap<>();
|
||||||
|
|
||||||
for (int d = maxDistance; d > minDistance; d -= SLICE_DISTANCE) {
|
for (int d = maxDistance; d > minDistance; d -= SLICE_DISTANCE) {
|
||||||
final int cDist = d;
|
final int cDist = d;
|
||||||
double[] data = computeAtDistance(tileMap, cDist, dc);
|
double[] data = computeAtDistance(tileMap, cDist, dc);
|
||||||
reversedLayers.add(new DistanceLayer(d - SLICE_DISTANCE, d, data));
|
dLayers.put(d, new DistanceLayer(d - SLICE_DISTANCE, d, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
long layersEnd = System.currentTimeMillis();
|
long layersEnd = System.currentTimeMillis();
|
||||||
LOG.info("Computed {} layers ({} ms)", reversedLayers.size(), layersEnd - layersStart);
|
LOG.info("Computed {} layers ({} ms)", dLayers.size(), layersEnd - layersStart);
|
||||||
|
|
||||||
long horizonStart = System.currentTimeMillis();
|
long horizonStart = System.currentTimeMillis();
|
||||||
// We compute the horizon and delete any layer that is behind the horizon
|
// We compute the horizon and delete any layer that is behind the horizon
|
||||||
// and is not used at all for the landscape.
|
// and is not used at all for the landscape.
|
||||||
// For this, we are using the fact that we computed the layers starting
|
// For this, we are using the fact that we computed the layers starting
|
||||||
// with the farthest ones in the build loop.
|
// with the farthest ones in the build loop.
|
||||||
DistanceLayer horizonLayer = reversedLayers.stream()
|
DistanceLayer horizonLayer = dLayers.values().stream()
|
||||||
.reduce((a, b) -> a.mergeWith(b))
|
.reduce((a, b) -> a.mergeWith(b))
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
reversedLayers = reversedLayers.stream()
|
LOG.info("Horizon is from {} to {}", horizonLayer.nearDistance, horizonLayer.farDistance);
|
||||||
.dropWhile(l -> horizonLayer.isLower(l))
|
|
||||||
.collect(Collectors.toList());
|
Integer fKey = dLayers.firstKey();
|
||||||
|
Integer lKey = dLayers.ceilingKey(horizonLayer.farDistance);
|
||||||
|
|
||||||
|
LOG.info("Split horizon from {} ({}) to {} ({})", fKey, fKey, lKey, horizonLayer.farDistance);
|
||||||
|
final NavigableMap<Integer, DistanceLayer> hLayers = dLayers.subMap(fKey, true, lKey, true);
|
||||||
|
|
||||||
// We recompute the min and max distances after the cleanup of layers that
|
// We recompute the min and max distances after the cleanup of layers that
|
||||||
// are not useful for the display.
|
// are not useful for the display.
|
||||||
maxDistance = reversedLayers.stream()
|
maxDistance = hLayers.values().stream()
|
||||||
.map(l -> l.farDistance)
|
.map(l -> l.farDistance)
|
||||||
.max(Integer::compareTo)
|
.max(Integer::compareTo)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
minDistance = reversedLayers.stream()
|
minDistance = hLayers.values().stream()
|
||||||
.map(l -> l.nearDistance)
|
.map(l -> l.nearDistance)
|
||||||
.min(Integer::compareTo)
|
.min(Integer::compareTo)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
dc.updateMinMaxVertAngle(reversedLayers);
|
dc.updateMinMaxVertAngle(hLayers.values());
|
||||||
|
|
||||||
long horizonEnd = System.currentTimeMillis();
|
long horizonEnd = System.currentTimeMillis();
|
||||||
LOG.info("There are {} layers remaining after horizon cleanup at distances {} to {} ({} ms)",
|
LOG.info("There are {} layers remaining after horizon cleanup at distances {} to {} ({} ms)",
|
||||||
reversedLayers.size(), minDistance, maxDistance, horizonEnd - horizonStart);
|
hLayers.size(), minDistance, maxDistance, horizonEnd - horizonStart);
|
||||||
|
|
||||||
long labelsStart = System.currentTimeMillis();
|
long labelsStart = System.currentTimeMillis();
|
||||||
TreeMap<Integer, DistanceLayer> dLayers = reversedLayers.stream()
|
|
||||||
.collect(Collectors.toMap(l -> l.nearDistance, l -> l, (a, b) -> a.mergeWith(b), TreeMap::new));
|
|
||||||
|
|
||||||
// We start processing the features to avoid overlapping features in the
|
// We start processing the features to avoid overlapping features in the
|
||||||
// labels as well as displaying features that are hidden from the observation
|
// labels as well as displaying features that are hidden from the observation
|
||||||
// point.
|
// point.
|
||||||
|
|
||||||
Predicate<ObservedFeature> isVisibleFilter = (feature) -> {
|
Predicate<ObservedFeature> isVisibleFilter = (feature) -> {
|
||||||
int x = dc.getXAtBearing(feature.bearing);
|
int x = dc.getXAtBearing(feature.bearing);
|
||||||
return DistanceLayer.isVisible(dLayers.values(), feature.distance, x, feature.visibleAngle);
|
return DistanceLayer.isVisible(hLayers.values(), feature.distance, x, feature.visibleAngle);
|
||||||
};
|
};
|
||||||
|
|
||||||
TreeMap<Integer, ObservedFeature> labeledFeatures = allFeatures.stream()
|
TreeMap<Integer, ObservedFeature> labeledFeatures = allFeatures.stream()
|
||||||
|
@ -152,7 +156,7 @@ public class Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ObservedFeature feature : labeledFeatures.values()) {
|
for (ObservedFeature feature : labeledFeatures.values()) {
|
||||||
var featureLayer = dLayers.floorEntry(feature.distance);
|
var featureLayer = hLayers.floorEntry(feature.distance);
|
||||||
if (featureLayer == null) {
|
if (featureLayer == null) {
|
||||||
LOG.info("Unable to find layer for feature {}", feature);
|
LOG.info("Unable to find layer for feature {}", feature);
|
||||||
continue;
|
continue;
|
||||||
|
@ -166,12 +170,12 @@ public class Cli {
|
||||||
|
|
||||||
long mergeStart = System.currentTimeMillis();
|
long mergeStart = System.currentTimeMillis();
|
||||||
List<DistanceLayer> sortedLayers = new ArrayList<>();
|
List<DistanceLayer> sortedLayers = new ArrayList<>();
|
||||||
Integer cDist = dLayers.firstKey();
|
Integer cDist = hLayers.firstKey();
|
||||||
|
|
||||||
DistanceLayer cLayer = dLayers.get(cDist);
|
DistanceLayer cLayer = hLayers.get(cDist);
|
||||||
while (cDist < dLayers.lastKey()) {
|
while (cDist < hLayers.lastKey()) {
|
||||||
Integer nDist = dLayers.higherKey(cDist);
|
Integer nDist = hLayers.higherKey(cDist);
|
||||||
DistanceLayer nLayer = dLayers.get(nDist);
|
DistanceLayer nLayer = hLayers.get(nDist);
|
||||||
|
|
||||||
var bothFeatures = Stream.concat(cLayer.getHorizonFeatures().stream(),nLayer.getHorizonFeatures().stream())
|
var bothFeatures = Stream.concat(cLayer.getHorizonFeatures().stream(),nLayer.getHorizonFeatures().stream())
|
||||||
.filter(isVisibleFilter)
|
.filter(isVisibleFilter)
|
||||||
|
@ -209,7 +213,7 @@ public class Cli {
|
||||||
LOG.info(" Removed {}", missingFeatures);
|
LOG.info(" Removed {}", missingFeatures);
|
||||||
}
|
}
|
||||||
|
|
||||||
continueMerge = mergeScore >= bothScore * 0.1;
|
continueMerge = mergeScore >= bothScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!continueMerge) {
|
if (!continueMerge) {
|
||||||
|
@ -258,7 +262,7 @@ public class Cli {
|
||||||
int x = dc.getXAtBearing(feature.bearing);
|
int x = dc.getXAtBearing(feature.bearing);
|
||||||
int y = dc.getYAtAngle(feature.visibleAngle);
|
int y = dc.getYAtAngle(feature.visibleAngle);
|
||||||
|
|
||||||
boolean isVisible = DistanceLayer.isVisible(dLayers.values(), feature.distance, x, feature.visibleAngle);
|
boolean isVisible = DistanceLayer.isVisible(hLayers.values(), feature.distance, x, feature.visibleAngle);
|
||||||
if (!isVisible) {
|
if (!isVisible) {
|
||||||
LOG.debug("Feature {} is hidden behind terrain", feature.name);
|
LOG.debug("Feature {} is hidden behind terrain", feature.name);
|
||||||
continue;
|
continue;
|
||||||
|
@ -286,7 +290,7 @@ public class Cli {
|
||||||
|
|
||||||
int x = dc.getXAtBearing(feature.bearing);
|
int x = dc.getXAtBearing(feature.bearing);
|
||||||
|
|
||||||
boolean isVisible = DistanceLayer.isVisible(dLayers.values(), feature.distance, x, feature.visibleAngle);
|
boolean isVisible = DistanceLayer.isVisible(hLayers.values(), feature.distance, x, feature.visibleAngle);
|
||||||
if (!isVisible) {
|
if (!isVisible) {
|
||||||
LOG.debug("Feature {} is not visible, hidden behind terrain.", feature.name);
|
LOG.debug("Feature {} is not visible, hidden behind terrain.", feature.name);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -56,21 +56,31 @@ public class DistanceLayer implements Comparable<DistanceLayer>, Cloneable {
|
||||||
|
|
||||||
double[] output = new double[near.data.length];
|
double[] output = new double[near.data.length];
|
||||||
|
|
||||||
boolean anyNear = false;
|
int countNear = 0;
|
||||||
|
int countFar = 0;
|
||||||
|
|
||||||
for (int d = 0; d < near.data.length; d += 1) {
|
for (int d = 0; d < near.data.length; d += 1) {
|
||||||
double yNear = near.data[d];
|
double yNear = near.data[d];
|
||||||
double yFar = far.data[d];
|
double yFar = far.data[d];
|
||||||
|
|
||||||
if (yNear > yFar) {
|
if (yNear >= yFar) {
|
||||||
output[d] = yNear;
|
output[d] = yNear;
|
||||||
anyNear = true;
|
countNear += 1;
|
||||||
} else {
|
} else {
|
||||||
output[d] = yFar;
|
output[d] = yFar;
|
||||||
|
countFar += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tolerance = near.data.length / 100;
|
||||||
|
|
||||||
int outputNearDistance = anyNear ? near.nearDistance : far.nearDistance;
|
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);
|
||||||
|
|
||||||
List<ObservedFeature> outHorizonFeatures = Stream.concat(this.horizonFeatures.parallelStream(), other.horizonFeatures.parallelStream())
|
List<ObservedFeature> outHorizonFeatures = Stream.concat(this.horizonFeatures.parallelStream(), other.horizonFeatures.parallelStream())
|
||||||
.filter(f -> isAtHorizon(f, output))
|
.filter(f -> isAtHorizon(f, output))
|
||||||
|
@ -80,7 +90,7 @@ public class DistanceLayer implements Comparable<DistanceLayer>, Cloneable {
|
||||||
outNonHorizonFeatures.addAll(this.nonHorizonFeatures);
|
outNonHorizonFeatures.addAll(this.nonHorizonFeatures);
|
||||||
outNonHorizonFeatures.addAll(other.nonHorizonFeatures);
|
outNonHorizonFeatures.addAll(other.nonHorizonFeatures);
|
||||||
|
|
||||||
return new DistanceLayer(outputNearDistance, far.farDistance, output, outHorizonFeatures, outNonHorizonFeatures);
|
return new DistanceLayer(outputNearDistance, outputFarDistance, output, outHorizonFeatures, outNonHorizonFeatures);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue