This is an automated email from the ASF dual-hosted git repository. bchapuis pushed a commit to branch gdal in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git
commit 374c6858599a56dd946eba28cca059e0a777f434 Author: Bertil Chapuis <[email protected]> AuthorDate: Sun Apr 23 12:44:02 2023 +0200 Serve contour tiles --- .../java/org/apache/baremaps/cli/map/Contour.java | 82 +++++++++++++++ .../main/java/org/apache/baremaps/cli/map/Map.java | 2 +- baremaps-core/pom.xml | 8 +- .../apache/baremaps/raster/ContourTileStore.java | 73 +++++++------ .../main/java/org/apache/baremaps/raster/Main.java | 26 ++--- .../org/apache/baremaps/vectortile/Feature.java | 8 +- .../baremaps/vectortile/VectorTileEncoder.java | 5 +- .../baremaps/vectortile/VectorTileFunctions.java | 34 ++++-- .../vectortile/VectorTileFunctionsTest.java | 2 +- .../apache/baremaps/vectortile/VectorTileTest.java | 4 +- .../baremaps/vectortile/VectorTileViewer.java | 16 +-- .../apache/baremaps/server/ServerResources.java | 116 +++++++++++++++++++++ examples/contour/style.json | 4 +- examples/contour/tileset.json | 11 +- 14 files changed, 304 insertions(+), 87 deletions(-) diff --git a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Contour.java b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Contour.java new file mode 100644 index 00000000..055456d5 --- /dev/null +++ b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Contour.java @@ -0,0 +1,82 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.baremaps.cli.map; + +import static io.servicetalk.data.jackson.jersey.ServiceTalkJacksonSerializerFeature.contextResolverFor; +import static org.apache.baremaps.server.DefaultObjectMapper.defaultObjectMapper; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.servicetalk.http.netty.HttpServers; +import io.servicetalk.http.router.jersey.HttpJerseyRouterBuilder; +import java.nio.file.Path; +import java.util.concurrent.Callable; +import org.apache.baremaps.database.tile.TileStore; +import org.apache.baremaps.raster.ContourTileStore; +import org.apache.baremaps.server.CorsFilter; +import org.apache.baremaps.server.ServerResources; +import org.glassfish.hk2.utilities.binding.AbstractBinder; +import org.glassfish.jersey.server.ResourceConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +@Command(name = "contour", description = "Start a tile server with caching capabilities.") +public class Contour implements Callable<Integer> { + + private static final Logger logger = LoggerFactory.getLogger(Contour.class); + + @Option(names = {"--cache"}, paramLabel = "CACHE", description = "The caffeine cache directive.") + private String cache = ""; + + @Option(names = {"--tileset"}, paramLabel = "TILESET", description = "The tileset file.", + required = true) + private Path tileset; + + @Option(names = {"--style"}, paramLabel = "STYLE", description = "The style file.", + required = true) + private Path style; + + @Option(names = {"--host"}, paramLabel = "HOST", description = "The host of the server.") + private String host = "localhost"; + + @Option(names = {"--port"}, paramLabel = "PORT", description = "The port of the server.") + private int port = 9000; + + @Override + public Integer call() throws Exception { + var objectMapper = defaultObjectMapper(); + var tileStore = new ContourTileStore(); + + // Configure the application + var application = + new ResourceConfig().register(CorsFilter.class).register(ServerResources.class) + .register(contextResolverFor(objectMapper)).register(new AbstractBinder() { + @Override + protected void configure() { + bind(Contour.this.tileset).to(Path.class).named("tileset"); + bind(style).to(Path.class).named("style"); + bind(objectMapper).to(ObjectMapper.class); + bind(tileStore).to(TileStore.class); + } + }); + + var httpService = new HttpJerseyRouterBuilder().buildBlockingStreaming(application); + var serverContext = HttpServers.forPort(port).listenBlockingStreamingAndAwait(httpService); + + logger.info("Listening on {}", serverContext.listenAddress()); + + serverContext.awaitShutdown(); + return 0; + } +} diff --git a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Map.java b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Map.java index 0f0f1a4c..6744b00d 100644 --- a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Map.java +++ b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Map.java @@ -24,7 +24,7 @@ import picocli.CommandLine.Command; @Command(name = "map", description = "Map commands.", subcommands = {Init.class, Export.class, Serve.class, Dev.class, StyleCommand.class, - MBTiles.class}, + MBTiles.class, Contour.class}, sortOptions = false) public class Map implements Runnable { diff --git a/baremaps-core/pom.xml b/baremaps-core/pom.xml index 4b83e635..55159841 100644 --- a/baremaps-core/pom.xml +++ b/baremaps-core/pom.xml @@ -110,6 +110,10 @@ limitations under the License. <groupId>org.apache.lucene</groupId> <artifactId>lucene-spatial-extras</artifactId> </dependency> + <dependency> + <groupId>org.gdal</groupId> + <artifactId>gdal</artifactId> + </dependency> <dependency> <groupId>org.graalvm.js</groupId> <artifactId>js</artifactId> @@ -118,10 +122,6 @@ limitations under the License. <groupId>org.graalvm.sdk</groupId> <artifactId>graal-sdk</artifactId> </dependency> - <dependency> - <groupId>org.gdal</groupId> - <artifactId>gdal</artifactId> - </dependency> <dependency> <groupId>org.locationtech.jts</groupId> <artifactId>jts-core</artifactId> diff --git a/baremaps-core/src/main/java/org/apache/baremaps/raster/ContourTileStore.java b/baremaps-core/src/main/java/org/apache/baremaps/raster/ContourTileStore.java index 9e2b093b..bce42cd5 100644 --- a/baremaps-core/src/main/java/org/apache/baremaps/raster/ContourTileStore.java +++ b/baremaps-core/src/main/java/org/apache/baremaps/raster/ContourTileStore.java @@ -15,23 +15,24 @@ package org.apache.baremaps.raster; import java.nio.ByteBuffer; import java.nio.file.Paths; import java.util.List; +import java.util.Map; import java.util.Vector; -import java.util.stream.IntStream; import java.util.stream.LongStream; - -import org.apache.baremaps.database.tile.Tile; -import org.apache.baremaps.database.tile.TileStore; -import org.apache.baremaps.database.tile.TileStoreException; -import org.apache.baremaps.openstreetmap.utils.GeometryUtils; -import org.apache.baremaps.openstreetmap.utils.ProjectionTransformer; +import org.apache.baremaps.tilestore.TileCoord; +import org.apache.baremaps.tilestore.TileStore; +import org.apache.baremaps.tilestore.TileStoreException; +import org.apache.baremaps.utils.GeometryUtils; +import org.apache.baremaps.vectortile.Feature; +import org.apache.baremaps.vectortile.Layer; +import org.apache.baremaps.vectortile.VectorTileFunctions; import org.gdal.gdal.Dataset; import org.gdal.gdal.WarpOptions; import org.gdal.gdal.gdal; import org.gdal.gdalconst.gdalconstConstants; -import org.gdal.ogr.FieldDefn; import org.gdal.ogr.ogr; import org.gdal.osr.SpatialReference; -import org.locationtech.jts.geom.util.GeometryTransformer; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.proj4j.ProjCoordinate; public class ContourTileStore implements TileStore, AutoCloseable { @@ -49,52 +50,62 @@ public class ContourTileStore implements TileStore, AutoCloseable { } @Override - public ByteBuffer read(Tile tile) throws TileStoreException { + public ByteBuffer read(TileCoord tile) throws TileStoreException { var sourceBand = sourceDataset.GetRasterBand(1); var envelope = tile.envelope(); + // Transform the extent to the source projection + var transformer = GeometryUtils.coordinateTransform(4326, 3857); + var min = transformer.transform(new ProjCoordinate(envelope.getMinX(), envelope.getMinY()), + new ProjCoordinate()); + var max = transformer.transform(new ProjCoordinate(envelope.getMaxX(), envelope.getMaxY()), + new ProjCoordinate()); + var targetEnvelope = new Envelope(min.x, max.x, min.y, max.y); + // Warp the raster to the requested extent var rasterOptions = new WarpOptions(new Vector<>(List.of( - "-of", "MEM", - "-te", Double.toString(envelope.getMinX()), Double.toString(envelope.getMinY()), Double.toString(envelope.getMaxX()), Double.toString(envelope.getMaxY()), - "-te_srs", "EPSG:4326"))); - var rasterDataset = gdal.Warp("", new Dataset[]{sourceDataset}, rasterOptions); + "-of", "MEM", + "-te", Double.toString(envelope.getMinX()), Double.toString(envelope.getMinY()), + Double.toString(envelope.getMaxX()), Double.toString(envelope.getMaxY()), + "-te_srs", "EPSG:4326"))); + var rasterDataset = gdal.Warp("", new Dataset[] {sourceDataset}, rasterOptions); var rasterBand = rasterDataset.GetRasterBand(1); // Generate the contours - //var wkt = rasterDataset.GetProjection(); - //var srs = new SpatialReference(wkt); - var srs = new SpatialReference("EPSG:4326"); + var wkt = rasterDataset.GetProjection(); + var srs = new SpatialReference(wkt); var vectorDriver = ogr.GetDriverByName("Memory"); var vectorDataSource = vectorDriver.CreateDataSource("vector"); var vectorLayer = vectorDataSource.CreateLayer("vector", srs, ogr.wkbLineString); - gdal.ContourGenerateEx(rasterBand, vectorLayer, new Vector<>(List.of("LEVEL_INTERVAL=" + 10))); + gdal.ContourGenerateEx(rasterBand, vectorLayer, new Vector<>(List.of("LEVEL_INTERVAL=" + 50))); // return the contours - var geometries = LongStream.range(0, vectorLayer.GetFeatureCount()) - .mapToObj(vectorLayer::GetFeature) - .map(feature -> feature.GetGeometryRef()) - .map(geometry -> GeometryUtils.deserialize(geometry.ExportToWkb())) - .toList(); + var features = LongStream.range(0, vectorLayer.GetFeatureCount()) + .mapToObj(vectorLayer::GetFeature) + .map(feature -> feature.GetGeometryRef()) + .map(geometry -> GeometryUtils.deserialize(geometry.ExportToWkb())) + .map(geometry -> VectorTileFunctions.asVectorTileGeom(geometry, targetEnvelope, 4096, 0, true)) + .map(geometry -> new Feature(null, Map.of(), geometry)) + .toList(); + + var vectorTile = VectorTileFunctions.asVectorTile( + new org.apache.baremaps.vectortile.Tile(List.of(new Layer("contours", 4096, features)))); - var transformer = GeometryUtils.coordinateTransform(4326, 3857); - var min = transformer.transform(new ProjCoordinate(envelope.getMinX(), envelope.getMinY()), new ProjCoordinate()); - var max = transformer.transform(new ProjCoordinate(envelope.getMaxX(), envelope.getMaxY()), new ProjCoordinate()); rasterBand.delete(); rasterDataset.delete(); sourceBand.delete(); - return null; + return vectorTile; } @Override - public void write(Tile tile, ByteBuffer blob) throws TileStoreException { + public void write(TileCoord tile, ByteBuffer blob) throws TileStoreException { throw new UnsupportedOperationException(); } @Override - public void delete(Tile tile) throws TileStoreException { + public void delete(TileCoord tile) throws TileStoreException { throw new UnsupportedOperationException(); } @@ -105,8 +116,8 @@ public class ContourTileStore implements TileStore, AutoCloseable { public static void main(String[] args) throws Exception { var store = new ContourTileStore(); - store.read(new Tile(8492, 5792, 14).parent()); - } + store.read(new TileCoord(8492, 5792, 14).parent()); + } } diff --git a/baremaps-core/src/main/java/org/apache/baremaps/raster/Main.java b/baremaps-core/src/main/java/org/apache/baremaps/raster/Main.java index f447f5c5..e7dbd691 100644 --- a/baremaps-core/src/main/java/org/apache/baremaps/raster/Main.java +++ b/baremaps-core/src/main/java/org/apache/baremaps/raster/Main.java @@ -26,15 +26,15 @@ import org.gdal.osr.SpatialReference; public class Main { public static void main(String[] args) { - var sourceFilename = Paths.get("examples/contour/liecthenstein-aster-dem-v2-3857.tif") - .toAbsolutePath().toString(); - var hillshadeFilename = - Paths.get("examples/contour/liecthenstein-aster-dem-v2-3857-hillshade.tif").toAbsolutePath() - .toString(); - var outputFilename = Paths.get("examples/contour/liecthenstein-aster-dem-v2-3857.shp") - .toAbsolutePath().toString(); - var warpFilename = Paths.get("examples/contour/liecthenstein-aster-dem-v2-3857-warp.tif") - .toAbsolutePath().toString(); + var sourceFilename = Paths.get("examples/contour/liecthenstein-aster-dem-v2-3857.tif") + .toAbsolutePath().toString(); + var hillshadeFilename = + Paths.get("examples/contour/liecthenstein-aster-dem-v2-3857-hillshade.tif").toAbsolutePath() + .toString(); + var outputFilename = Paths.get("examples/contour/liecthenstein-aster-dem-v2-3857.shp") + .toAbsolutePath().toString(); + var warpFilename = Paths.get("examples/contour/liecthenstein-aster-dem-v2-3857-warp.tif") + .toAbsolutePath().toString(); var dem = Paths.get("examples/contour/dem.xml") .toAbsolutePath().toString(); @@ -44,10 +44,10 @@ public class Main { planetContour(); - hillshade(sourceFilename, 1, hillshadeFilename, 45d, 315d); - contourEx(hillshadeFilename, 1, outputFilename, 50, 0); - warp(sourceFilename, warpFilename); - shadow(hillshadeFilename, outputFilename); + hillshade(sourceFilename, 1, hillshadeFilename, 45d, 315d); + contourEx(hillshadeFilename, 1, outputFilename, 50, 0); + warp(sourceFilename, warpFilename); + shadow(hillshadeFilename, outputFilename); } public static void planetContour() { diff --git a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/Feature.java b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/Feature.java index 3fc3c587..7bd1d67a 100644 --- a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/Feature.java +++ b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/Feature.java @@ -26,7 +26,7 @@ import org.locationtech.jts.geom.Geometry; */ public class Feature { - private long id; + private Long id; private Map<String, Object> tags; @@ -44,7 +44,7 @@ public class Feature { * @param tags The tags of the feature. * @param geometry The geometry of the feature. */ - public Feature(long id, Map<String, Object> tags, Geometry geometry) { + public Feature(Long id, Map<String, Object> tags, Geometry geometry) { this.id = id; this.tags = tags; this.geometry = geometry; @@ -55,7 +55,7 @@ public class Feature { * * @return The id of the feature. */ - public long getId() { + public Long getId() { return id; } @@ -64,7 +64,7 @@ public class Feature { * * @param id The id of the feature. */ - public void setId(long id) { + public void setId(Long id) { this.id = id; } diff --git a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileEncoder.java b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileEncoder.java index a7c04910..0a679303 100644 --- a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileEncoder.java +++ b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileEncoder.java @@ -125,9 +125,10 @@ public class VectorTileEncoder { cy = 0; VectorTile.Tile.Feature.Builder builder = VectorTile.Tile.Feature.newBuilder(); - builder.setId(feature.getId()); + if (feature.getId() != null) { + builder.setId(feature.getId()); + } builder.setType(encodeGeometryType(feature.getGeometry())); - encodeTag(feature.getTags(), builder::addTags); encodeGeometry(feature.getGeometry(), builder::addGeometry); diff --git a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileFunctions.java b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileFunctions.java index 31888d83..59a45654 100644 --- a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileFunctions.java +++ b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileFunctions.java @@ -45,14 +45,13 @@ public class VectorTileFunctions { * @param clipGeom A flag to clip the geometry * @return The transformed geometry */ - public static Geometry asVectorTileGeom(Geometry geometry, Geometry envelope, int extent, + public static Geometry asVectorTileGeom(Geometry geometry, Envelope envelope, int extent, int buffer, boolean clipGeom) { // Scale the geometry to the extent of the tile - var envelopeInternal = envelope.getEnvelopeInternal(); - double scaleX = extent / envelopeInternal.getWidth(); - double scaleY = extent / envelopeInternal.getHeight(); + double scaleX = extent / envelope.getWidth(); + double scaleY = extent / envelope.getHeight(); AffineTransformation affineTransformation = new AffineTransformation(); - affineTransformation.translate(-envelopeInternal.getMinX(), -envelopeInternal.getMinY()); + affineTransformation.translate(-envelope.getMinX(), -envelope.getMinY()); affineTransformation.scale(scaleX, -scaleY); affineTransformation.translate(0, extent); Geometry scaledGeometry = affineTransformation.transform(geometry); @@ -94,11 +93,16 @@ public class VectorTileFunctions { * @return The transformed tile */ public static ByteBuffer asVectorTile(Tile vectorTile) { - return new VectorTileEncoder() - .encodeTile(vectorTile) - .toByteString() - .asReadOnlyByteBuffer(); - + ByteBuffer original = new VectorTileEncoder() + .encodeTile(vectorTile) + .toByteString() + .asReadOnlyByteBuffer(); + ByteBuffer clone = ByteBuffer.allocate(original.capacity()); + original.rewind();// copy from the beginning + clone.put(original); + original.rewind(); + clone.flip(); + return clone; } /** @@ -108,10 +112,18 @@ public class VectorTileFunctions { * @return The transformed layer */ public static ByteBuffer asVectorTileLayer(Layer layer) { - return new VectorTileEncoder() + ByteBuffer original = new VectorTileEncoder() .encodeLayer(layer) .toByteString() .asReadOnlyByteBuffer(); + + ByteBuffer clone = ByteBuffer.allocate(original.capacity()); + original.rewind();// copy from the beginning + clone.put(original); + original.rewind(); + clone.flip(); + + return clone; } /** diff --git a/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileFunctionsTest.java b/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileFunctionsTest.java index afeb8b89..181d493a 100644 --- a/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileFunctionsTest.java +++ b/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileFunctionsTest.java @@ -45,7 +45,7 @@ class VectorTileFunctionsTest { // Transform the input geometry into a vector tile geometry var outputGeom = - VectorTileFunctions.asVectorTileGeom(inputGeom, envelope, extent, buffer, clipGeom); + VectorTileFunctions.asVectorTileGeom(inputGeom, envelope.getEnvelopeInternal(), extent, buffer, clipGeom); // Check if the output geometry is not null assertNotNull(outputGeom); diff --git a/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileTest.java b/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileTest.java index bc8fbfa9..2f2394a9 100644 --- a/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileTest.java +++ b/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileTest.java @@ -41,9 +41,9 @@ public class VectorTileTest { public void endToEnd() { var tile = new Tile(List.of( new Layer("layer", 256, List.of( - new Feature(1, Map.of("a", 1.0, "b", "2"), + new Feature(1l, Map.of("a", 1.0, "b", "2"), GEOMETRY_FACTORY.createPoint(new Coordinate(1, 2))), - new Feature(2, Map.of("c", 3.0, "d", "4"), + new Feature(2l, Map.of("c", 3.0, "d", "4"), GEOMETRY_FACTORY.createPoint(new Coordinate(2, 3))))))); var encoded = new VectorTileEncoder().encodeTile(tile); diff --git a/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileViewer.java b/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileViewer.java index faada968..a4f75369 100644 --- a/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileViewer.java +++ b/baremaps-core/src/test/java/org/apache/baremaps/vectortile/VectorTileViewer.java @@ -19,10 +19,8 @@ package org.apache.baremaps.vectortile; import java.awt.*; import java.awt.Dimension; +import java.net.URL; import java.nio.ByteBuffer; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.zip.GZIPInputStream; import javax.swing.*; import org.locationtech.jts.geom.*; import org.locationtech.jts.geom.Point; @@ -34,10 +32,14 @@ import org.locationtech.jts.geom.Polygon; public class VectorTileViewer { public static void main(String... args) throws Exception { - String arg = - args.length > 0 ? args[0] : "baremaps-core/src/test/resources/vectortile/14-8493-5795.mvt"; - var path = Path.of(arg); - try (var input = new GZIPInputStream(Files.newInputStream(path))) { + // String arg = + // args.length > 0 ? args[0] : "baremaps-core/src/test/resources/vectortile/14-8493-5795.mvt"; + // var path = Path.of(arg); + // try (var input = new GZIPInputStream(Files.newInputStream(path))) { + + var url = new URL("http://localhost:9000/tiles/14/8628/5750.mvt"); + + try (var input = url.openStream()) { var buffer = ByteBuffer.wrap(input.readAllBytes()); var parsed = org.apache.baremaps.mvt.binary.VectorTile.Tile.parseFrom(buffer); var tile = new VectorTileDecoder().decodeTile(parsed); diff --git a/baremaps-server/src/main/java/org/apache/baremaps/server/ServerResources.java b/baremaps-server/src/main/java/org/apache/baremaps/server/ServerResources.java new file mode 100644 index 00000000..781dbf68 --- /dev/null +++ b/baremaps-server/src/main/java/org/apache/baremaps/server/ServerResources.java @@ -0,0 +1,116 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.baremaps.server; + +import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN; +import static com.google.common.net.HttpHeaders.CONTENT_TYPE; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.file.Path; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.ws.rs.GET; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.apache.baremaps.config.ConfigReader; +import org.apache.baremaps.tilestore.TileCoord; +import org.apache.baremaps.tilestore.TileStore; +import org.apache.baremaps.tilestore.TileStoreException; +import org.apache.baremaps.vectortile.style.Style; +import org.apache.baremaps.vectortile.tileset.Tileset; +import org.apache.baremaps.vectortile.tileset.TilesetLayer; + +@Singleton [email protected]("/") +public class ServerResources { + + private final Style style; + + private final Tileset tileset; + + private final TileStore tileStore; + + public static final String TILE_ENCODING = "gzip"; + + public static final String TILE_TYPE = "application/vnd.mapbox-vector-tile"; + + @Inject + public ServerResources(@Named("tileset") Path tileset, @Named("style") Path style, + TileStore tileStore, ObjectMapper objectMapper) throws IOException { + this.tileStore = tileStore; + var configReader = new ConfigReader(); + this.style = objectMapper.readValue(configReader.read(style), Style.class); + this.tileset = objectMapper.readValue(configReader.read(tileset), Tileset.class); + + // Hide the SQL queries in production + for (TilesetLayer layer : this.tileset.getVectorLayers()) { + layer.setQueries(null); + } + } + + @GET + @javax.ws.rs.Path("style.json") + @Produces(MediaType.APPLICATION_JSON) + public Style getStyle() { + return style; + } + + @GET + @javax.ws.rs.Path("tiles.json") + @Produces(MediaType.APPLICATION_JSON) + public Tileset getTileset() { + return tileset; + } + + @GET + @javax.ws.rs.Path("/tiles/{z}/{x}/{y}.mvt") + public Response getTile(@PathParam("z") int z, @PathParam("x") int x, @PathParam("y") int y) { + TileCoord tileCoord = new TileCoord(x, y, z); + try { + ByteBuffer blob = tileStore.read(tileCoord); + if (blob != null) { + var array = blob.array(); + return Response.status(200) // lgtm [java/xss] + .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .header(CONTENT_TYPE, TILE_TYPE) + // .header(CONTENT_ENCODING, TILE_ENCODING) + .entity(array).build(); + } else { + return Response.status(204).build(); + } + } catch (TileStoreException ex) { + return Response.status(404).build(); + } + } + + @GET + @javax.ws.rs.Path("{path:.*}") + public Response get(@PathParam("path") String path) throws IOException { + if (path.equals("") || path.endsWith("/")) { + path += "server.html"; + } + path = String.format("assets/%s", path); + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path)) { + var bytes = inputStream.readAllBytes(); + return Response.ok().entity(bytes).build(); + } catch (NullPointerException | IOException e) { + return Response.status(404).build(); + } + } +} diff --git a/examples/contour/style.json b/examples/contour/style.json index 16047415..bb982ecc 100644 --- a/examples/contour/style.json +++ b/examples/contour/style.json @@ -14,10 +14,10 @@ "background-color" : "rgba(255, 255, 255, 1)" } }, { - "id" : "aster_dem", + "id" : "contours", "type" : "line", "source" : "baremaps", - "source-layer" : "aster_dem", + "source-layer" : "contours", "layout" : { "line-cap" : "round", "line-join" : "round" diff --git a/examples/contour/tileset.json b/examples/contour/tileset.json index 74307e7b..2d729bdb 100644 --- a/examples/contour/tileset.json +++ b/examples/contour/tileset.json @@ -5,24 +5,17 @@ 47.166, 14.0 ], - "bounds": [ - 9.471078, - 47.04774, - 9.636217, - 47.27128 - ], "tiles": [ "http://localhost:9000/tiles/{z}/{x}/{y}.mvt" ], "database": "jdbc:postgresql://localhost:5432/baremaps?&user=baremaps&password=baremaps", "vector_layers": [ { - "id": "aster_dem", + "id": "contours", "queries": [ { "minzoom": 0, - "maxzoom": 20, - "sql": "SELECT ogc_fid, jsonb_build_object('elevation', elevation::text), wkb_geometry FROM aster_dem" + "maxzoom": 20 } ] }
