Skip to content

Commit

Permalink
basic forest implementation; works but wip
Browse files Browse the repository at this point in the history
  • Loading branch information
svencc committed Mar 5, 2024
1 parent 9876838 commit e54f48f
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 50 deletions.
6 changes: 4 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
* add DynamicLayerProperties (configuration for RecomMapEntity) (/)

* known issues or optimizations:
* TODO >>> extract to 3d-2d converter (_3dZTo2dY ....)
* /api/v1/configuration/map-tools/resources/forest?mapName= tooks way too long; does it already make use of the resource_name, class_name and prefab_name tables?
* cache forest and/or spatialForestMap
* prebuffering of zoom levels (for faster zooming) or find out what is the bottleneck
* (add pre-click-event (preclick, click, doublecklic) events)
* fix window stage tacview size setting onload -> issue is that window size + engine size is the same at this moment
* eclipse store
* Switch map data to eclipse store!
* switch all data to eclipse store!
* Switch map data to eclipse store!
* switch all data to eclipse store!


# 1.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ public int compose(final int alpha, final int red, final int green, final int bl
((blue & 0xFF << 0));
}

public int modifyBrightness(final int baseColor, double brightness) {
public int modifyBrightness(
final int baseColor,
final double brightness
) {
return compose(
getAlphaComponent(baseColor),
Round.halfUp(getRedComponent(baseColor) * brightness),
Expand All @@ -66,4 +69,16 @@ public int modifyBrightness(final int baseColor, double brightness) {
);
}

public int modifyTransparency(
final int baseColor,
final double alphaFactor
) {
return compose(
Round.halfUp(getAlphaComponent(baseColor) * alphaFactor),
getRedComponent(baseColor),
getGreenComponent(baseColor),
getBlueComponent(baseColor)
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ private int calculateContour(
@NonNull final MapDesignScheme mapScheme
) {
final float[][] dem = demDescriptor.getDem();
final int demWidth = demDescriptor.getDemWidth();
final int demHeigth = demDescriptor.getDemHeight();
final List<ContourLineLayer> contourLayers = generateContourLineLayers(demDescriptor, mapScheme);

for (final ContourLineLayer layer : contourLayers) {
Expand All @@ -50,8 +52,8 @@ private int calculateContour(
final int adjacentOppositeNeighborY = y + D8AspectMatrix.directionYComponentMatrix[direction + 3]; // Calculate the Y-coordinate of the adjacent opposite neighbor.

// Check if the new point is within the bounds
if (adjacentNeighborX >= 0 && adjacentNeighborY >= 0 && adjacentNeighborX < dem.length && adjacentNeighborY < dem[0].length
&& adjacentOppositeNeighborX >= 0 && adjacentOppositeNeighborY >= 0 && adjacentOppositeNeighborX < dem.length && adjacentOppositeNeighborY < dem[0].length) {
if (adjacentNeighborX >= 0 && adjacentNeighborY >= 0 && adjacentNeighborX < demWidth && adjacentNeighborY < demHeigth
&& adjacentOppositeNeighborX >= 0 && adjacentOppositeNeighborY >= 0 && adjacentOppositeNeighborX < demWidth && adjacentOppositeNeighborY < demHeigth) {
if (dem[adjacentNeighborX][adjacentNeighborY] > layer.getHeight()
&& dem[adjacentOppositeNeighborX][adjacentOppositeNeighborY] < layer.getHeight()
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.recom.commons.calculator.d8algorithm;


import com.recom.commons.calculator.ARGBCalculator;
import com.recom.commons.map.rasterizer.mapdesignscheme.MapDesignScheme;
import com.recom.commons.model.DEMDescriptor;
import com.recom.commons.model.maprendererpipeline.dataprovider.forest.ForestItem;
import com.recom.commons.model.maprendererpipeline.dataprovider.forest.SpacialIndex;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import java.util.List;

@RequiredArgsConstructor
public class D8AlgorithmForForestMap {

@NonNull
private final ARGBCalculator colorCalculator = new ARGBCalculator();
private final double cellSize;


@NonNull
public int[][] generateForestMap(
@NonNull final DEMDescriptor demDescriptor,
@NonNull final SpacialIndex<ForestItem> spacialIndex,
@NonNull final MapDesignScheme mapScheme
) {
final int demWidth = demDescriptor.getDemWidth();
final int demHeight = demDescriptor.getDemHeight();

final int[][] forestMap = new int[demWidth][demHeight];

for (int x = 0; x < demWidth; x++) {
for (int y = 0; y < demHeight; y++) {
forestMap[x][y] = calculateForestFragment(demDescriptor, spacialIndex, mapScheme, x, y);
}
}

return forestMap;
}

@NonNull
private int calculateForestFragment(
@NonNull final DEMDescriptor demDescriptor,
@NonNull final SpacialIndex<ForestItem> spacialIndex,
@NonNull final MapDesignScheme mapScheme,
final int x,
final int y
) {
final List<ForestItem> forestItemsInSpace = spacialIndex.get(x, y);
final double treeDensity = forestItemsInSpace.size() / cellSize * cellSize;
int surroundingForestNeighbourSpaces = 0;

// 0.15 Trees per 25m²
float forestDensityThreshold = 0.15f; // @TODO extract to conf

final int demWidth = demDescriptor.getDemWidth();
final int demHeight = demDescriptor.getDemHeight();

for (int direction = 0; direction < 8; direction++) {
final int adjacentNeighborX = x + D8AspectMatrix.directionXComponentMatrix[direction]; // Calculate the X-coordinate of the adjacent neighbor.
final int adjacentNeighborY = y + D8AspectMatrix.directionYComponentMatrix[direction]; // Calculate the Y-coordinate of the adjacent neighbor.

if (adjacentNeighborX >= 0 && adjacentNeighborY >= 0 && adjacentNeighborX < demWidth && adjacentNeighborY < demHeight) {
final List<ForestItem> forestItemsInNeighborSpace = spacialIndex.get(adjacentNeighborX, adjacentNeighborY);
final double neighbourForestDensity = forestItemsInNeighborSpace.size() / cellSize * cellSize;
if (neighbourForestDensity > forestDensityThreshold) {
surroundingForestNeighbourSpaces++;
}
}

}

int baseColorForest = mapScheme.getBaseColorForest();

if (treeDensity < forestDensityThreshold) {
return mapScheme.getBaseColorContourBackground(); // @TODO extract to new variable baseColorOfForestBackground!!!
} else if (treeDensity > forestDensityThreshold && surroundingForestNeighbourSpaces >= 5) {
return baseColorForest;
} else {
return colorCalculator.modifyTransparency(baseColorForest, 0.5);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

import com.recom.commons.calculator.ARGBCalculator;
import com.recom.commons.calculator.VectorCalculator;
import com.recom.commons.map.rasterizer.mapdesignscheme.MapDesignScheme;
import com.recom.commons.model.SlopeAndAspect;
import com.recom.commons.model.Vector3D;
import com.recom.commons.map.rasterizer.mapdesignscheme.MapDesignScheme;
import lombok.NoArgsConstructor;
import lombok.NonNull;

Expand All @@ -21,10 +21,12 @@ public int[][] generateShadedMap(
@NonNull final SlopeAndAspect[][] slopeAndAspectMap,
@NonNull final MapDesignScheme shadowingScheme
) {
final int[][] shadingMap = new int[slopeAndAspectMap.length][slopeAndAspectMap[0].length];
final int mapWidth = slopeAndAspectMap.length;
final int mapHeight = slopeAndAspectMap[0].length;
final int[][] shadingMap = new int[mapWidth][mapHeight];

for (int x = 0; x < slopeAndAspectMap.length; x++) {
for (int y = 0; y < slopeAndAspectMap[0].length; y++) {
for (int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
shadingMap[x][y] = calculateShading(slopeAndAspectMap[x][y], shadowingScheme);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ public class D8AlgorithmForSlopeAndAspectMap {
*/
@NonNull
public SlopeAndAspect[][] generateSlopeAndAspectMap(@NonNull final float[][] dem) {
final SlopeAndAspect[][] slopeAndAspects = new SlopeAndAspect[dem.length][dem[0].length];
final int demWidth = dem.length;
final int demHeight = dem[0].length;
final SlopeAndAspect[][] slopeAndAspects = new SlopeAndAspect[demWidth][demHeight];

// Iterate through each cell in the DEM to calculate its slope and aspect.
for (int x = 0; x < dem.length; x++) {
for (int y = 0; y < dem[0].length; y++) {
for (int x = 0; x < demWidth; x++) {
for (int y = 0; y < demHeight; y++) {
slopeAndAspects[x][y] = calculateSlopeAndAspect(dem, x, y);
}
}
Expand All @@ -49,6 +51,8 @@ private SlopeAndAspect calculateSlopeAndAspect(
final int x,
final int y
) {
final int demWidth = dem.length;
final int demHeight = dem[0].length;
Aspect aspect = Aspect.NULL_ASPECT; // Initialize the aspect to null.
double maxSlope = 0; // Initialize the maximum slope to 0.
final double diagonalCellSize = Math.sqrt(2) * cellSize;
Expand All @@ -59,23 +63,25 @@ private SlopeAndAspect calculateSlopeAndAspect(
final int adjacentNeighborY = y + D8AspectMatrix.directionYComponentMatrix[direction]; // Calculate the Y-coordinate of the adjacent neighbor.

// Prüfen, ob der neue Punkt innerhalb der Grenzen liegt
if (adjacentNeighborX >= 0 && adjacentNeighborY >= 0 && adjacentNeighborX < dem.length && adjacentNeighborY < dem[0].length) {
final double relativeDifference = dem[x][y] - dem[adjacentNeighborX][adjacentNeighborY];
final int differenceSign = Sign.of(relativeDifference);
final double absoluteDifference = Math.abs(relativeDifference);
// Elevation difference to the neighbor.
final double distance = (currentAspect.isCardinal()) ? cellSize : diagonalCellSize; // Choose the correct distance based on direction.
final double slope = absoluteDifference / distance; // Calculate the slope as elevation difference divided by distance.
if (adjacentNeighborX >= 0 && adjacentNeighborY >= 0 && adjacentNeighborX < demWidth) {
if (adjacentNeighborY < demHeight) {
final double relativeDifference = dem[x][y] - dem[adjacentNeighborX][adjacentNeighborY];
final int differenceSign = Sign.of(relativeDifference);
final double absoluteDifference = Math.abs(relativeDifference);
// Elevation difference to the neighbor.
final double distance = (currentAspect.isCardinal()) ? cellSize : diagonalCellSize; // Choose the correct distance based on direction.
final double slope = absoluteDifference / distance; // Calculate the slope as elevation difference divided by distance.

// Update the maximum slope if this slope is steeper.
// so, if more than one slope is the same, the first one will stay the aspect
if (slope > maxSlope) {
aspect = currentAspect;
// If difference is negative, the aspect is the opposite of the current aspect, because the slope is in the descending direction.
if (differenceSign == -1) {
aspect = currentAspect.getOpposite();
// Update the maximum slope if this slope is steeper.
// so, if more than one slope is the same, the first one will stay the aspect
if (slope > maxSlope) {
aspect = currentAspect;
// If difference is negative, the aspect is the opposite of the current aspect, because the slope is in the descending direction.
if (differenceSign == -1) {
aspect = currentAspect.getOpposite();
}
maxSlope = slope;
}
maxSlope = slope;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ public int[][] generateSlopeMap(
@NonNull final SlopeAndAspect[][] slopeAndAspects,
@NonNull final MapDesignScheme mapScheme
) {
final int[][] slopeMap = new int[slopeAndAspects.length][slopeAndAspects[0].length];
final int mapWidth = slopeAndAspects.length;
final int mapHeight = slopeAndAspects[0].length;
final int[][] slopeMap = new int[mapWidth][mapHeight];

for (int x = 0; x < slopeAndAspects.length; x++) {
for (int y = 0; y < slopeAndAspects[0].length; y++) {
for (int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
slopeMap[x][y] = calculateSlopeColor(slopeAndAspects[x][y], mapScheme);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.recom.commons.map.rasterizer;

import com.recom.commons.calculator.d8algorithm.D8AlgorithmForForestMap;
import com.recom.commons.map.MapComposer;
import com.recom.commons.map.rasterizer.configuration.LayerOrder;
import com.recom.commons.map.rasterizer.configuration.MapLayerRasterizer;
import com.recom.commons.map.rasterizer.mapdesignscheme.MapDesignScheme;
import com.recom.commons.math.Round;
import com.recom.commons.model.DEMDescriptor;
import com.recom.commons.model.maprendererpipeline.MapComposerWorkPackage;
import com.recom.commons.model.maprendererpipeline.MapLayerRasterizerConfiguration;
import com.recom.commons.model.maprendererpipeline.dataprovider.forest.ForestItem;
Expand All @@ -15,7 +17,6 @@
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -45,26 +46,22 @@ public class ForestMapRasterizer implements MapLayerRasterizer {

@NonNull
public int[] rasterizeForestMap(
final SpacialIndex<ForestItem> spacialIndex,
@NonNull final DEMDescriptor demDescriptor,
@NonNull final SpacialIndex<ForestItem> spacialIndex,
@NonNull final MapDesignScheme mapScheme
) {
// final D8AlgorithmForSlopeAndAspectMap algorithmForSlopeAndAspect = new D8AlgorithmForSlopeAndAspectMap(5.0);
// final D8AlgorithmForSlopeMap d8AlgorithmForSlopeMap = new D8AlgorithmForSlopeMap();
//
// final SlopeAndAspect[][] slopeAndAspects = algorithmForSlopeAndAspect.generateSlopeAndAspectMap(DEMDescriptor.getDem());
// final int[][] contourMap = d8AlgorithmForSlopeMap.generateSlopeMap(slopeAndAspects, mapScheme);
//
// @TODO <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
final D8AlgorithmForForestMap d8AlgorithmForForestMap = new D8AlgorithmForForestMap(5.0);

final int[][] forestMap = d8AlgorithmForForestMap.generateForestMap(demDescriptor, spacialIndex, mapScheme);

final int width = spacialIndex.getWidth();
final int height = spacialIndex.getHeight();

final int[] pixelBuffer = new int[width * height];
// for (int x = 0; x < width; x++) {
// for (int z = 0; z < height; z++) {
// pixelBuffer[x + z * width] = contourMap[x][z];
// }
// }
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
pixelBuffer[x + y * width] = forestMap[x][y];
}
}

return pixelBuffer;
}
Expand Down Expand Up @@ -111,20 +108,34 @@ public void render(@NonNull MapComposerWorkPackage workPackage) {
final SpacialIndex<ForestItem> spatialIndex = new SpacialIndex<>(demWidth, demHeight);
forestEntities.forEach(forestItem -> {
final int x = Round.halfUp(forestItem.getCoordinateX().doubleValue() / stepSize);
final int y = Round.halfUp(forestItem.getCoordinateY().doubleValue() / stepSize);

final double normalizedYCoordinate = _3dZTo2dY(forestItem.getCoordinateY().doubleValue(), demHeight, stepSize);
final int y = Round.halfUp(normalizedYCoordinate / stepSize);

if (x >= 0 && x < demWidth && y >= 0 && y < demHeight) {
spatialIndex.put(x, y, forestItem);
}
});

final int[] rawForestMap = rasterizeForestMap(spatialIndex, workPackage.getMapComposerConfiguration().getMapDesignScheme());

final int[] rawForestMap = rasterizeForestMap(workPackage.getMapComposerConfiguration().getDemDescriptor(), spatialIndex, workPackage.getMapComposerConfiguration().getMapDesignScheme());
workPackage.getPipelineArtifacts().addArtifact(this, rawForestMap);

} else {
log.error("ForestMapRasterizer is not prepared, and prepareAsync was not called in advance!");
return;
}
}





// TODO >>> extract to 3d-2d converter
public double _3dXTo2dX(final double x,final int demWidth, final double stepSize) {
return demWidth * stepSize - x;
}

public double _3dZTo2dY(final double y, final int demHeight, final double stepSize) {
return demHeight * stepSize - y;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public int[] rasterizeSlopeMap(
@NonNull final DEMDescriptor DEMDescriptor,
@NonNull final MapDesignScheme mapScheme
) {
final D8AlgorithmForSlopeAndAspectMap algorithmForSlopeAndAspect = new D8AlgorithmForSlopeAndAspectMap(5.0);
final D8AlgorithmForSlopeAndAspectMap algorithmForSlopeAndAspect = new D8AlgorithmForSlopeAndAspectMap(DEMDescriptor.getStepSize());
final D8AlgorithmForSlopeMap d8AlgorithmForSlopeMap = new D8AlgorithmForSlopeMap();

final SlopeAndAspect[][] slopeAndAspects = algorithmForSlopeAndAspect.generateSlopeAndAspectMap(DEMDescriptor.getDem());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,10 @@ private DEMDescriptor invertDEM(@NonNull final TopographyData topograpyModel) {
.build();
}

// warum muss ich das machen? die daten kommen aus einer 3d engine; die koordinaten muss hich hier quasi invertieren
// in der 3d engine ist unten links 0,0, ich brauche aber ein 0,0 oben links (image)
// ich muss also die daten um 180° drehen; wie mache ich das?
// ich iteriere über die daten und schreibe sie in ein neues array; das neue array ist dann um 180° gedreht
// gibts da einen algorithmus?

}

0 comments on commit e54f48f

Please sign in to comment.