Skip to content

Commit d899027

Browse files
committed
Add support for Digital Elevation Models
- Enabled reading of Digital Elevation Models (DEMs) encoded as Cloud Optimized GeoTIFF (COG TIFF) using Apache SIS. - Implemented an algorithm to compute hillshades on raster data, enhancing visualization of terrain features. - Added an algorithm to compute vector contours from raster data, enabling elevation representation as contour lines.
1 parent 091a0e9 commit d899027

File tree

81 files changed

+5761
-261
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+5761
-261
lines changed

.run/basemap-dem.run.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<component name="ProjectRunConfigurationManager">
2+
<configuration default="false" name="basemap-dem" type="Application" factoryName="Application">
3+
<option name="MAIN_CLASS_NAME" value="org.apache.baremaps.cli.Baremaps" />
4+
<module name="baremaps-cli" />
5+
<option name="PROGRAM_PARAMETERS" value="dem serve --path /data/gebco_2024.tif" />
6+
<target name="gdal" />
7+
<extension name="software.aws.toolkits.jetbrains.core.execution.JavaAwsConnectionExtension">
8+
<option name="credential" />
9+
<option name="region" />
10+
<option name="useCurrentConnection" value="false" />
11+
</extension>
12+
<method v="2">
13+
<option name="Make" enabled="true" />
14+
</method>
15+
</configuration>
16+
</component>

baremaps-cli/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ limitations under the License.
4343
<groupId>org.apache.baremaps</groupId>
4444
<artifactId>baremaps-core</artifactId>
4545
</dependency>
46+
<dependency>
47+
<groupId>org.apache.baremaps</groupId>
48+
<artifactId>baremaps-dem</artifactId>
49+
</dependency>
4650
<dependency>
4751
<groupId>org.apache.baremaps</groupId>
4852
<artifactId>baremaps-server</artifactId>

baremaps-cli/src/main/java/org/apache/baremaps/cli/Baremaps.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.concurrent.Callable;
2626
import org.apache.baremaps.cli.Baremaps.VersionProvider;
2727
import org.apache.baremaps.cli.database.Database;
28+
import org.apache.baremaps.cli.dem.DEM;
2829
import org.apache.baremaps.cli.geocoder.Geocoder;
2930
import org.apache.baremaps.cli.iploc.IpLoc;
3031
import org.apache.baremaps.cli.map.Map;
@@ -42,7 +43,14 @@
4243
name = "baremaps",
4344
description = "A toolkit for producing vector tiles.",
4445
versionProvider = VersionProvider.class,
45-
subcommands = {Workflow.class, Database.class, Map.class, Geocoder.class, IpLoc.class},
46+
subcommands = {
47+
Workflow.class,
48+
Database.class,
49+
Map.class,
50+
Geocoder.class,
51+
IpLoc.class,
52+
DEM.class
53+
},
4654
sortOptions = false)
4755
@SuppressWarnings("squid:S106")
4856
public class Baremaps implements Callable<Integer> {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.baremaps.cli.dem;
19+
20+
21+
22+
import picocli.CommandLine;
23+
import picocli.CommandLine.Command;
24+
25+
@Command(name = "dem", description = "DEM processing commands.",
26+
subcommands = {
27+
Serve.class,
28+
VectorTileContours.class
29+
},
30+
sortOptions = false)
31+
@SuppressWarnings("squid:S106")
32+
public class DEM implements Runnable {
33+
34+
@Override
35+
public void run() {
36+
CommandLine.usage(this, System.out);
37+
}
38+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.baremaps.cli.dem;
19+
20+
import static org.apache.baremaps.utils.ObjectMapperUtils.objectMapper;
21+
22+
import com.linecorp.armeria.common.*;
23+
import com.linecorp.armeria.server.Server;
24+
import com.linecorp.armeria.server.annotation.JacksonResponseConverterFunction;
25+
import com.linecorp.armeria.server.cors.CorsService;
26+
import com.linecorp.armeria.server.docs.DocService;
27+
import com.linecorp.armeria.server.file.HttpFile;
28+
import java.nio.file.Path;
29+
import java.util.concurrent.Callable;
30+
import org.apache.baremaps.dem.ElevationUtils;
31+
import org.apache.baremaps.server.BufferedImageResource;
32+
import org.apache.baremaps.server.VectorTileResource;
33+
import org.apache.baremaps.tilestore.raster.*;
34+
import picocli.CommandLine.Command;
35+
import picocli.CommandLine.Option;
36+
37+
@Command(name = "serve", description = "Start a tile server that serves elevation data.")
38+
public class Serve implements Callable<Integer> {
39+
40+
@Option(names = {"--host"}, paramLabel = "HOST", description = "The host of the server.")
41+
private String host = "localhost";
42+
43+
@Option(names = {"--port"}, paramLabel = "PORT", description = "The port of the server.")
44+
private int port = 9000;
45+
46+
@Option(names = {"--path"}, paramLabel = "PATH", description = "The path of a geoTIFF file.")
47+
private Path path;
48+
49+
@Override
50+
public Integer call() throws Exception {
51+
// Initialize the tile stores
52+
var geoTiffReader = new GeoTiffReader(path);
53+
var rasterElevationTileStore = new TerrariumTileStore(geoTiffReader);
54+
var rasterHillshadeTileStore =
55+
new RasterHillshadeTileStore(
56+
rasterElevationTileStore,
57+
ElevationUtils::terrariumToElevation);
58+
var vectorHillshadeTileStore =
59+
new VectorHillshadeTileStore(
60+
geoTiffReader,
61+
ElevationUtils::terrariumToElevation);
62+
var vectorContourTileStore =
63+
new VectorContourTileStore(geoTiffReader);
64+
65+
// Initialize the server
66+
var objectMapper = objectMapper();
67+
var jsonResponseConverter = new JacksonResponseConverterFunction(objectMapper);
68+
var serverBuilder = Server.builder();
69+
serverBuilder.http(port);
70+
71+
// Register the services
72+
serverBuilder.annotatedService(
73+
"/raster/elevation",
74+
new BufferedImageResource(() -> rasterElevationTileStore),
75+
jsonResponseConverter);
76+
serverBuilder.annotatedService(
77+
"/raster/hillshade",
78+
new BufferedImageResource(() -> rasterHillshadeTileStore),
79+
jsonResponseConverter);
80+
serverBuilder.annotatedService(
81+
"/vector/contour",
82+
new VectorTileResource(() -> vectorContourTileStore),
83+
jsonResponseConverter);
84+
serverBuilder.annotatedService(
85+
"/vector/hillshade",
86+
new VectorTileResource(() -> vectorHillshadeTileStore),
87+
jsonResponseConverter);
88+
89+
var index = HttpFile.of(ClassLoader.getSystemClassLoader(), "/dem/index.html");
90+
serverBuilder.service("/", index.asService());
91+
92+
serverBuilder.decorator(CorsService.builderForAnyOrigin()
93+
.allowAllRequestHeaders(true)
94+
.allowRequestMethods(
95+
HttpMethod.GET,
96+
HttpMethod.POST,
97+
HttpMethod.PUT,
98+
HttpMethod.DELETE,
99+
HttpMethod.OPTIONS,
100+
HttpMethod.HEAD)
101+
.allowCredentials()
102+
.exposeHeaders(HttpHeaderNames.LOCATION)
103+
.newDecorator());
104+
105+
serverBuilder.serviceUnder("/docs", new DocService());
106+
107+
serverBuilder.disableServerHeader();
108+
serverBuilder.disableDateHeader();
109+
var server = serverBuilder.build();
110+
111+
var startFuture = server.start();
112+
startFuture.join();
113+
114+
var shutdownFuture = server.closeOnJvmShutdown();
115+
shutdownFuture.join();
116+
117+
return 0;
118+
}
119+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.baremaps.cli.dem;
19+
20+
21+
22+
import java.nio.file.Path;
23+
import java.util.List;
24+
import java.util.concurrent.Callable;
25+
import org.apache.baremaps.maplibre.tileset.Tileset;
26+
import org.apache.baremaps.maplibre.tileset.TilesetLayer;
27+
import org.apache.baremaps.openstreetmap.stream.ProgressLogger;
28+
import org.apache.baremaps.openstreetmap.stream.StreamUtils;
29+
import org.apache.baremaps.tilestore.TileCoord;
30+
import org.apache.baremaps.tilestore.TileEntry;
31+
import org.apache.baremaps.tilestore.TileStoreException;
32+
import org.apache.baremaps.tilestore.pmtiles.PMTilesStore;
33+
import org.apache.baremaps.tilestore.raster.*;
34+
import org.apache.baremaps.workflow.WorkflowException;
35+
import org.apache.baremaps.workflow.tasks.ExportVectorTiles;
36+
import org.locationtech.jts.geom.Envelope;
37+
import picocli.CommandLine.Command;
38+
import picocli.CommandLine.Option;
39+
40+
@Command(name = "vector-contours", description = "Generate vector contours from a DEM.")
41+
@SuppressWarnings("squid:S106")
42+
public class VectorTileContours implements Callable<Integer> {
43+
44+
@Option(names = {"--path"}, paramLabel = "PATH", description = "The path of a geoTIFF file.")
45+
private Path path;
46+
47+
@Option(names = {"--repository"}, paramLabel = "REPOSITORY", description = "The tile repository.",
48+
required = true)
49+
private Path repository;
50+
51+
@Option(names = {"--format"}, paramLabel = "FORMAT",
52+
description = "The format of the repository.")
53+
private ExportVectorTiles.Format format = ExportVectorTiles.Format.FILE;
54+
55+
56+
@Override
57+
public Integer call() throws Exception {
58+
var contourLayer = new TilesetLayer();
59+
contourLayer.setId("contours");
60+
61+
var tileset = new Tileset();
62+
tileset.setName("contours");
63+
tileset.setMinzoom(2);
64+
tileset.setMaxzoom(10);
65+
tileset.setCenter(List.of(0d, 0d, 1d));
66+
tileset.setBounds(List.of(-180d, -85.0511d, 180d, 85.0511d));
67+
tileset.setVectorLayers(List.of(contourLayer));
68+
69+
// Initialize the tile stores
70+
try (var geoTiffReader = new GeoTiffReader(path);
71+
var sourceTileStore = new VectorContourTileStore(geoTiffReader);
72+
var targetTileStore = new PMTilesStore(repository, tileset);) {
73+
74+
var envelope = new Envelope(-180, 180, -85.0511, 85.0511);
75+
var count = TileCoord.count(envelope, 0, 10);
76+
77+
var tileCoordIterator =
78+
TileCoord.iterator(envelope, 2, 10);
79+
var tileCoordStream =
80+
StreamUtils.stream(tileCoordIterator).peek(new ProgressLogger<>(count, 1000));
81+
82+
83+
var bufferedTileEntryStream = StreamUtils.bufferInCompletionOrder(tileCoordStream, tile -> {
84+
85+
try {
86+
return new TileEntry<>(tile, sourceTileStore.read(tile));
87+
} catch (TileStoreException e) {
88+
throw new WorkflowException(e);
89+
} finally {
90+
System.out.println("Processing tile " + tile);
91+
}
92+
}, 8);
93+
94+
var partitionedTileEntryStream = StreamUtils.partition(bufferedTileEntryStream, 8);
95+
partitionedTileEntryStream.forEach(batch -> {
96+
try {
97+
targetTileStore.write(batch);
98+
} catch (TileStoreException e) {
99+
throw new WorkflowException(e);
100+
}
101+
});
102+
return 0;
103+
}
104+
}
105+
}

baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Dev.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.linecorp.armeria.server.file.FileService;
2828
import com.linecorp.armeria.server.file.HttpFile;
2929
import java.io.IOException;
30+
import java.nio.ByteBuffer;
3031
import java.nio.file.Path;
3132
import java.util.concurrent.Callable;
3233
import java.util.function.Supplier;
@@ -37,8 +38,8 @@
3738
import org.apache.baremaps.maplibre.tileset.Tileset;
3839
import org.apache.baremaps.server.ChangeResource;
3940
import org.apache.baremaps.server.StyleResource;
40-
import org.apache.baremaps.server.TileResource;
4141
import org.apache.baremaps.server.TilesetResource;
42+
import org.apache.baremaps.server.VectorTileResource;
4243
import org.apache.baremaps.tilestore.TileStore;
4344
import org.apache.baremaps.tilestore.postgres.PostgresTileStore;
4445
import org.apache.baremaps.utils.PostgresUtils;
@@ -96,7 +97,7 @@ public Integer call() throws Exception {
9697
}
9798
};
9899

99-
var tileStoreSupplier = (Supplier<TileStore>) () -> {
100+
var tileStoreSupplier = (Supplier<TileStore<ByteBuffer>>) () -> {
100101
var tileJSON = tilesetSupplier.get();
101102
return new PostgresTileStore(datasource, tileJSON);
102103
};
@@ -116,7 +117,8 @@ public Integer call() throws Exception {
116117
var jsonResponseConverter = new JacksonResponseConverterFunction(objectMapper);
117118
serverBuilder.annotatedService(new ChangeResource(tilesetPath, stylePath),
118119
jsonResponseConverter);
119-
serverBuilder.annotatedService(new TileResource(tileStoreSupplier), jsonResponseConverter);
120+
serverBuilder.annotatedService("/tiles", new VectorTileResource(tileStoreSupplier),
121+
jsonResponseConverter);
120122
serverBuilder.annotatedService(new StyleResource(styleSupplier), jsonResponseConverter);
121123
serverBuilder.annotatedService(new TilesetResource(tilesetSupplier), jsonResponseConverter);
122124

baremaps-cli/src/main/java/org/apache/baremaps/cli/map/MBTiles.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.linecorp.armeria.server.docs.DocService;
2929
import com.linecorp.armeria.server.file.FileService;
3030
import com.linecorp.armeria.server.file.HttpFile;
31+
import java.nio.ByteBuffer;
3132
import java.nio.file.Path;
3233
import java.util.concurrent.Callable;
3334
import java.util.function.Supplier;
@@ -36,9 +37,9 @@
3637
import org.apache.baremaps.maplibre.style.Style;
3738
import org.apache.baremaps.maplibre.tilejson.TileJSON;
3839
import org.apache.baremaps.server.*;
39-
import org.apache.baremaps.tilestore.TileCache;
4040
import org.apache.baremaps.tilestore.TileStore;
4141
import org.apache.baremaps.tilestore.mbtiles.MBTilesStore;
42+
import org.apache.baremaps.tilestore.vector.VectorTileCache;
4243
import org.apache.baremaps.utils.SqliteUtils;
4344
import org.slf4j.Logger;
4445
import org.slf4j.LoggerFactory;
@@ -83,8 +84,8 @@ public Integer call() throws Exception {
8384

8485
var datasource = SqliteUtils.createDataSource(mbtilesPath, true);
8586
try (var tileStore = new MBTilesStore(datasource);
86-
var tileCache = new TileCache(tileStore, caffeineSpec)) {
87-
var tileStoreSupplier = (Supplier<TileStore>) () -> tileCache;
87+
var tileCache = new VectorTileCache(tileStore, caffeineSpec)) {
88+
var tileStoreSupplier = (Supplier<TileStore<ByteBuffer>>) () -> tileCache;
8889

8990
var style = objectMapper.readValue(configReader.read(stylePath), Style.class);
9091
var styleSupplier = (Supplier<Style>) () -> style;
@@ -96,7 +97,8 @@ public Integer call() throws Exception {
9697
serverBuilder.http(port);
9798

9899
var jsonResponseConverter = new JacksonResponseConverterFunction(objectMapper);
99-
serverBuilder.annotatedService(new TileResource(tileStoreSupplier), jsonResponseConverter);
100+
serverBuilder.annotatedService("/tiles", new VectorTileResource(tileStoreSupplier),
101+
jsonResponseConverter);
100102
serverBuilder.annotatedService(new StyleResource(styleSupplier), jsonResponseConverter);
101103
serverBuilder.annotatedService(new TileJSONResource(tileJSONSupplier), jsonResponseConverter);
102104

0 commit comments

Comments
 (0)