Skip to content

Commit 74fb471

Browse files
committed
Refactor the code and improve javadoc
1 parent 4eb2d23 commit 74fb471

File tree

13 files changed

+805
-289
lines changed

13 files changed

+805
-289
lines changed

baremaps-cli/src/main/java/org/apache/baremaps/cli/raster/HillShade.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ public Integer call() throws Exception {
104104
public static class HillShadeTileStore implements TileStore {
105105

106106
// private String url = "https://s3.amazonaws.com/elevation-tiles-prod/geotiff/{z}/{x}/{y}.tif";
107-
private String url = "https://demotiles.maplibre.org/terrain-tiles/{z}/{x}/{y}.png";
107+
// private String url = "https://demotiles.maplibre.org/terrain-tiles/{z}/{x}/{y}.png";
108+
private String url = "https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png";
108109

109110
private final LoadingCache<TileCoord, BufferedImage> cache = Caffeine.newBuilder()
110111
.maximumSize(1000)

baremaps-raster/pom.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
<dependency>
1212
<groupId>com.twelvemonkeys.imageio</groupId>
1313
<artifactId>imageio-tiff</artifactId>
14-
<version>3.11.0</version>
14+
</dependency>
15+
<dependency>
16+
<groupId>org.locationtech.jts</groupId>
17+
<artifactId>jts-core</artifactId>
1518
</dependency>
1619
</dependencies>
1720
</project>

baremaps-raster/src/main/java/org/apache/baremaps/raster/ImageUtils.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,17 @@
2121

2222
public class ImageUtils {
2323

24-
2524
public static double[] grid(BufferedImage image) {
26-
int gridSize = image.getWidth();
27-
double[] terrain = new double[gridSize * gridSize];
28-
29-
int tileSize = image.getWidth();
30-
31-
// decode terrain values
32-
for (int y = 0; y < tileSize; y++) {
33-
for (int x = 0; x < tileSize; x++) {
25+
double[] grid = new double[image.getWidth() * image.getHeight()];
26+
for (int y = 0; y < image.getHeight(); y++) {
27+
for (int x = 0; x < image.getWidth(); x++) {
3428
int r = (image.getRGB(x, y) >> 16) & 0xFF;
3529
int g = (image.getRGB(x, y) >> 8) & 0xFF;
3630
int b = image.getRGB(x, y) & 0xFF;
37-
terrain[y * gridSize + x] = (r * 256.0 * 256.0 + g * 256.0 + b) / 10.0 - 10000.0;
31+
grid[y * image.getHeight() + x] = (r * 256.0 * 256.0 + g * 256.0 + b) / 10.0 - 10000.0;
3832
}
3933
}
40-
41-
return terrain;
34+
return grid;
4235
}
4336

4437
}

baremaps-raster/src/main/java/org/apache/baremaps/raster/contour/IsoLines.java

Lines changed: 172 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -19,97 +19,193 @@
1919

2020
import java.util.ArrayList;
2121
import java.util.List;
22+
import org.locationtech.jts.geom.Coordinate;
23+
import org.locationtech.jts.geom.GeometryFactory;
24+
import org.locationtech.jts.geom.LineString;
25+
import org.locationtech.jts.operation.linemerge.LineMerger;
2226

27+
/**
28+
* Provides methods for generating isoline contours from digital elevation models (DEMs).
29+
*/
2330
public class IsoLines {
2431

25-
public record Point(double x, double y) {
26-
}
32+
private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
33+
34+
private static final double EPSILON = 1e-10;
2735

28-
public record IsoLine(List<Point> points) {
36+
private IsoLines() {
37+
// Prevent instantiation
2938
}
3039

31-
public static List<IsoLine> isoLines(double[] grid, int gridSize, double level) {
32-
List<IsoLine> isoLines = new ArrayList<>();
33-
for (int y = 0; y < gridSize - 1; y++) {
34-
for (int x = 0; x < gridSize - 1; x++) {
35-
int index = (grid[y * (gridSize + 1) + x] > level ? 1 : 0) |
36-
(grid[y * (gridSize + 1) + (x + 1)] > level ? 2 : 0) |
37-
(grid[(y + 1) * (gridSize + 1) + (x + 1)] > level ? 4 : 0) |
38-
(grid[(y + 1) * (gridSize + 1) + x] > level ? 8 : 0);
39-
List<Point> points = new ArrayList<>();
40-
switch (index) {
41-
case 1:
42-
case 14:
43-
points.add(interpolate(x, y, x, y + 1, grid, gridSize + 1, level));
44-
points.add(interpolate(x, y, x + 1, y, grid, gridSize + 1, level));
45-
break;
46-
case 2:
47-
case 13:
48-
points.add(interpolate(x + 1, y, x, y, grid, gridSize + 1, level));
49-
points.add(interpolate(x + 1, y, x + 1, y + 1, grid, gridSize + 1, level));
50-
break;
51-
case 3:
52-
case 12:
53-
points.add(interpolate(x, y, x, y + 1, grid, gridSize + 1, level));
54-
points.add(interpolate(x + 1, y, x + 1, y + 1, grid, gridSize + 1, level));
55-
break;
56-
case 4:
57-
case 11:
58-
points.add(interpolate(x + 1, y + 1, x + 1, y, grid, gridSize + 1, level));
59-
points.add(interpolate(x + 1, y + 1, x, y + 1, grid, gridSize + 1, level));
60-
break;
61-
case 5:
62-
points.add(interpolate(x, y, x, y + 1, grid, gridSize + 1, level));
63-
points.add(interpolate(x, y + 1, x + 1, y + 1, grid, gridSize + 1, level));
64-
points.add(interpolate(x + 1, y, x + 1, y + 1, grid, gridSize + 1, level));
65-
points.add(interpolate(x + 1, y, x, y, grid, gridSize + 1, level));
66-
break;
67-
case 6:
68-
case 9:
69-
points.add(interpolate(x, y, x + 1, y, grid, gridSize + 1, level));
70-
points.add(interpolate(x, y + 1, x + 1, y + 1, grid, gridSize + 1, level));
71-
break;
72-
case 7:
73-
case 8:
74-
points.add(interpolate(x, y, x, y + 1, grid, gridSize + 1, level));
75-
points.add(interpolate(x, y + 1, x + 1, y + 1, grid, gridSize + 1, level));
76-
break;
77-
case 10:
78-
points.add(interpolate(x, y, x + 1, y, grid, gridSize + 1, level));
79-
points.add(interpolate(x + 1, y, x + 1, y + 1, grid, gridSize + 1, level));
80-
points.add(interpolate(x + 1, y + 1, x, y + 1, grid, gridSize + 1, level));
81-
points.add(interpolate(x, y + 1, x, y, grid, gridSize + 1, level));
82-
break;
83-
}
84-
if (!points.isEmpty()) {
85-
isoLines.add(new IsoLine(points));
86-
}
40+
/**
41+
* Generates isolines for a given grid at a specific level.
42+
*
43+
* @param grid The elevation data
44+
* @param width The width of the grid
45+
* @param height The height of the grid
46+
* @param level The elevation level for which to generate isolines
47+
* @param normalize Whether to normalize the coordinates
48+
* @return A list of LineString objects representing the isolines
49+
*/
50+
public static List<LineString> generateIsoLines(
51+
double[] grid, int width, int height,
52+
double level, boolean normalize) {
53+
validateInput(grid, width, height);
54+
List<LineString> lineStrings = new ArrayList<>();
55+
for (int y = 0; y < height - 1; y++) {
56+
for (int x = 0; x < width - 1; x++) {
57+
processCell(grid, width, height, level, normalize, lineStrings, y, x);
8758
}
8859
}
89-
return isoLines;
60+
return mergeLineStrings(lineStrings);
9061
}
9162

92-
public static List<IsoLine> isoLines(double[] grid, int gridSize, int start, int end,
93-
int interval) {
94-
List<IsoLine> isoLines = new ArrayList<>();
95-
for (int level = start; level < end; level++) {
96-
isoLines.addAll(isoLines(grid, gridSize, level));
63+
/**
64+
* Generates isolines for a given grid at multiple levels within a specified range.
65+
*
66+
* @param grid The elevation data
67+
* @param width The width of the grid
68+
* @param height The height of the grid
69+
* @param start The starting elevation level
70+
* @param end The ending elevation level
71+
* @param interval The interval between elevation levels
72+
* @param normalize Whether to normalize the coordinates
73+
* @return A list of LineString objects representing the isolines
74+
*/
75+
public static List<LineString> generateIsoLines(
76+
double[] grid, int width, int height,
77+
int start, int end, int interval,
78+
boolean normalize) {
79+
validateInput(grid, width, height);
80+
List<LineString> isoLines = new ArrayList<>();
81+
for (int level = start; level < end; level += interval) {
82+
isoLines.addAll(generateIsoLines(grid, width, height, level, normalize));
9783
}
9884
return isoLines;
9985
}
10086

101-
private static Point interpolate(
102-
int x1,
103-
int y1,
104-
int x2,
105-
int y2,
106-
double[] grid,
107-
int width,
108-
double level) {
87+
private static List<LineString> mergeLineStrings(List<LineString> lineStrings) {
88+
LineMerger lineMerger = new LineMerger();
89+
lineMerger.add(lineStrings);
90+
return new ArrayList<>(lineMerger.getMergedLineStrings());
91+
}
92+
93+
private static void validateInput(double[] grid, int width, int height) {
94+
if (grid == null || grid.length == 0) {
95+
throw new IllegalArgumentException("Grid array cannot be null or empty");
96+
}
97+
if (width <= 0 || height <= 0) {
98+
throw new IllegalArgumentException("Width and height must be positive");
99+
}
100+
if (grid.length != width * height) {
101+
throw new IllegalArgumentException("Grid array length does not match width * height");
102+
}
103+
}
104+
105+
private static void processCell(
106+
double[] grid, int width, int height,
107+
double level, boolean normalize, List<LineString> lineStrings,
108+
int y, int x) {
109+
double tl = grid[y * width + x];
110+
double tr = grid[y * width + (x + 1)];
111+
double br = grid[(y + 1) * width + (x + 1)];
112+
double bl = grid[(y + 1) * width + x];
113+
114+
int index =
115+
(tl > level ? 1 : 0) |
116+
(tr > level ? 2 : 0) |
117+
(br > level ? 4 : 0) |
118+
(bl > level ? 8 : 0);
119+
120+
switch (index) {
121+
case 1:
122+
case 14:
123+
createLineString(
124+
grid, width, height, level, normalize, lineStrings,
125+
x, y, x + 1, y,
126+
x, y + 1, x, y);
127+
break;
128+
case 2:
129+
case 13:
130+
createLineString(
131+
grid, width, height, level, normalize, lineStrings,
132+
x + 1, y, x, y,
133+
x + 1, y, x + 1, y + 1);
134+
break;
135+
case 3:
136+
case 12:
137+
createLineString(
138+
grid, width, height, level, normalize, lineStrings,
139+
x, y, x, y + 1,
140+
x + 1, y, x + 1, y + 1);
141+
break;
142+
case 4:
143+
case 11:
144+
createLineString(
145+
grid, width, height, level, normalize, lineStrings,
146+
x + 1, y + 1, x + 1, y,
147+
x + 1, y + 1, x, y + 1);
148+
break;
149+
case 5:
150+
createLineString(
151+
grid, width, height, level, normalize, lineStrings,
152+
x, y, x, y + 1,
153+
x, y + 1, x + 1, y + 1);
154+
createLineString(
155+
grid, width, height, level, normalize, lineStrings,
156+
x + 1, y, x + 1, y + 1,
157+
x + 1, y, x, y);
158+
break;
159+
case 6:
160+
case 9:
161+
createLineString(
162+
grid, width, height, level, normalize, lineStrings,
163+
x, y, x + 1, y,
164+
x, y + 1, x + 1, y + 1);
165+
break;
166+
case 7:
167+
case 8:
168+
createLineString(
169+
grid, width, height, level, normalize, lineStrings,
170+
x, y, x, y + 1,
171+
x, y + 1, x + 1, y + 1);
172+
break;
173+
case 10:
174+
createLineString(
175+
grid, width, height, level, normalize, lineStrings,
176+
x, y, x + 1, y,
177+
x + 1, y, x + 1, y + 1);
178+
createLineString(
179+
grid, width, height, level, normalize, lineStrings,
180+
x + 1, y + 1, x, y + 1,
181+
x, y + 1, x, y);
182+
break;
183+
}
184+
}
185+
186+
private static void createLineString(
187+
double[] grid, int width, int height,
188+
double level, boolean normalize, List<LineString> lineStrings,
189+
int x1, int y1, int x2, int y2,
190+
int x3, int y3, int x4, int y4) {
191+
Coordinate c1 = interpolate(grid, width, height, level, normalize, x1, y1, x2, y2);
192+
Coordinate c2 = interpolate(grid, width, height, level, normalize, x3, y3, x4, y4);
193+
lineStrings.add(GEOMETRY_FACTORY.createLineString(new Coordinate[] {c1, c2}));
194+
}
195+
196+
private static Coordinate interpolate(
197+
double[] grid, int width, int height,
198+
double level, boolean normalize,
199+
int x1, int y1, int x2, int y2) {
109200
double v1 = grid[y1 * width + x1];
110201
double v2 = grid[y2 * width + x2];
111-
double t = (level - v1) / (v2 - v1);
112-
return new Point(x1 + t * (x2 - x1), y1 + t * (y2 - y1));
202+
double t = (Math.abs(v2 - v1) < EPSILON) ? 0.5 : (level - v1) / (v2 - v1);
203+
double x = x1 + t * (x2 - x1);
204+
double y = y1 + t * (y2 - y1);
205+
if (normalize) {
206+
x = x / (width - 1) * width;
207+
y = y / (height - 1) * height;
208+
}
209+
return new Coordinate(x, y);
113210
}
114-
115211
}

0 commit comments

Comments
 (0)