You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@baremaps.apache.org by bc...@apache.org on 2023/10/08 14:43:18 UTC

[incubator-baremaps] branch gdal updated (e7626452 -> 0ba2afb6)

This is an automated email from the ASF dual-hosted git repository.

bchapuis pushed a change to branch gdal
in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git


 discard e7626452 Improve contour generation
 discard 6f2adc55 Add raster source
 discard 1b71c591 Improve levels and config
 discard f5fa44f5 Fix envelope and levels
 discard 39abfc9c Some fixes
 discard d88eb0b4 Fix issues after rebase
 discard fec514b9 Experiment with TwelveMonkeys
 discard 374c6858 Serve contour tiles
 discard 41407c56 Improve tile store
 discard 499a2381 WIP - experiment with gdal
    omit 6509fc43 Correct the license header and the disclaimer-wip file (#786)
     add 8860c7f2 Correct the license header and the disclaimer-wip file (#786)
     new 044970fc WIP - experiment with gdal
     new 717f3fac Improve tile store
     new e0c6791e Serve contour tiles
     new 2a80557c Experiment with TwelveMonkeys
     new e4c6b216 Fix issues after rebase
     new 0fb7a99a Some fixes
     new 8a2fe32e Fix envelope and levels
     new 03e6542b Improve levels and config
     new 09a1f15b Add raster source
     new 0ba2afb6 Improve contour generation

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (e7626452)
            \
             N -- N -- N   refs/heads/gdal (0ba2afb6)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 10 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../baremaps/geocoderosm/GeocoderOsmConsumerEntity.java | 17 +++++++++++------
 .../baremaps/geocoderosm/GeocoderOsmDocumentMapper.java | 17 +++++++++++------
 .../apache/baremaps/geocoderosm/GeocoderOsmQuery.java   | 17 +++++++++++------
 .../java/org/apache/baremaps/geocoderosm/OsmTags.java   | 17 +++++++++++------
 .../workflow/tasks/CreateGeocoderOpenStreetMap.java     | 17 +++++++++++------
 .../org/apache/baremaps/geocoderosm/OSMIndexTest.java   | 17 +++++++++++------
 6 files changed, 66 insertions(+), 36 deletions(-)


[incubator-baremaps] 09/10: Add raster source

Posted by bc...@apache.org.
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 09a1f15b73791f889fca1db7349448fea027df9b
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Tue Oct 3 23:08:33 2023 +0200

    Add raster source
---
 examples/contour/style.json | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/examples/contour/style.json b/examples/contour/style.json
index bb982ecc..90d99d22 100644
--- a/examples/contour/style.json
+++ b/examples/contour/style.json
@@ -4,6 +4,16 @@
     "baremaps" : {
       "type" : "vector",
       "url" : "http://localhost:9000/tiles.json"
+    },
+    "dem": {
+     "type": "raster-dem",
+      "encoding": "terrarium",
+      "tiles": [
+        "https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png"
+      ],
+      "tileSize": 256,
+      "maxzoom": 13,
+      "minzoom": 0
     }
   },
   "layers" : [ {
@@ -13,6 +23,13 @@
     "paint" : {
       "background-color" : "rgba(255, 255, 255, 1)"
     }
+  },{
+    "id": "hills",
+    "type": "hillshade",
+    "source": "dem",
+    "paint": {
+      "hillshade-exaggeration": 0.25
+    }
   }, {
     "id" : "contours",
     "type" : "line",


[incubator-baremaps] 04/10: Experiment with TwelveMonkeys

Posted by bc...@apache.org.
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 2a80557cbc9b05ffb7d8a8521e53efe95c5dc925
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Mon Apr 24 13:20:23 2023 +0200

    Experiment with TwelveMonkeys
---
 baremaps-core/pom.xml                              | 22 ++++++++++++++++++++++
 .../java/org/apache/baremaps/raster/Image.java     | 22 ++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/baremaps-core/pom.xml b/baremaps-core/pom.xml
index 55159841..e38f2be1 100644
--- a/baremaps-core/pom.xml
+++ b/baremaps-core/pom.xml
@@ -25,7 +25,29 @@ limitations under the License.
 
   <artifactId>baremaps-core</artifactId>
 
+  <repositories>
+    <repository>
+      <id>twelvemonkeys</id>
+      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
+    </repository>
+  </repositories>
+
   <dependencies>
+    <dependency>
+      <groupId>com.twelvemonkeys.imageio</groupId>
+      <artifactId>imageio-tiff</artifactId>
+      <version>3.10.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>com.twelvemonkeys.imageio</groupId>
+      <artifactId>imageio-metadata</artifactId>
+      <version>3.9.4</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sis.storage</groupId>
+      <artifactId>sis-geotiff</artifactId>
+      <version>1.3</version>
+    </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/raster/Image.java b/baremaps-core/src/main/java/org/apache/baremaps/raster/Image.java
new file mode 100644
index 00000000..896304be
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/raster/Image.java
@@ -0,0 +1,22 @@
+package org.apache.baremaps.raster;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.URL;
+
+
+public class Image {
+
+    public static void main(String[] args) throws IOException {
+        String urlString = String.format("https://s3.amazonaws.com/elevation-tiles-prod/geotiff/%s/%s/%s.tif", 14, 8492, 5792);
+        URL url = new URL(urlString);
+        BufferedImage tiffImage = ImageIO.read(url);
+        var raster = tiffImage.getRaster();
+        for (int x = 0; x < raster.getWidth(); x++) {
+            for (int y = 0; y < raster.getHeight(); y++) {
+                System.out.println(raster.getSampleFloat(y, y, 0));
+            }
+        }
+    }
+}


[incubator-baremaps] 02/10: Improve tile store

Posted by bc...@apache.org.
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 717f3fac517f6b8998e6651ad253cecdd1182fcf
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Sun Apr 23 11:03:36 2023 +0200

    Improve tile store
---
 .run/ContourTileStore.run.xml                      |  16 +++
 .../apache/baremaps/raster/ContourTileStore.java   | 116 ++++++++++++---------
 .../main/java/org/apache/baremaps/raster/Main.java |  35 +++----
 .../org/apache/baremaps/tilestore/TileCoord.java   |   4 +-
 4 files changed, 100 insertions(+), 71 deletions(-)

diff --git a/.run/ContourTileStore.run.xml b/.run/ContourTileStore.run.xml
new file mode 100644
index 00000000..307d971d
--- /dev/null
+++ b/.run/ContourTileStore.run.xml
@@ -0,0 +1,16 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="ContourTileStore" type="Application" factoryName="Application" nameIsGenerated="true">
+    <option name="MAIN_CLASS_NAME" value="org.apache.baremaps.raster.ContourTileStore" />
+    <module name="baremaps-core" />
+    <option name="VM_PARAMETERS" value="-Djava.library.path=$PROJECT_DIR$/../../osgeo/gdal/build/swig/java" />
+    <extension name="coverage">
+      <pattern>
+        <option name="PATTERN" value="org.apache.baremaps.raster.*" />
+        <option name="ENABLED" value="true" />
+      </pattern>
+    </extension>
+    <method v="2">
+      <option name="Make" enabled="true" />
+    </method>
+  </configuration>
+</component>
\ No newline at end of file
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 a352fa20..9e2b093b 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
@@ -12,73 +12,79 @@
 
 package org.apache.baremaps.raster;
 
-import java.net.URL;
 import java.nio.ByteBuffer;
-import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.List;
 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.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.proj4j.ProjCoordinate;
 
-public class ContourTileStore implements TileStore {
-  @Override
-  public ByteBuffer read(Tile tile) throws TileStoreException {
-    var file = Paths.get(String.format("%s/%s/%s.tif", tile.z(), tile.x(), tile.y()));
-    var source = String.format("https://s3.amazonaws.com/elevation-tiles-prod/geotiff/%s", file);
-
-    try {
-      Files.deleteIfExists(file);
-      Files.createDirectories(file.getParent());
-      Files.createFile(file);
-      try (var stream = new URL(source).openStream()) {
-        Files.copy(stream, file);
-      }
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-
-    var dataset = gdal.Open(file.toString(), gdalconstConstants.GA_ReadOnly);
-
-    dataset.GetGeoTransform();
-    dataset.GetRasterXSize();
-    dataset.GetRasterYSize();
-
-
-    var band = dataset.GetRasterBand(1);
-
-    band.ReadRaster_Direct(0, 0, 100, 100);
-
-    var wkt = dataset.GetProjection();
-    var srs = new SpatialReference(wkt);
-
-    var driver = ogr.GetDriverByName("Memory");
-    var dataSource = driver.CreateDataSource("memory_name");
+public class ContourTileStore implements TileStore, AutoCloseable {
 
-    var layer = dataSource.CreateLayer("contour", srs, ogr.wkbLineString);
+  static {
+    gdal.AllRegister();
+    ogr.RegisterAll();
+  }
 
-    var field = new FieldDefn("ID", ogr.OFTInteger);
-    field.SetWidth(8);
-    layer.CreateField(field, 0);
-    field.delete();
+  private final Dataset sourceDataset;
 
-    gdal.ContourGenerateEx(band, layer, new Vector<>(List.of(
-        "LEVEL_INTERVAL=" + 10)));
+  public ContourTileStore() {
+    var dem = Paths.get("examples/contour/dem.xml").toAbsolutePath().toString();
+    sourceDataset = gdal.Open(dem, gdalconstConstants.GA_ReadOnly);
+  }
 
-    for (int i = 0; i < layer.GetFeatureCount(); i++) {
-      var feature = layer.GetFeature(i);
-      var geometry = feature.GetGeometryRef();
-      // System.out.println(geometry.ExportToWkt());
-    }
+  @Override
+  public ByteBuffer read(Tile tile) throws TileStoreException {
+    var sourceBand = sourceDataset.GetRasterBand(1);
+    var envelope = tile.envelope();
+
+    // 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);
+    var rasterBand = rasterDataset.GetRasterBand(1);
+
+    // Generate the contours
+    //var wkt = rasterDataset.GetProjection();
+    //var srs = new SpatialReference(wkt);
+    var srs = new SpatialReference("EPSG:4326");
+    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)));
+
+    // 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 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();
 
-    dataSource.delete();
-    dataset.delete();
     return null;
   }
 
@@ -91,4 +97,16 @@ public class ContourTileStore implements TileStore {
   public void delete(Tile tile) throws TileStoreException {
     throw new UnsupportedOperationException();
   }
+
+  @Override
+  public void close() throws Exception {
+    sourceDataset.delete();
+  }
+
+  public static void main(String[] args) throws Exception {
+    var store = new ContourTileStore();
+    store.read(new Tile(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 4e37b4d5..f447f5c5 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,17 +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,15 +42,15 @@ public class Main {
     gdal.AllRegister();
     ogr.RegisterAll();
 
-    planetContour("https://s3.amazonaws.com/elevation-tiles-prod/geotiff/%s/%s/%s.tif");
+    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(String source) {
+  public static void planetContour() {
     var file = Paths.get(String.format("%s/%s/%s.tif", 14, 8514, 5816));
     var url = String.format("https://s3.amazonaws.com/elevation-tiles-prod/geotiff/%s", file);
     System.out.println(url);
@@ -69,7 +67,6 @@ public class Main {
       e.printStackTrace();
     }
 
-
     var dataset = gdal.Open(file.toString(), gdalconstConstants.GA_ReadOnly);
 
     var band = dataset.GetRasterBand(1);
@@ -231,10 +228,8 @@ public class Main {
 
     dataSource.delete();
     dataset.delete();
-
   }
 
-
   public static void hillshade(String source, Integer sourceBand, String target, Double azimuth,
       Double altitude) {
     var options = new Vector<>(List.of(
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileCoord.java b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileCoord.java
index a9dbf97e..1953e634 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileCoord.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileCoord.java
@@ -198,8 +198,8 @@ public final class TileCoord implements Comparable<TileCoord> {
   public Envelope envelope() {
     double x1 = tile2lon(x, z);
     double x2 = tile2lon(x + 1, z);
-    double y1 = tile2lat(y + 1, z);
-    double y2 = tile2lat(y, z);
+    double y1 = tile2lat(y, z);
+    double y2 = tile2lat(y + 1, z);
     return new Envelope(x1, x2, y1, y2);
   }
 


[incubator-baremaps] 01/10: WIP - experiment with gdal

Posted by bc...@apache.org.
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 044970fc4bb4a3fcaf3f381a4b23cde552f30aa5
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Fri Mar 24 16:05:09 2023 +0100

    WIP - experiment with gdal
---
 baremaps-core/pom.xml                              |   4 +
 .../apache/baremaps/raster/ContourTileStore.java   |  94 ++++++++
 .../main/java/org/apache/baremaps/raster/Main.java | 257 +++++++++++++++++++++
 examples/contour/dem.xml                           |  29 +++
 pom.xml                                            |   6 +
 5 files changed, 390 insertions(+)

diff --git a/baremaps-core/pom.xml b/baremaps-core/pom.xml
index fe924eec..4b83e635 100644
--- a/baremaps-core/pom.xml
+++ b/baremaps-core/pom.xml
@@ -118,6 +118,10 @@ 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
new file mode 100644
index 00000000..a352fa20
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/raster/ContourTileStore.java
@@ -0,0 +1,94 @@
+/*
+ * 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.raster;
+
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Vector;
+import org.apache.baremaps.database.tile.Tile;
+import org.apache.baremaps.database.tile.TileStore;
+import org.apache.baremaps.database.tile.TileStoreException;
+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;
+
+public class ContourTileStore implements TileStore {
+  @Override
+  public ByteBuffer read(Tile tile) throws TileStoreException {
+    var file = Paths.get(String.format("%s/%s/%s.tif", tile.z(), tile.x(), tile.y()));
+    var source = String.format("https://s3.amazonaws.com/elevation-tiles-prod/geotiff/%s", file);
+
+    try {
+      Files.deleteIfExists(file);
+      Files.createDirectories(file.getParent());
+      Files.createFile(file);
+      try (var stream = new URL(source).openStream()) {
+        Files.copy(stream, file);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+    var dataset = gdal.Open(file.toString(), gdalconstConstants.GA_ReadOnly);
+
+    dataset.GetGeoTransform();
+    dataset.GetRasterXSize();
+    dataset.GetRasterYSize();
+
+
+    var band = dataset.GetRasterBand(1);
+
+    band.ReadRaster_Direct(0, 0, 100, 100);
+
+    var wkt = dataset.GetProjection();
+    var srs = new SpatialReference(wkt);
+
+    var driver = ogr.GetDriverByName("Memory");
+    var dataSource = driver.CreateDataSource("memory_name");
+
+    var layer = dataSource.CreateLayer("contour", srs, ogr.wkbLineString);
+
+    var field = new FieldDefn("ID", ogr.OFTInteger);
+    field.SetWidth(8);
+    layer.CreateField(field, 0);
+    field.delete();
+
+    gdal.ContourGenerateEx(band, layer, new Vector<>(List.of(
+        "LEVEL_INTERVAL=" + 10)));
+
+    for (int i = 0; i < layer.GetFeatureCount(); i++) {
+      var feature = layer.GetFeature(i);
+      var geometry = feature.GetGeometryRef();
+      // System.out.println(geometry.ExportToWkt());
+    }
+
+    dataSource.delete();
+    dataset.delete();
+    return null;
+  }
+
+  @Override
+  public void write(Tile tile, ByteBuffer blob) throws TileStoreException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void delete(Tile tile) throws TileStoreException {
+    throw new UnsupportedOperationException();
+  }
+}
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
new file mode 100644
index 00000000..4e37b4d5
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/raster/Main.java
@@ -0,0 +1,257 @@
+/*
+ * 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.raster;
+
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.List;
+import java.util.Vector;
+import org.gdal.gdal.*;
+import org.gdal.gdalconst.gdalconstConstants;
+import org.gdal.ogr.*;
+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 dem = Paths.get("examples/contour/dem.xml")
+        .toAbsolutePath().toString();
+
+    gdal.AllRegister();
+    ogr.RegisterAll();
+
+    planetContour("https://s3.amazonaws.com/elevation-tiles-prod/geotiff/%s/%s/%s.tif");
+
+    // hillshade(sourceFilename, 1, hillshadeFilename, 45d, 315d);
+    // contourEx(hillshadeFilename, 1, outputFilename, 50, 0);
+    // warp(sourceFilename, warpFilename);
+    // shadow(hillshadeFilename, outputFilename);
+  }
+
+  public static void planetContour(String source) {
+    var file = Paths.get(String.format("%s/%s/%s.tif", 14, 8514, 5816));
+    var url = String.format("https://s3.amazonaws.com/elevation-tiles-prod/geotiff/%s", file);
+    System.out.println(url);
+
+    try {
+      Files.deleteIfExists(file);
+      Files.createDirectories(file.getParent());
+      Files.createFile(file);
+      try (var stream = new URL(url).openStream()) {
+        Files.copy(stream, file, StandardCopyOption.REPLACE_EXISTING);
+      }
+      System.out.println(Files.size(file));
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+
+    var dataset = gdal.Open(file.toString(), gdalconstConstants.GA_ReadOnly);
+
+    var band = dataset.GetRasterBand(1);
+
+    band.ReadRaster_Direct(0, 0, 100, 100);
+
+    var wkt = dataset.GetProjection();
+    var srs = new SpatialReference(wkt);
+
+    var driver = ogr.GetDriverByName("Memory");
+    var dataSource = driver.CreateDataSource("memory_name");
+
+    var layer = dataSource.CreateLayer("contour", srs, ogr.wkbLineString);
+
+    var field = new FieldDefn("ID", ogr.OFTInteger);
+    field.SetWidth(8);
+    layer.CreateField(field, 0);
+    field.delete();
+
+    gdal.ContourGenerateEx(band, layer, new Vector<>(List.of(
+        "LEVEL_INTERVAL=" + 10)));
+
+    for (int i = 0; i < layer.GetFeatureCount(); i++) {
+      var feature = layer.GetFeature(i);
+      var geometry = feature.GetGeometryRef();
+      System.out.println(geometry.ExportToWkt());
+    }
+
+    dataSource.delete();
+    dataset.delete();
+  }
+
+  public static void contour(String source, Integer sourceBand, String target,
+      Integer contourInterval,
+      Integer contourBase) {
+
+    var dataset = gdal.Open(source, gdalconstConstants.GA_ReadOnly);
+    var band = dataset.GetRasterBand(sourceBand);
+    var wkt = dataset.GetProjection();
+    var srs = new SpatialReference(wkt);
+
+    var driver = ogr.GetDriverByName("ESRI Shapefile");
+    var dataSource = driver.CreateDataSource(target);
+    var layer = dataSource.CreateLayer("contour", srs, ogr.wkbLineString);
+    var field = new FieldDefn("ID", ogr.OFTInteger);
+
+    field.SetWidth(8);
+    layer.CreateField(field, 0);
+    field.delete();
+
+    var feature = layer.GetLayerDefn();
+    gdal.ContourGenerate(band, contourInterval, contourBase, null,
+        0, 0, layer, feature.GetFieldIndex("ID"),
+        -1);
+
+    dataSource.delete();
+    dataset.delete();
+  }
+
+  public static void contourEx(String source, Integer sourceBand, String target,
+      Integer contourInterval,
+      Integer contourBase) {
+
+    var dataset = gdal.Open(source, gdalconstConstants.GA_ReadOnly);
+    var band = dataset.GetRasterBand(sourceBand);
+    var wkt = dataset.GetProjection();
+    var srs = new SpatialReference(wkt);
+
+    var driver = ogr.GetDriverByName("ESRI Shapefile");
+    var dataSource = driver.CreateDataSource(target);
+    var layer = dataSource.CreateLayer("contour", srs, ogr.wkbLineString);
+    var field = new FieldDefn("ID", ogr.OFTInteger);
+
+    field.SetWidth(8);
+    layer.CreateField(field, 0);
+    field.delete();
+
+    gdal.ContourGenerateEx(band, layer, new Vector<>(List.of(
+        "LEVEL_BASE=" + contourBase,
+        "LEVEL_INTERVAL=" + contourInterval,
+        "POLYGONIZE=YES")));
+
+    dataSource.delete();
+    dataset.delete();
+  }
+
+  public static void polygonize(String source, Integer sourceBand, String target) {
+    var dataset = gdal.Open(source, gdalconstConstants.GA_ReadOnly);
+    var band = dataset.GetRasterBand(sourceBand);
+    var wkt = dataset.GetProjection();
+    var srs = new SpatialReference(wkt);
+
+    var driver = ogr.GetDriverByName("ESRI Shapefile");
+    var dataSource = driver.CreateDataSource(target);
+    var layer = dataSource.CreateLayer("polygonize", srs, ogr.wkbPolygon);
+    var field = new FieldDefn("ID", ogr.OFTInteger);
+
+    field.SetWidth(8);
+    layer.CreateField(field, 0);
+    field.delete();
+
+    var feature = layer.GetLayerDefn();
+    gdal.Polygonize(band, null, layer, feature.GetFieldIndex("ID"),
+        null, null);
+
+    dataSource.delete();
+    dataset.delete();
+  }
+
+  public static void warp(String source, String target) {
+    var dataset = gdal.Open(source, gdalconstConstants.GA_ReadOnly);
+    var transform = dataset.GetGeoTransform();
+    var xRes = transform[1];
+    var yRes = transform[5];
+
+    var options = new Vector<>(List.of(
+        "-tr", Double.toString(xRes * 10), Double.toString(yRes * 10)));
+
+    var warp = gdal.Warp(target, new Dataset[] {dataset}, new WarpOptions(options));
+
+    warp.delete();
+    dataset.delete();
+  }
+
+
+  public record ShadowClass(int a, int b, int c) {
+  }
+
+  private static final List<ShadowClass> shadowClasses = List.of(
+      new ShadowClass(1, 250, 255),
+      new ShadowClass(2, 240, 255),
+      new ShadowClass(3, 1, 150),
+      new ShadowClass(4, 1, 100),
+      new ShadowClass(5, 1, 65),
+      new ShadowClass(6, 1, 2));
+
+  private static void shadow(String source, String target) {
+    var dataset = gdal.Open(source, gdalconstConstants.GA_ReadOnly);
+    var band = dataset.GetRasterBand(1);
+    var wkt = dataset.GetProjection();
+    var srs = new SpatialReference(wkt);
+
+    var driver = ogr.GetDriverByName("ESRI Shapefile");
+    var dataSource = driver.CreateDataSource(target);
+
+    for (var shadowClass : shadowClasses) {
+      var layer = dataSource.CreateLayer("shadow-" + shadowClass.a(), srs, ogr.wkbPolygon);
+      var field = new FieldDefn("ID", ogr.OFTInteger);
+
+      field.SetWidth(8);
+      layer.CreateField(field, 0);
+      field.delete();
+
+      gdal.ContourGenerateEx(band, layer, new Vector<>(List.of(
+          "LEVEL_BASE=" + shadowClass.b(),
+          "LEVEL_INTERVAL=" + shadowClass.c(),
+          "POLYGONIZE=YES")));
+    }
+
+    dataSource.delete();
+    dataset.delete();
+
+  }
+
+
+  public static void hillshade(String source, Integer sourceBand, String target, Double azimuth,
+      Double altitude) {
+    var options = new Vector<>(List.of(
+        "-az", azimuth.toString(),
+        "-alt", altitude.toString(),
+        "-z", "1.0",
+        "-s", "1.0",
+        "-b", sourceBand.toString(),
+        "-of", "GTiff",
+        "-combined"));
+
+    var dataset = gdal.Open(source, gdalconstConstants.GA_ReadOnly);
+    var hillshadeDataset =
+        gdal.DEMProcessing(target, dataset, "hillshade", null, new DEMProcessingOptions(options));
+
+    hillshadeDataset.delete();
+    dataset.delete();
+  }
+
+}
diff --git a/examples/contour/dem.xml b/examples/contour/dem.xml
new file mode 100644
index 00000000..178ca466
--- /dev/null
+++ b/examples/contour/dem.xml
@@ -0,0 +1,29 @@
+<GDAL_WMS>
+    <!--
+    Generate contour lines for the planet.
+    gdal_contour -a elevation -nln contour -i 10 -f GPKG dem.xml dem.gpkg
+    -->
+    <Service name="TMS">
+        <ServerUrl>https://s3.amazonaws.com/elevation-tiles-prod/geotiff/${z}/${x}/${y}.tif</ServerUrl>
+    </Service>
+    <DataWindow>
+        <UpperLeftX>-20037508.34</UpperLeftX>
+        <UpperLeftY>20037508.34</UpperLeftY>
+        <LowerRightX>20037508.34</LowerRightX>
+        <LowerRightY>-20037508.34</LowerRightY>
+        <TileLevel>14</TileLevel>
+        <TileCountX>1</TileCountX>
+        <TileCountY>1</TileCountY>
+        <YOrigin>top</YOrigin>
+    </DataWindow>
+    <Projection>EPSG:3857</Projection>
+    <BlockSizeX>512</BlockSizeX>
+    <BlockSizeY>512</BlockSizeY>
+    <BandsCount>1</BandsCount>
+    <DataType>Int16</DataType>
+    <ZeroBlockHttpCodes>403,404</ZeroBlockHttpCodes>
+    <DataValues>
+        <NoData>-32768</NoData>
+    </DataValues>
+    <Cache/>
+</GDAL_WMS>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 83662488..e0d6812f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -89,6 +89,7 @@ limitations under the License.
     <version.lib.commons-compress>1.21</version.lib.commons-compress>
     <version.lib.fastutil>8.5.9</version.lib.fastutil>
     <version.lib.flatgeobuf>3.24.0</version.lib.flatgeobuf>
+    <version.lib.gdal>3.6.3</version.lib.gdal>
     <version.lib.geopackage>6.5.0</version.lib.geopackage>
     <version.lib.graalvm>22.2.0</version.lib.graalvm>
     <version.lib.guava>31.1-jre</version.lib.guava>
@@ -393,6 +394,11 @@ limitations under the License.
         <version>${version.lib.awaitability}</version>
         <scope>test</scope>
       </dependency>
+      <dependency>
+        <groupId>org.gdal</groupId>
+        <artifactId>gdal</artifactId>
+        <version>${version.lib.gdal}</version>
+      </dependency>
       <dependency>
         <groupId>org.glassfish.jaxb</groupId>
         <artifactId>jaxb-runtime</artifactId>


[incubator-baremaps] 07/10: Fix envelope and levels

Posted by bc...@apache.org.
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 8a2fe32e6b1db6e0d7c65210e493d74b189a1214
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Mon Sep 25 17:22:32 2023 +0200

    Fix envelope and levels
---
 .../apache/baremaps/raster/ContourTileStore.java   | 231 +++++++++++++--------
 .../org/apache/baremaps/tilestore/TileCoord.java   |   4 +-
 .../baremaps/database/calcite/CalciteTest.java     |   1 -
 .../apache/baremaps/server/TilesetResource.java    |   5 +-
 examples/contour/dem.xml                           |   2 +-
 5 files changed, 149 insertions(+), 94 deletions(-)

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 5eeb5855..2aead3c5 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
@@ -12,12 +12,6 @@
 
 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.LongStream;
 import org.apache.baremaps.tilestore.TileCoord;
 import org.apache.baremaps.tilestore.TileStore;
 import org.apache.baremaps.tilestore.TileStoreException;
@@ -32,91 +26,152 @@ import org.gdal.gdal.gdal;
 import org.gdal.gdalconst.gdalconstConstants;
 import org.gdal.ogr.ogr;
 import org.gdal.osr.SpatialReference;
-import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.proj4j.ProjCoordinate;
 
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+
 public class ContourTileStore implements TileStore, AutoCloseable {
 
-  static {
-    gdal.AllRegister();
-    ogr.RegisterAll();
-  }
-
-  private final Dataset sourceDataset;
-
-  public ContourTileStore() {
-    var dem = Paths.get("dem.xml").toAbsolutePath().toString();
-    sourceDataset = gdal.Open(dem, gdalconstConstants.GA_ReadOnly);
-  }
-
-  @Override
-  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);
-    var rasterBand = rasterDataset.GetRasterBand(1);
-
-    // Generate the contours
-    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=" + 50)));
-
-    // return the contours
-    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 VectorTile(List.of(new Layer("contours", 4096, features))));
-
-    rasterBand.delete();
-    rasterDataset.delete();
-    sourceBand.delete();
-
-    return vectorTile;
-  }
-
-  @Override
-  public void write(TileCoord tile, ByteBuffer blob) throws TileStoreException {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  public void delete(TileCoord tile) throws TileStoreException {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  public void close() throws Exception {
-    sourceDataset.delete();
-  }
-
-  public static void main(String[] args) throws Exception {
-    var store = new ContourTileStore();
-    store.read(new TileCoord(8492, 5792, 14).parent());
-  }
+    static {
+        gdal.AllRegister();
+        ogr.RegisterAll();
+    }
+
+    private final Map<Integer, Dataset> datasets = new HashMap<>();
+
+    public ContourTileStore() {
+    }
+
+    public Dataset initDataset(Integer zoom) {
+        try {
+            var dem = Files.readString(Paths.get("dem.xml"));
+            dem = dem.replace("<TileLevel>0</TileLevel>", "<TileLevel>" + zoom + "</TileLevel>");
+            return gdal.Open(dem, gdalconstConstants.GA_ReadOnly);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    @Override
+    public synchronized ByteBuffer read(TileCoord tile) throws TileStoreException {
+        var dataset = datasets.computeIfAbsent(tile.z(), this::initDataset);
+        var sourceBand = dataset.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 buffer = (max.x - min.x) / 4096 * 16;
+
+        var targetEnvelope = new GeometryFactory().createPolygon(new Coordinate[]{
+                new Coordinate(min.x, min.y),
+                new Coordinate(min.x, max.y),
+                new Coordinate(max.x, max.y),
+                new Coordinate(max.x, min.y),
+                new Coordinate(min.x, min.y)
+        });
+        var bufferedEnvelope = targetEnvelope.buffer(buffer);
+
+        // Warp the raster to the requested extent
+        var rasterOptions = new WarpOptions(new Vector<>(List.of(
+                "-of", "MEM",
+                "-te", Double.toString(bufferedEnvelope.getEnvelopeInternal().getMinX()), Double.toString(bufferedEnvelope.getEnvelopeInternal().getMinY()),
+                Double.toString(bufferedEnvelope.getEnvelopeInternal().getMaxX()), Double.toString(bufferedEnvelope.getEnvelopeInternal().getMaxY()),
+                "-te_srs", "EPSG:3857")));
+        var rasterDataset = gdal.Warp("", new Dataset[]{dataset}, rasterOptions);
+        var rasterBand = rasterDataset.GetRasterBand(1);
+
+        // Generate the contours
+        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);
+
+
+        String levels = IntStream.range(1, 1000)
+                .mapToObj(i -> i * 10)
+                .filter(l -> {
+                    if (tile.z() <= 9) {
+                        return l % 1000 == 0;
+                    } else if (tile.z() <= 10) {
+                        return l % 800 == 0;
+                    } else if (tile.z() <= 11) {
+                        return l % 400 == 0;
+                    } else if (tile.z() <= 12) {
+                        return l % 200 == 0;
+                    } else if (tile.z() <= 13) {
+                        return l % 100 == 0;
+                    } else if (tile.z() <= 14) {
+                        return l % 50 == 0;
+                    } else {
+                        return l % 10 == 0;
+                    }
+                })
+                .map(Object::toString)
+                .collect(Collectors.joining(","));
+
+        gdal.ContourGenerateEx(rasterBand, vectorLayer, new Vector<>(List.of("FIXED_LEVELS=" + levels)));
+
+        // return the contours
+        var geometries = LongStream.range(0, vectorLayer.GetFeatureCount())
+                .mapToObj(vectorLayer::GetFeature)
+                .map(feature -> feature.GetGeometryRef())
+                .map(geometry -> GeometryUtils.deserialize(geometry.ExportToWkb()))
+                .map(targetEnvelope::intersection)
+                .toList();
+
+        var features = geometries.stream()
+                .map(geometry -> VectorTileFunctions.asVectorTileGeom(geometry, targetEnvelope.getEnvelopeInternal(), 4096, 0,
+                        true))
+                .filter(geometry -> geometry.getCoordinates().length >= 2)
+                .map(geometry -> new Feature(null, Map.of(), geometry))
+                .toList();
+
+        var vectorTile = VectorTileFunctions
+                .asVectorTile(new VectorTile(List.of(new Layer("contours", 4096, features))));
+
+        rasterBand.delete();
+        rasterDataset.delete();
+        sourceBand.delete();
+
+        return vectorTile;
+    }
+
+    @Override
+    public void write(TileCoord tile, ByteBuffer blob) throws TileStoreException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void delete(TileCoord tile) throws TileStoreException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void close() throws Exception {
+        datasets.values().forEach(Dataset::delete);
+    }
+
+    public static void main(String[] args) throws Exception {
+        var store = new ContourTileStore();
+        store.read(new TileCoord(8492, 5792, 14).parent());
+    }
 
 }
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileCoord.java b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileCoord.java
index 1953e634..c08505e7 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileCoord.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileCoord.java
@@ -203,8 +203,8 @@ public final class TileCoord implements Comparable<TileCoord> {
     return new Envelope(x1, x2, y1, y2);
   }
 
-  public static double tile2lon(int x, int z) {
-    return x / Math.pow(2.0, z) * 360.0 - 180;
+  public static double tile2lon(double x, double z) {
+    return x / Math.pow(2.0, z) * 360.0 - 180.0;
   }
 
   public static double tile2lat(int y, int z) {
diff --git a/baremaps-core/src/test/java/org/apache/baremaps/database/calcite/CalciteTest.java b/baremaps-core/src/test/java/org/apache/baremaps/database/calcite/CalciteTest.java
index ed22aee5..ea32f16b 100644
--- a/baremaps-core/src/test/java/org/apache/baremaps/database/calcite/CalciteTest.java
+++ b/baremaps-core/src/test/java/org/apache/baremaps/database/calcite/CalciteTest.java
@@ -44,7 +44,6 @@ import org.locationtech.jts.geom.*;
 
 public class CalciteTest {
 
-
   @Test
   public void test() throws SQLException {
     GeometryFactory geometryFactory = new GeometryFactory();
diff --git a/baremaps-server/src/main/java/org/apache/baremaps/server/TilesetResource.java b/baremaps-server/src/main/java/org/apache/baremaps/server/TilesetResource.java
index d2434b84..2ebcb816 100644
--- a/baremaps-server/src/main/java/org/apache/baremaps/server/TilesetResource.java
+++ b/baremaps-server/src/main/java/org/apache/baremaps/server/TilesetResource.java
@@ -17,13 +17,14 @@
 
 package org.apache.baremaps.server;
 
-import java.util.function.Supplier;
+import org.apache.baremaps.vectortile.tileset.Tileset;
+
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import javax.ws.rs.GET;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
-import org.apache.baremaps.vectortile.tileset.Tileset;
+import java.util.function.Supplier;
 
 /**
  * A resource that provides access to the tileset file. Only suitable for development purposes, as
diff --git a/examples/contour/dem.xml b/examples/contour/dem.xml
index 178ca466..ac8b8e1d 100644
--- a/examples/contour/dem.xml
+++ b/examples/contour/dem.xml
@@ -11,7 +11,7 @@
         <UpperLeftY>20037508.34</UpperLeftY>
         <LowerRightX>20037508.34</LowerRightX>
         <LowerRightY>-20037508.34</LowerRightY>
-        <TileLevel>14</TileLevel>
+        <TileLevel>0</TileLevel>
         <TileCountX>1</TileCountX>
         <TileCountY>1</TileCountY>
         <YOrigin>top</YOrigin>


[incubator-baremaps] 08/10: Improve levels and config

Posted by bc...@apache.org.
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 03e6542be19d7dd8fe3a05994d567343047ad186
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Tue Sep 26 22:51:22 2023 +0200

    Improve levels and config
---
 .../apache/baremaps/raster/ContourTileStore.java   | 103 +++++++++++++++------
 1 file changed, 74 insertions(+), 29 deletions(-)

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 2aead3c5..62d95ce8 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
@@ -27,7 +27,11 @@ import org.gdal.gdalconst.gdalconstConstants;
 import org.gdal.ogr.ogr;
 import org.gdal.osr.SpatialReference;
 import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.simplify.DouglasPeuckerSimplifier;
+import org.locationtech.jts.simplify.TopologyPreservingSimplifier;
 import org.locationtech.proj4j.ProjCoordinate;
 
 import java.io.IOException;
@@ -49,19 +53,42 @@ public class ContourTileStore implements TileStore, AutoCloseable {
         ogr.RegisterAll();
     }
 
+    private final String dem = """
+            <GDAL_WMS>
+                <Service name="TMS">
+                    <ServerUrl>https://s3.amazonaws.com/elevation-tiles-prod/geotiff/${z}/${x}/${y}.tif</ServerUrl>
+                </Service>
+                <DataWindow>
+                    <UpperLeftX>-20037508.34</UpperLeftX>
+                    <UpperLeftY>20037508.34</UpperLeftY>
+                    <LowerRightX>20037508.34</LowerRightX>
+                    <LowerRightY>-20037508.34</LowerRightY>
+                    <TileLevel>0</TileLevel>
+                    <TileCountX>1</TileCountX>
+                    <TileCountY>1</TileCountY>
+                    <YOrigin>top</YOrigin>
+                </DataWindow>
+                <Projection>EPSG:3857</Projection>
+                <BlockSizeX>512</BlockSizeX>
+                <BlockSizeY>512</BlockSizeY>
+                <BandsCount>1</BandsCount>
+                <DataType>Int16</DataType>
+                <ZeroBlockHttpCodes>403,404</ZeroBlockHttpCodes>
+                <DataValues>
+                    <NoData>-32768</NoData>
+                </DataValues>
+                <Cache/>
+            </GDAL_WMS>
+            """;
+
     private final Map<Integer, Dataset> datasets = new HashMap<>();
 
     public ContourTileStore() {
     }
 
     public Dataset initDataset(Integer zoom) {
-        try {
-            var dem = Files.readString(Paths.get("dem.xml"));
-            dem = dem.replace("<TileLevel>0</TileLevel>", "<TileLevel>" + zoom + "</TileLevel>");
-            return gdal.Open(dem, gdalconstConstants.GA_ReadOnly);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        var dem = this.dem.replace("<TileLevel>0</TileLevel>", "<TileLevel>" + zoom + "</TileLevel>");
+        return gdal.Open(dem, gdalconstConstants.GA_ReadOnly);
     }
 
 
@@ -102,51 +129,69 @@ public class ContourTileStore implements TileStore, AutoCloseable {
         var srs = new SpatialReference(wkt);
         var vectorDriver = ogr.GetDriverByName("Memory");
         var vectorDataSource = vectorDriver.CreateDataSource("vector");
-        var vectorLayer = vectorDataSource.CreateLayer("vector", srs, ogr.wkbLineString);
+        var vectorLayer = vectorDataSource.CreateLayer("vector", srs, ogr.wkbLineString, new Vector(List.of("ADVERTIZE_UTF8=YES")));
 
+        vectorLayer.CreateField(new org.gdal.ogr.FieldDefn("elevation", ogr.OFTReal));
 
         String levels = IntStream.range(1, 1000)
                 .mapToObj(i -> i * 10)
                 .filter(l -> {
-                    if (tile.z() <= 9) {
-                        return l % 1000 == 0;
-                    } else if (tile.z() <= 10) {
+                    if (tile.z() <= 4) {
+                        return l == 1000 || l == 3000 || l == 5000 || l == 7000 || l == 9000;
+                    } else if (tile.z() <= 8) {
                         return l % 800 == 0;
-                    } else if (tile.z() <= 11) {
+                    } else if (tile.z() == 9) {
+                        return l % 400 == 0;
+                    } else if (tile.z() == 10) {
                         return l % 400 == 0;
-                    } else if (tile.z() <= 12) {
+                    } else if (tile.z() == 11) {
                         return l % 200 == 0;
-                    } else if (tile.z() <= 13) {
+                    } else if (tile.z() == 12) {
+                        return l % 200 == 0;
+                    } else if (tile.z() == 13) {
                         return l % 100 == 0;
-                    } else if (tile.z() <= 14) {
+                    } else if (tile.z() == 14) {
                         return l % 50 == 0;
                     } else {
-                        return l % 10 == 0;
+                        return false;
                     }
                 })
                 .map(Object::toString)
                 .collect(Collectors.joining(","));
 
-        gdal.ContourGenerateEx(rasterBand, vectorLayer, new Vector<>(List.of("FIXED_LEVELS=" + levels)));
+        gdal.ContourGenerateEx(rasterBand, vectorLayer, new Vector<>(List.of("ELEV_FIELD=elevation", "FIXED_LEVELS=" + levels)));
 
         // return the contours
-        var geometries = LongStream.range(0, vectorLayer.GetFeatureCount())
-                .mapToObj(vectorLayer::GetFeature)
-                .map(feature -> feature.GetGeometryRef())
-                .map(geometry -> GeometryUtils.deserialize(geometry.ExportToWkb()))
-                .map(targetEnvelope::intersection)
-                .toList();
-
-        var features = geometries.stream()
-                .map(geometry -> VectorTileFunctions.asVectorTileGeom(geometry, targetEnvelope.getEnvelopeInternal(), 4096, 0,
-                        true))
-                .filter(geometry -> geometry.getCoordinates().length >= 2)
-                .map(geometry -> new Feature(null, Map.of(), geometry))
+        var featureCount = vectorLayer.GetFeatureCount();
+        var features = LongStream.range(0, featureCount).mapToObj(featureIndex -> {
+                    var feature = vectorLayer.GetFeature(featureIndex);
+                    var id = feature.GetFID();
+                    var properties = new HashMap<String, Object>();
+                    var fieldCount = feature.GetFieldCount();
+                    for (int i = 0; i < fieldCount; i++) {
+                        var field = feature.GetFieldDefnRef(i);
+                        var name = field.GetName();
+                        var value = feature.GetFieldAsString(name);
+                        properties.put(name, value);
+                        field.delete();
+                    }
+                    var ref = feature.GetGeometryRef();
+                    var wkb = ref.ExportToWkb();
+                    var geometry = GeometryUtils.deserialize(wkb);
+                    var tileGeometry = targetEnvelope.intersection(geometry);
+                    var mvtGeometry = VectorTileFunctions
+                            .asVectorTileGeom(tileGeometry, targetEnvelope.getEnvelopeInternal(), 4096, 0, true);
+
+                    feature.delete();
+                    return new Feature(id, properties, mvtGeometry);
+                })
+                .filter(feature -> feature.getGeometry().getCoordinates().length >= 2)
                 .toList();
 
         var vectorTile = VectorTileFunctions
                 .asVectorTile(new VectorTile(List.of(new Layer("contours", 4096, features))));
 
+        vectorLayer.delete();
         rasterBand.delete();
         rasterDataset.delete();
         sourceBand.delete();


[incubator-baremaps] 06/10: Some fixes

Posted by bc...@apache.org.
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 0fb7a99aef8598e4bc3bc47503c22e95e5f12fe1
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Mon May 22 17:06:11 2023 +0200

    Some fixes
---
 baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Contour.java | 1 +
 1 file changed, 1 insertion(+)

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
index de4b9e94..c2505c4f 100644
--- 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
@@ -77,6 +77,7 @@ public class Contour implements Callable<Integer> {
     logger.info("Listening on {}", serverContext.listenAddress());
 
     serverContext.awaitShutdown();
+
     return 0;
   }
 }


[incubator-baremaps] 10/10: Improve contour generation

Posted by bc...@apache.org.
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 0ba2afb6334db414faaa1c8bbc17f218746a2e73
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Sun Oct 8 16:42:11 2023 +0200

    Improve contour generation
---
 baremaps-core/pom.xml.versionsBackup               | 177 +++++++++++
 .../apache/baremaps/raster/ContourTileStore.java   | 328 ++++++++++++++-------
 .../baremaps/vectortile/VectorTileEncoder.java     |   4 +-
 basemap/config.js                                  |   6 +-
 examples/contour/dem.xml                           |  29 --
 examples/contour/style.json                        |  33 +--
 examples/contour/tileset.json                      |  11 +-
 7 files changed, 430 insertions(+), 158 deletions(-)

diff --git a/baremaps-core/pom.xml.versionsBackup b/baremaps-core/pom.xml.versionsBackup
new file mode 100644
index 00000000..fe924eec
--- /dev/null
+++ b/baremaps-core/pom.xml.versionsBackup
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to you 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.baremaps</groupId>
+    <artifactId>baremaps</artifactId>
+    <version>0.7.2-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>baremaps-core</artifactId>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.dataformat</groupId>
+      <artifactId>jackson-dataformat-csv</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.dataformat</groupId>
+      <artifactId>jackson-dataformat-yaml</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.datatype</groupId>
+      <artifactId>jackson-datatype-jdk8</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.github.ben-manes.caffeine</groupId>
+      <artifactId>caffeine</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.github.jsqlparser</groupId>
+      <artifactId>jsqlparser</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.google.protobuf</groupId>
+      <artifactId>protobuf-java</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.zaxxer</groupId>
+      <artifactId>HikariCP</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>de.bytefish</groupId>
+      <artifactId>pgbulkinsert</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>it.unimi.dsi</groupId>
+      <artifactId>fastutil</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>mil.nga.geopackage</groupId>
+      <artifactId>geopackage</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>net.ripe.ipresource</groupId>
+      <artifactId>ipresource</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-compress</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-expressions</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-queryparser</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-spatial-extras</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.graalvm.js</groupId>
+      <artifactId>js</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.graalvm.sdk</groupId>
+      <artifactId>graal-sdk</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.locationtech.jts</groupId>
+      <artifactId>jts-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.locationtech.proj4j</groupId>
+      <artifactId>proj4j</artifactId>
+    </dependency>
+    <!-- TODO: Remove this dependency due to license incompatibility -->
+    <dependency>
+      <groupId>org.locationtech.proj4j</groupId>
+      <artifactId>proj4j-epsg</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.postgresql</groupId>
+      <artifactId>postgresql</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.wololo</groupId>
+      <artifactId>flatgeobuf</artifactId>
+      <version>3.24.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.xerial</groupId>
+      <artifactId>sqlite-jdbc</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.xolstice.maven.plugins</groupId>
+        <artifactId>protobuf-maven-plugin</artifactId>
+        <version>${version.plugin.protobuf-maven-plugin}</version>
+        <extensions>true</extensions>
+        <executions>
+          <execution>
+            <goals>
+              <goal>compile</goal>
+              <goal>test-compile</goal>
+            </goals>
+            <configuration>
+              <protocArtifact>com.google.protobuf:protoc:3.19.3:exe:${os.detected.classifier}</protocArtifact>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+    <extensions>
+      <extension>
+        <groupId>kr.motd.maven</groupId>
+        <artifactId>os-maven-plugin</artifactId>
+        <version>${version.plugin.os-maven-plugin}</version>
+      </extension>
+    </extensions>
+  </build>
+</project>
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 62d95ce8..bb5e98d0 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
@@ -24,36 +24,30 @@ 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.Coordinate;
-import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
-import org.locationtech.jts.geom.LineString;
-import org.locationtech.jts.simplify.DouglasPeuckerSimplifier;
+import org.locationtech.jts.geom.*;
 import org.locationtech.jts.simplify.TopologyPreservingSimplifier;
 import org.locationtech.proj4j.ProjCoordinate;
 
-import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Vector;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
-import java.util.stream.LongStream;
 
 public class ContourTileStore implements TileStore, AutoCloseable {
 
     static {
+        // Register the gdal and ogr drivers
         gdal.AllRegister();
         ogr.RegisterAll();
     }
 
-    private final String dem = """
+    /**
+     * The definition of the digital elevation model.
+     */
+    private static final String DEM = """
             <GDAL_WMS>
                 <Service name="TMS">
                     <ServerUrl>https://s3.amazonaws.com/elevation-tiles-prod/geotiff/${z}/${x}/${y}.tif</ServerUrl>
@@ -81,122 +75,247 @@ public class ContourTileStore implements TileStore, AutoCloseable {
             </GDAL_WMS>
             """;
 
+
+    private static final Map<Integer, String> CONTOUR_LEVELS_BY_ZOOM_LEVELS_IN_METERS = IntStream.range(0, 20).mapToObj(zoomLevel -> {
+        var contourLevels = IntStream.range(1, 10000)
+                .mapToObj(i -> (double) i)
+                .filter(l -> {
+                    if (zoomLevel <= 8) {
+                        return l % 1000 == 0;
+                    } else if (zoomLevel == 9) {
+                        return l % 500 == 0;
+                    } else if (zoomLevel == 10) {
+                        return l % 200 == 0;
+                    } else if (zoomLevel == 11) {
+                        return l % 100 == 0;
+                    } else if (zoomLevel == 12) {
+                        return l % 50 == 0;
+                    } else if (zoomLevel == 13) {
+                        return l % 20 == 0;
+                    } else if (zoomLevel == 14) {
+                        return l % 10 == 0;
+                    } else {
+                        return false;
+                    }
+                })
+                .map(Object::toString)
+                .collect(Collectors.joining(","));
+        return Map.entry(zoomLevel, contourLevels);
+    }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+    private static final Map<Integer, String> CONTOUR_LEVELS_BY_ZOOM_LEVELS_IN_FEETS = IntStream.range(0, 20).mapToObj(zoomLevel -> {
+        var contourLevels = IntStream.range(1, 30000)
+                .mapToObj(i -> (double) i)
+                .filter(l -> {
+                    if (zoomLevel <= 9) {
+                        return l % 1000 == 0;
+                    } else if (zoomLevel == 10) {
+                        return l % 500 == 0;
+                    } else if (zoomLevel == 11) {
+                        return l % 200 == 0;
+                    } else if (zoomLevel == 12) {
+                        return l % 100 == 0;
+                    } else if (zoomLevel == 13) {
+                        return l % 50 == 0;
+                    } else if (zoomLevel == 14) {
+                        return l % 20 == 0;
+                    } else {
+                        return false;
+                    }
+                })
+                .map(l -> l * 0.3048)
+                .map(Object::toString)
+                .collect(Collectors.joining(","));
+        return Map.entry(zoomLevel, contourLevels);
+    }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
     private final Map<Integer, Dataset> datasets = new HashMap<>();
 
     public ContourTileStore() {
     }
 
-    public Dataset initDataset(Integer zoom) {
-        var dem = this.dem.replace("<TileLevel>0</TileLevel>", "<TileLevel>" + zoom + "</TileLevel>");
+    public Dataset initRasterDatasetAtZoomLevel(Integer zoom) {
+        var dem = DEM.replace("<TileLevel>0</TileLevel>", "<TileLevel>" + zoom + "</TileLevel>");
         return gdal.Open(dem, gdalconstConstants.GA_ReadOnly);
     }
 
-
     @Override
-    public synchronized ByteBuffer read(TileCoord tile) throws TileStoreException {
-        var dataset = datasets.computeIfAbsent(tile.z(), this::initDataset);
-        var sourceBand = dataset.GetRasterBand(1);
-        var envelope = tile.envelope();
-
-        // Transform the extent to the source projection
+    public ByteBuffer read(TileCoord tile) throws TileStoreException {
+        // Transform the tile envelope to the raster projection
+        var tileEnvelope = tile.envelope();
         var transformer = GeometryUtils.coordinateTransform(4326, 3857);
-        var min = transformer.transform(new ProjCoordinate(envelope.getMinX(), envelope.getMinY()),
+        var rasterMin = transformer.transform(
+                new ProjCoordinate(tileEnvelope.getMinX(), tileEnvelope.getMinY()),
                 new ProjCoordinate());
-        var max = transformer.transform(new ProjCoordinate(envelope.getMaxX(), envelope.getMaxY()),
+        var rasterMax = transformer.transform(
+                new ProjCoordinate(tileEnvelope.getMaxX(), tileEnvelope.getMaxY()),
                 new ProjCoordinate());
-        var buffer = (max.x - min.x) / 4096 * 16;
-
-        var targetEnvelope = new GeometryFactory().createPolygon(new Coordinate[]{
-                new Coordinate(min.x, min.y),
-                new Coordinate(min.x, max.y),
-                new Coordinate(max.x, max.y),
-                new Coordinate(max.x, min.y),
-                new Coordinate(min.x, min.y)
+        var rasterBuffer = (rasterMax.x - rasterMin.x) / 4096 * 128;
+        var rasterEnvelope = new GeometryFactory().createPolygon(new Coordinate[]{
+                new Coordinate(rasterMin.x, rasterMin.y),
+                new Coordinate(rasterMin.x, rasterMax.y),
+                new Coordinate(rasterMax.x, rasterMax.y),
+                new Coordinate(rasterMax.x, rasterMin.y),
+                new Coordinate(rasterMin.x, rasterMin.y)
         });
-        var bufferedEnvelope = targetEnvelope.buffer(buffer);
+        var rasterEnvelopeWithBuffer = rasterEnvelope.buffer(rasterBuffer);
 
-        // Warp the raster to the requested extent
-        var rasterOptions = new WarpOptions(new Vector<>(List.of(
+        // Load the raster data for the tile into the memory
+        // The cache used by gdal is not thread safe, so we need to synchronize the access to the dataset
+        // Otherwise, some tiles would be corrupted
+        Dataset rasterDataset1;
+        synchronized (this) {
+            var dataset = datasets.computeIfAbsent(tile.z(), this::initRasterDatasetAtZoomLevel);
+            var rasterOptions1 = new WarpOptions(new Vector<>(List.of(
+                    "-of", "MEM",
+                    "-te",
+                    Double.toString(rasterEnvelopeWithBuffer.getEnvelopeInternal().getMinX()),
+                    Double.toString(rasterEnvelopeWithBuffer.getEnvelopeInternal().getMinY()),
+                    Double.toString(rasterEnvelopeWithBuffer.getEnvelopeInternal().getMaxX()),
+                    Double.toString(rasterEnvelopeWithBuffer.getEnvelopeInternal().getMaxY()),
+                    "-te_srs", "EPSG:3857")));
+            rasterDataset1 = gdal.Warp("", new Dataset[]{dataset}, rasterOptions1);
+        }
+        
+        // Reduce the resolution of the raster by a factor of 4 to remove artifacts
+        var rasterOptions2 = new WarpOptions(new Vector<>(List.of(
                 "-of", "MEM",
-                "-te", Double.toString(bufferedEnvelope.getEnvelopeInternal().getMinX()), Double.toString(bufferedEnvelope.getEnvelopeInternal().getMinY()),
-                Double.toString(bufferedEnvelope.getEnvelopeInternal().getMaxX()), Double.toString(bufferedEnvelope.getEnvelopeInternal().getMaxY()),
-                "-te_srs", "EPSG:3857")));
-        var rasterDataset = gdal.Warp("", new Dataset[]{dataset}, rasterOptions);
-        var rasterBand = rasterDataset.GetRasterBand(1);
+                "-ts",
+                String.valueOf(rasterDataset1.getRasterXSize() / 4),
+                String.valueOf(rasterDataset1.getRasterYSize() / 4),
+                "-r", "cubicspline")));
+        var rasterDataset2 = gdal.Warp("", new Dataset[]{rasterDataset1}, rasterOptions2);
 
-        // Generate the contours
-        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, new Vector(List.of("ADVERTIZE_UTF8=YES")));
+        // Increase the resolution of the raster by a factor of 2 to smooth the contours
+        var rasterOptions3 = new WarpOptions(new Vector<>(List.of(
+                "-of", "MEM",
+                "-ts", String.valueOf(rasterDataset1.getRasterXSize() * 2), String.valueOf(rasterDataset1.getRasterYSize() * 2),
+                "-r", "cubicspline")));
+        var rasterDataset3 = gdal.Warp("", new Dataset[]{rasterDataset2}, rasterOptions3);
 
-        vectorLayer.CreateField(new org.gdal.ogr.FieldDefn("elevation", ogr.OFTReal));
+        // Generate the contours in meters
+        var contourLevelsInMeters = CONTOUR_LEVELS_BY_ZOOM_LEVELS_IN_METERS.get(tile.z());
+        var featuresInMeters = generateContours(rasterDataset3, rasterEnvelope, rasterEnvelopeWithBuffer, contourLevelsInMeters);
 
-        String levels = IntStream.range(1, 1000)
-                .mapToObj(i -> i * 10)
-                .filter(l -> {
-                    if (tile.z() <= 4) {
-                        return l == 1000 || l == 3000 || l == 5000 || l == 7000 || l == 9000;
-                    } else if (tile.z() <= 8) {
-                        return l % 800 == 0;
-                    } else if (tile.z() == 9) {
-                        return l % 400 == 0;
-                    } else if (tile.z() == 10) {
-                        return l % 400 == 0;
-                    } else if (tile.z() == 11) {
-                        return l % 200 == 0;
-                    } else if (tile.z() == 12) {
-                        return l % 200 == 0;
-                    } else if (tile.z() == 13) {
-                        return l % 100 == 0;
-                    } else if (tile.z() == 14) {
-                        return l % 50 == 0;
-                    } else {
-                        return false;
-                    }
-                })
-                .map(Object::toString)
-                .collect(Collectors.joining(","));
+        // Generate the contours in feets
+        var contourLevelsInFeets = CONTOUR_LEVELS_BY_ZOOM_LEVELS_IN_FEETS.get(tile.z());
+        var featuresInFeets = generateContours(rasterDataset3, rasterEnvelope, rasterEnvelopeWithBuffer, contourLevelsInFeets);
+
+        // Release the resources
+        rasterDataset1.delete();
+        rasterDataset2.delete();
+        rasterDataset3.delete();
 
-        gdal.ContourGenerateEx(rasterBand, vectorLayer, new Vector<>(List.of("ELEV_FIELD=elevation", "FIXED_LEVELS=" + levels)));
+        // Create the vector tile
+        return VectorTileFunctions
+                .asVectorTile(new VectorTile(List.of(
+                        new Layer("contours_m", 4096, featuresInMeters),
+                        new Layer("contours_ft", 4096, featuresInFeets))));
+    }
+
+
+    public List<Feature> generateContours(Dataset rasterDataset, Geometry rasterEnvelope, Geometry rasterEnvelopeWithBuffer, String contourLevels) {
+        // Initialize the vector dataset and layer to store the contours
+        var vectorProjection = rasterDataset.GetProjection();
+        var vectorSpatialReferenceSystem = new SpatialReference(vectorProjection);
+        var vectorMemoryDriver = ogr.GetDriverByName("Memory");
+        var vectorDataSource = vectorMemoryDriver.CreateDataSource("vector");
+        var vectorLayer = vectorDataSource.CreateLayer("vector", vectorSpatialReferenceSystem, ogr.wkbLineString, new Vector(List.of("ADVERTIZE_UTF8=YES")));
+        vectorLayer.CreateField(new FieldDefn("elevation", ogr.OFTReal));
+
+        // Get the raster band to generate the contours from
+        var rasterBand = rasterDataset.GetRasterBand(1);
+
+        // Generate the contours and store them in the vector layer
+        gdal.ContourGenerateEx(rasterBand, vectorLayer, new Vector<>(List.of("ELEV_FIELD=elevation", "FIXED_LEVELS=" + contourLevels)));
 
         // return the contours
-        var featureCount = vectorLayer.GetFeatureCount();
-        var features = LongStream.range(0, featureCount).mapToObj(featureIndex -> {
-                    var feature = vectorLayer.GetFeature(featureIndex);
-                    var id = feature.GetFID();
-                    var properties = new HashMap<String, Object>();
-                    var fieldCount = feature.GetFieldCount();
-                    for (int i = 0; i < fieldCount; i++) {
-                        var field = feature.GetFieldDefnRef(i);
-                        var name = field.GetName();
-                        var value = feature.GetFieldAsString(name);
-                        properties.put(name, value);
-                        field.delete();
-                    }
-                    var ref = feature.GetGeometryRef();
-                    var wkb = ref.ExportToWkb();
-                    var geometry = GeometryUtils.deserialize(wkb);
-                    var tileGeometry = targetEnvelope.intersection(geometry);
-                    var mvtGeometry = VectorTileFunctions
-                            .asVectorTileGeom(tileGeometry, targetEnvelope.getEnvelopeInternal(), 4096, 0, true);
-
-                    feature.delete();
-                    return new Feature(id, properties, mvtGeometry);
-                })
-                .filter(feature -> feature.getGeometry().getCoordinates().length >= 2)
-                .toList();
+        var features = new ArrayList<Feature>();
+        for (var i = 0; i < vectorLayer.GetFeatureCount(); i++) {
+            var vectorFeature = vectorLayer.GetFeature(i);
 
-        var vectorTile = VectorTileFunctions
-                .asVectorTile(new VectorTile(List.of(new Layer("contours", 4096, features))));
+            // Get the feature id
+            var id = vectorFeature.GetFID();
 
+            // Get the feature properties
+            var properties = new HashMap<String, Object>();
+            for (int j = 0; j < vectorFeature.GetFieldCount(); j++) {
+                var field = vectorFeature.GetFieldDefnRef(j);
+                var name = field.GetName();
+                var value = vectorFeature.GetFieldAsString(name);
+
+                // Parse the elevation
+                if (name.equals("elevation")) {
+                    var elevationInMeters = Double.parseDouble(value);
+                    var elevationInFeet = Math.round(elevationInMeters * 3.28084 * 100) / 100.0;
+                    properties.put("elevation_m", elevationInMeters);
+                    properties.put("elevation_ft", elevationInFeet);
+                }
+
+                // Release the field resources
+                field.delete();
+            }
+
+            // Get the wkb geometry
+            var vectorGeometry = vectorFeature.GetGeometryRef();
+            var wkb = vectorGeometry.ExportToWkb();
+
+            // Release the geometry and feature resources
+            vectorGeometry.delete();
+            vectorFeature.delete();
+
+            // Deserialize the wkb geometry and clip it to the raster envelope with buffer
+            var geometry = GeometryUtils.deserialize(wkb);
+            geometry = rasterEnvelopeWithBuffer.intersection(geometry);
+
+            // Create the vector tile geometry with the correct buffer
+            var mvtGeometry = VectorTileFunctions
+                    .asVectorTileGeom(geometry, rasterEnvelope.getEnvelopeInternal(), 4096, 128, true);
+
+            // The following code is used to simplify the geometry at the tile boundaries,
+            // which is necessary to prevent artifacts from appearing at the intersections
+            // of the vector tiles.
+
+            // Create the vector tile envelope to simplify the geometry at the tile boundaries
+            var mvtEnvelope = new GeometryFactory().createPolygon(new Coordinate[]{
+                    new Coordinate(0, 0),
+                    new Coordinate(0, 4096),
+                    new Coordinate(4096, 4096),
+                    new Coordinate(4096, 0),
+                    new Coordinate(0, 0)
+            });
+
+            // The tolerance to use when simplifying the geometry at the tile boundaries.
+            // This value is a good balance between preserving the accuracy of the geometry
+            // and minimizing the size of the vector tiles.
+            var tolerance = 10;
+
+            // Simplify the inside of the vector tile at the tile boundaries
+            var insideGeom = mvtGeometry.intersection(mvtEnvelope);
+            var insideSimplifier = new TopologyPreservingSimplifier(insideGeom);
+            insideSimplifier.setDistanceTolerance(tolerance);
+            insideGeom = insideSimplifier.getResultGeometry();
+
+            // Simplify the outside of the vector tile at the tile boundaries
+            var outsideGeom = mvtGeometry.difference(mvtEnvelope);
+            var outsideSimplifier = new TopologyPreservingSimplifier(outsideGeom);
+            outsideSimplifier.setDistanceTolerance(tolerance);
+            outsideGeom = outsideSimplifier.getResultGeometry();
+
+            // Merge the simplified geometries back together
+            mvtGeometry = insideGeom.union(outsideGeom);
+
+            // Add the feature to the list of features if it is valid and has more than one coordinate
+            if (mvtGeometry.isValid() && mvtGeometry.getCoordinates().length > 1) {
+                features.add(new Feature(id, properties, mvtGeometry));
+            }
+        }
+
+        // Release the resources
         vectorLayer.delete();
         rasterBand.delete();
-        rasterDataset.delete();
-        sourceBand.delete();
 
-        return vectorTile;
+        return features;
     }
 
     @Override
@@ -218,5 +337,4 @@ public class ContourTileStore implements TileStore, AutoCloseable {
         var store = new ContourTileStore();
         store.read(new TileCoord(8492, 5792, 14).parent());
     }
-
 }
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 45a1d6d0..7dc93987 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
@@ -58,7 +58,9 @@ public class VectorTileEncoder {
    */
   public Tile encodeTile(VectorTile tile) {
     Tile.Builder builder = Tile.newBuilder();
-    tile.getLayers().forEach(layer -> builder.addLayers(encodeLayer(layer)));
+    for (var layer : tile.getLayers()) {
+      builder.addLayers(encodeLayer(layer));
+    }
     return builder.build();
   }
 
diff --git a/basemap/config.js b/basemap/config.js
index 9b69b989..62263a13 100644
--- a/basemap/config.js
+++ b/basemap/config.js
@@ -17,8 +17,8 @@
 export default {
     "host": "http://localhost:9000",
     "database": "jdbc:postgresql://localhost:5432/baremaps?&user=baremaps&password=baremaps",
-    "osmPbfUrl": "https://download.geofabrik.de/europe/switzerland-latest.osm.pbf",
-    "center": [6.6323, 46.5197],
-    "bounds": [6.02260949059, 45.7769477403, 10.4427014502, 47.8308275417],
+    "osmPbfUrl": "https://download.geofabrik.de/europe/new-york-latest.osm.pbf",
+    "center": [-74.0060, 40.7128],
+    "bounds": [-180, -85, 180, 85],
     "zoom": 14,
 }
diff --git a/examples/contour/dem.xml b/examples/contour/dem.xml
deleted file mode 100644
index ac8b8e1d..00000000
--- a/examples/contour/dem.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<GDAL_WMS>
-    <!--
-    Generate contour lines for the planet.
-    gdal_contour -a elevation -nln contour -i 10 -f GPKG dem.xml dem.gpkg
-    -->
-    <Service name="TMS">
-        <ServerUrl>https://s3.amazonaws.com/elevation-tiles-prod/geotiff/${z}/${x}/${y}.tif</ServerUrl>
-    </Service>
-    <DataWindow>
-        <UpperLeftX>-20037508.34</UpperLeftX>
-        <UpperLeftY>20037508.34</UpperLeftY>
-        <LowerRightX>20037508.34</LowerRightX>
-        <LowerRightY>-20037508.34</LowerRightY>
-        <TileLevel>0</TileLevel>
-        <TileCountX>1</TileCountX>
-        <TileCountY>1</TileCountY>
-        <YOrigin>top</YOrigin>
-    </DataWindow>
-    <Projection>EPSG:3857</Projection>
-    <BlockSizeX>512</BlockSizeX>
-    <BlockSizeY>512</BlockSizeY>
-    <BandsCount>1</BandsCount>
-    <DataType>Int16</DataType>
-    <ZeroBlockHttpCodes>403,404</ZeroBlockHttpCodes>
-    <DataValues>
-        <NoData>-32768</NoData>
-    </DataValues>
-    <Cache/>
-</GDAL_WMS>
\ No newline at end of file
diff --git a/examples/contour/style.json b/examples/contour/style.json
index 90d99d22..ff7e81d5 100644
--- a/examples/contour/style.json
+++ b/examples/contour/style.json
@@ -4,16 +4,6 @@
     "baremaps" : {
       "type" : "vector",
       "url" : "http://localhost:9000/tiles.json"
-    },
-    "dem": {
-     "type": "raster-dem",
-      "encoding": "terrarium",
-      "tiles": [
-        "https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png"
-      ],
-      "tileSize": 256,
-      "maxzoom": 13,
-      "minzoom": 0
     }
   },
   "layers" : [ {
@@ -23,24 +13,29 @@
     "paint" : {
       "background-color" : "rgba(255, 255, 255, 1)"
     }
-  },{
-    "id": "hills",
-    "type": "hillshade",
-    "source": "dem",
-    "paint": {
-      "hillshade-exaggeration": 0.25
+  }, {
+    "id" : "contours_m",
+    "type" : "line",
+    "source" : "baremaps",
+    "source-layer" : "contours_m",
+    "layout" : {
+      "line-cap" : "round",
+      "line-join" : "round"
+    },
+    "paint" : {
+      "line-color" : "rgba(0, 0, 0, 1)"
     }
   }, {
-    "id" : "contours",
+    "id" : "contours_ft",
     "type" : "line",
     "source" : "baremaps",
-    "source-layer" : "contours",
+    "source-layer" : "contours_ft",
     "layout" : {
       "line-cap" : "round",
       "line-join" : "round"
     },
     "paint" : {
-      "line-color" : "rgba(181, 169, 152, 1)"
+      "line-color" : "rgba(100, 100, 100, 1)"
     }
   } ],
   "center" : [ 9.5554, 47.166 ],
diff --git a/examples/contour/tileset.json b/examples/contour/tileset.json
index 2d729bdb..baebaa18 100644
--- a/examples/contour/tileset.json
+++ b/examples/contour/tileset.json
@@ -11,7 +11,16 @@
   "database": "jdbc:postgresql://localhost:5432/baremaps?&user=baremaps&password=baremaps",
   "vector_layers": [
     {
-      "id": "contours",
+      "id": "contours_m",
+      "queries": [
+        {
+          "minzoom": 0,
+          "maxzoom": 20
+        }
+      ]
+    },
+    {
+      "id": "contours_ft",
       "queries": [
         {
           "minzoom": 0,


[incubator-baremaps] 03/10: Serve contour tiles

Posted by bc...@apache.org.
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 e0c6791e4ba8d4a30d9e3f9a8d86f926c28f37f2
Author: Bertil Chapuis <bc...@gmail.com>
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
+@javax.ws.rs.Path("/")
+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
         }
       ]
     }


[incubator-baremaps] 05/10: Fix issues after rebase

Posted by bc...@apache.org.
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 e4c6b216d62a843b054bed49f6997fc497eb0f26
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Sun May 21 20:10:26 2023 +0200

    Fix issues after rebase
---
 .run/ContourTileStore.run.xml                      | 16 -----
 .../java/org/apache/baremaps/cli/map/Contour.java  |  6 +-
 baremaps-core/pom.xml                              | 44 +++++++-------
 .../java/org/apache/baremaps/raster/AsterDem.java  | 70 ++++++++++++++++++++++
 .../apache/baremaps/raster/ContourTileStore.java   | 13 ++--
 .../java/org/apache/baremaps/raster/Image.java     | 35 +++++++----
 .../vectortile/{Tile.java => VectorTile.java}      |  6 +-
 .../baremaps/vectortile/VectorTileDecoder.java     | 20 +++----
 .../baremaps/vectortile/VectorTileEncoder.java     | 34 +++++------
 .../baremaps/vectortile/VectorTileFunctions.java   |  8 +--
 .../vectortile/VectorTileFunctionsTest.java        |  3 +-
 .../apache/baremaps/vectortile/VectorTileTest.java |  4 +-
 .../baremaps/vectortile/VectorTileViewer.java      |  4 +-
 13 files changed, 164 insertions(+), 99 deletions(-)

diff --git a/.run/ContourTileStore.run.xml b/.run/ContourTileStore.run.xml
deleted file mode 100644
index 307d971d..00000000
--- a/.run/ContourTileStore.run.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="ContourTileStore" type="Application" factoryName="Application" nameIsGenerated="true">
-    <option name="MAIN_CLASS_NAME" value="org.apache.baremaps.raster.ContourTileStore" />
-    <module name="baremaps-core" />
-    <option name="VM_PARAMETERS" value="-Djava.library.path=$PROJECT_DIR$/../../osgeo/gdal/build/swig/java" />
-    <extension name="coverage">
-      <pattern>
-        <option name="PATTERN" value="org.apache.baremaps.raster.*" />
-        <option name="ENABLED" value="true" />
-      </pattern>
-    </extension>
-    <method v="2">
-      <option name="Make" enabled="true" />
-    </method>
-  </configuration>
-</component>
\ No newline at end of file
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
index 055456d5..de4b9e94 100644
--- 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
@@ -13,17 +13,17 @@
 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.apache.baremaps.tilestore.TileStore;
+import org.apache.baremaps.utils.ObjectMapperUtils;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.slf4j.Logger;
@@ -55,7 +55,7 @@ public class Contour implements Callable<Integer> {
 
   @Override
   public Integer call() throws Exception {
-    var objectMapper = defaultObjectMapper();
+    var objectMapper = ObjectMapperUtils.objectMapper();
     var tileStore = new ContourTileStore();
 
     // Configure the application
diff --git a/baremaps-core/pom.xml b/baremaps-core/pom.xml
index e38f2be1..7649671e 100644
--- a/baremaps-core/pom.xml
+++ b/baremaps-core/pom.xml
@@ -25,29 +25,7 @@ limitations under the License.
 
   <artifactId>baremaps-core</artifactId>
 
-  <repositories>
-    <repository>
-      <id>twelvemonkeys</id>
-      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
-    </repository>
-  </repositories>
-
   <dependencies>
-    <dependency>
-      <groupId>com.twelvemonkeys.imageio</groupId>
-      <artifactId>imageio-tiff</artifactId>
-      <version>3.10.0-SNAPSHOT</version>
-    </dependency>
-    <dependency>
-      <groupId>com.twelvemonkeys.imageio</groupId>
-      <artifactId>imageio-metadata</artifactId>
-      <version>3.9.4</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.sis.storage</groupId>
-      <artifactId>sis-geotiff</artifactId>
-      <version>1.3</version>
-    </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
@@ -88,6 +66,16 @@ limitations under the License.
       <groupId>com.google.protobuf</groupId>
       <artifactId>protobuf-java</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.twelvemonkeys.imageio</groupId>
+      <artifactId>imageio-metadata</artifactId>
+      <version>3.9.4</version>
+    </dependency>
+    <dependency>
+      <groupId>com.twelvemonkeys.imageio</groupId>
+      <artifactId>imageio-tiff</artifactId>
+      <version>3.10.0-SNAPSHOT</version>
+    </dependency>
     <dependency>
       <groupId>com.zaxxer</groupId>
       <artifactId>HikariCP</artifactId>
@@ -132,6 +120,11 @@ limitations under the License.
       <groupId>org.apache.lucene</groupId>
       <artifactId>lucene-spatial-extras</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.sis.storage</groupId>
+      <artifactId>sis-geotiff</artifactId>
+      <version>1.3</version>
+    </dependency>
     <dependency>
       <groupId>org.gdal</groupId>
       <artifactId>gdal</artifactId>
@@ -172,6 +165,13 @@ limitations under the License.
     </dependency>
   </dependencies>
 
+  <repositories>
+    <repository>
+      <id>twelvemonkeys</id>
+      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
+    </repository>
+  </repositories>
+
   <build>
     <plugins>
       <plugin>
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/raster/AsterDem.java b/baremaps-core/src/main/java/org/apache/baremaps/raster/AsterDem.java
new file mode 100644
index 00000000..8ce28143
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/raster/AsterDem.java
@@ -0,0 +1,70 @@
+/*
+ * 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.raster;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class AsterDem {
+
+  public static void main(String[] args) throws IOException {
+
+    var target = Paths.get("/Volumes/Data/data/ASTGTMV003");
+    if (Files.notExists(target)) {
+      Files.createDirectories(target);
+    }
+    var baseUrl = "https://e4ftl01.cr.usgs.gov/ASTT/ASTGTM.003/2000.03.01/";
+    var token = "...";
+
+    var directoryUrl = new URL(baseUrl);
+    var directoryConnection = (HttpURLConnection) directoryUrl.openConnection();
+
+    var files = new ArrayList<String>();
+    try (var stream = new BufferedInputStream(directoryConnection.getInputStream())) {
+      var bytes = stream.readAllBytes();
+      var html = new String(bytes);
+      Pattern p = Pattern.compile("href=\"(ASTGTMV003_.*?)\"");
+      Matcher m = p.matcher(html);
+      while (m.find()) {
+        files.add(m.group(1));
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+    files.parallelStream().forEach(file -> {
+      try {
+        if (Files.exists(target.resolve(file))) {
+          return;
+        }
+        System.out.println("Downloading " + file);
+        var fileUrl = new URL(baseUrl + file);
+        var fileConnection = (HttpURLConnection) fileUrl.openConnection();
+        fileConnection.setRequestProperty("Authorization", "Bearer " + token);
+        try (var stream = new BufferedInputStream(fileConnection.getInputStream())) {
+          Files.copy(stream, target.resolve(file), StandardCopyOption.REPLACE_EXISTING);
+        }
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    });
+  }
+}
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 bce42cd5..5eeb5855 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
@@ -24,6 +24,7 @@ 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.VectorTile;
 import org.apache.baremaps.vectortile.VectorTileFunctions;
 import org.gdal.gdal.Dataset;
 import org.gdal.gdal.WarpOptions;
@@ -32,7 +33,6 @@ import org.gdal.gdalconst.gdalconstConstants;
 import org.gdal.ogr.ogr;
 import org.gdal.osr.SpatialReference;
 import org.locationtech.jts.geom.Envelope;
-import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.proj4j.ProjCoordinate;
 
 public class ContourTileStore implements TileStore, AutoCloseable {
@@ -45,7 +45,7 @@ public class ContourTileStore implements TileStore, AutoCloseable {
   private final Dataset sourceDataset;
 
   public ContourTileStore() {
-    var dem = Paths.get("examples/contour/dem.xml").toAbsolutePath().toString();
+    var dem = Paths.get("dem.xml").toAbsolutePath().toString();
     sourceDataset = gdal.Open(dem, gdalconstConstants.GA_ReadOnly);
   }
 
@@ -84,13 +84,13 @@ public class ContourTileStore implements TileStore, AutoCloseable {
         .mapToObj(vectorLayer::GetFeature)
         .map(feature -> feature.GetGeometryRef())
         .map(geometry -> GeometryUtils.deserialize(geometry.ExportToWkb()))
-        .map(geometry -> VectorTileFunctions.asVectorTileGeom(geometry, targetEnvelope, 4096, 0, true))
+        .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 vectorTile = VectorTileFunctions
+        .asVectorTile(new VectorTile(List.of(new Layer("contours", 4096, features))));
 
     rasterBand.delete();
     rasterDataset.delete();
@@ -117,7 +117,6 @@ public class ContourTileStore implements TileStore, AutoCloseable {
   public static void main(String[] args) throws Exception {
     var store = new ContourTileStore();
     store.read(new TileCoord(8492, 5792, 14).parent());
-
   }
 
 }
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/raster/Image.java b/baremaps-core/src/main/java/org/apache/baremaps/raster/Image.java
index 896304be..b26e3a53 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/raster/Image.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/raster/Image.java
@@ -1,22 +1,35 @@
+/*
+ * 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.raster;
 
-import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.net.URL;
+import javax.imageio.ImageIO;
 
 
 public class Image {
 
-    public static void main(String[] args) throws IOException {
-        String urlString = String.format("https://s3.amazonaws.com/elevation-tiles-prod/geotiff/%s/%s/%s.tif", 14, 8492, 5792);
-        URL url = new URL(urlString);
-        BufferedImage tiffImage = ImageIO.read(url);
-        var raster = tiffImage.getRaster();
-        for (int x = 0; x < raster.getWidth(); x++) {
-            for (int y = 0; y < raster.getHeight(); y++) {
-                System.out.println(raster.getSampleFloat(y, y, 0));
-            }
-        }
+  public static void main(String[] args) throws IOException {
+    String urlString = String.format(
+        "https://s3.amazonaws.com/elevation-tiles-prod/geotiff/%s/%s/%s.tif", 14, 8492, 5792);
+    URL url = new URL(urlString);
+    BufferedImage tiffImage = ImageIO.read(url);
+    var raster = tiffImage.getRaster();
+    for (int x = 0; x < raster.getWidth(); x++) {
+      for (int y = 0; y < raster.getHeight(); y++) {
+        System.out.println(raster.getSampleFloat(y, y, 0));
+      }
     }
+  }
 }
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/Tile.java b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTile.java
similarity index 93%
rename from baremaps-core/src/main/java/org/apache/baremaps/vectortile/Tile.java
rename to baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTile.java
index 12ea826d..bb37b33c 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/Tile.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTile.java
@@ -23,14 +23,14 @@ import java.util.List;
 /**
  * A vector tile layer.
  */
-public class Tile {
+public class VectorTile {
 
   private List<Layer> layers;
 
   /**
    * Creates a new tile.
    */
-  public Tile(List<Layer> layers) {
+  public VectorTile(List<Layer> layers) {
     this.layers = layers;
   }
 
@@ -58,7 +58,7 @@ public class Tile {
       return true;
     if (o == null || getClass() != o.getClass())
       return false;
-    Tile tile = (Tile) o;
+    VectorTile tile = (VectorTile) o;
     return Objects.equal(layers, tile.layers);
   }
 
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileDecoder.java b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileDecoder.java
index c76f502c..52f55892 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileDecoder.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/VectorTileDecoder.java
@@ -24,7 +24,7 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.*;
 import java.util.stream.Collectors;
-import org.apache.baremaps.mvt.binary.VectorTile;
+import org.apache.baremaps.mvt.binary.VectorTile.Tile;
 import org.apache.baremaps.mvt.binary.VectorTile.Tile.GeomType;
 import org.apache.baremaps.mvt.binary.VectorTile.Tile.Value;
 import org.locationtech.jts.geom.*;
@@ -57,9 +57,9 @@ public class VectorTileDecoder {
    * @return The decoded vector tile
    * @throws IOException If an error occurs while decoding the vector tile
    */
-  public Tile decodeTile(ByteBuffer buffer) throws IOException {
+  public VectorTile decodeTile(ByteBuffer buffer) throws IOException {
     try {
-      VectorTile.Tile tile = VectorTile.Tile.parseFrom(buffer);
+      org.apache.baremaps.mvt.binary.VectorTile.Tile tile = Tile.parseFrom(buffer);
       return decodeTile(tile);
     } catch (InvalidProtocolBufferException e) {
       throw new IOException(e);
@@ -72,11 +72,11 @@ public class VectorTileDecoder {
    * @param tile The vector tile to decode
    * @return The decoded vector tile
    */
-  public Tile decodeTile(VectorTile.Tile tile) {
+  public VectorTile decodeTile(Tile tile) {
     List<Layer> layers = tile.getLayersList().stream()
         .map(this::decodeLayer)
         .collect(Collectors.toList());
-    return new Tile(layers);
+    return new VectorTile(layers);
   }
 
   /**
@@ -88,7 +88,7 @@ public class VectorTileDecoder {
    */
   public Layer decodeLayer(ByteBuffer buffer) throws IOException {
     try {
-      VectorTile.Tile.Layer layer = VectorTile.Tile.Layer.parseFrom(buffer);
+      Tile.Layer layer = Tile.Layer.parseFrom(buffer);
       return decodeLayer(layer);
     } catch (InvalidProtocolBufferException e) {
       throw new IOException(e);
@@ -101,7 +101,7 @@ public class VectorTileDecoder {
    * @param layer The vector tile layer
    * @return The decoded layer
    */
-  public Layer decodeLayer(VectorTile.Tile.Layer layer) {
+  public Layer decodeLayer(Tile.Layer layer) {
     String name = layer.getName();
     int extent = layer.getExtent();
 
@@ -152,7 +152,7 @@ public class VectorTileDecoder {
    * @param feature The vector tile feature to decode
    * @return The decoded feature
    */
-  protected Feature decodeFeature(VectorTile.Tile.Feature feature) {
+  protected Feature decodeFeature(Tile.Feature feature) {
 
     cx = 0;
     cy = 0;
@@ -169,7 +169,7 @@ public class VectorTileDecoder {
    * @param feature The feature to decode
    * @return The tags of the feature
    */
-  protected Map<String, Object> decodeTags(VectorTile.Tile.Feature feature) {
+  protected Map<String, Object> decodeTags(Tile.Feature feature) {
     Map<String, Object> tags = new HashMap<>();
     List<Integer> encoding = feature.getTagsList();
     for (int i = 0; i < encoding.size(); i += 2) {
@@ -186,7 +186,7 @@ public class VectorTileDecoder {
    * @param feature The vector tile feature
    * @return The decoded geometry
    */
-  protected Geometry decodeGeometry(VectorTile.Tile.Feature feature) {
+  protected Geometry decodeGeometry(Tile.Feature feature) {
     GeomType type = feature.getType();
     List<Integer> encoding = feature.getGeometryList();
     switch (type) {
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 0a679303..45a1d6d0 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
@@ -25,7 +25,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.function.Consumer;
-import org.apache.baremaps.mvt.binary.VectorTile;
+import org.apache.baremaps.mvt.binary.VectorTile.Tile;
 import org.locationtech.jts.geom.*;
 
 /**
@@ -56,8 +56,8 @@ public class VectorTileEncoder {
    * @param tile The tile to encode
    * @return The vector tile
    */
-  public VectorTile.Tile encodeTile(Tile tile) {
-    VectorTile.Tile.Builder builder = VectorTile.Tile.newBuilder();
+  public Tile encodeTile(VectorTile tile) {
+    Tile.Builder builder = Tile.newBuilder();
     tile.getLayers().forEach(layer -> builder.addLayers(encodeLayer(layer)));
     return builder.build();
   }
@@ -68,11 +68,11 @@ public class VectorTileEncoder {
    * @param layer The layer to encode
    * @return The vector tile layer
    */
-  public VectorTile.Tile.Layer encodeLayer(Layer layer) {
+  public Tile.Layer encodeLayer(Layer layer) {
     keys = new ArrayList<>();
     values = new ArrayList<>();
 
-    VectorTile.Tile.Layer.Builder builder = VectorTile.Tile.Layer.newBuilder();
+    Tile.Layer.Builder builder = Tile.Layer.newBuilder();
     builder.setName(layer.getName());
     builder.setVersion(2);
     builder.setExtent(layer.getExtent());
@@ -94,8 +94,8 @@ public class VectorTileEncoder {
    * @param object The object to encode
    * @return The vector tile value
    */
-  protected VectorTile.Tile.Value encodeValue(Object object) {
-    VectorTile.Tile.Value.Builder builder = VectorTile.Tile.Value.newBuilder();
+  protected Tile.Value encodeValue(Object object) {
+    Tile.Value.Builder builder = Tile.Value.newBuilder();
 
     // Encode the value based on its type
     if (object instanceof String value) {
@@ -120,11 +120,11 @@ public class VectorTileEncoder {
    *
    * @param feature The feature to encode.
    */
-  protected void encodeFeature(Feature feature, Consumer<VectorTile.Tile.Feature> consumer) {
+  protected void encodeFeature(Feature feature, Consumer<Tile.Feature> consumer) {
     cx = 0;
     cy = 0;
 
-    VectorTile.Tile.Feature.Builder builder = VectorTile.Tile.Feature.newBuilder();
+    Tile.Feature.Builder builder = Tile.Feature.newBuilder();
     if (feature.getId() != null) {
       builder.setId(feature.getId());
     }
@@ -135,21 +135,21 @@ public class VectorTileEncoder {
     consumer.accept(builder.build());
   }
 
-  protected VectorTile.Tile.GeomType encodeGeometryType(Geometry geometry) {
+  protected Tile.GeomType encodeGeometryType(Geometry geometry) {
     if (geometry instanceof Point) {
-      return VectorTile.Tile.GeomType.POINT;
+      return Tile.GeomType.POINT;
     } else if (geometry instanceof MultiPoint) {
-      return VectorTile.Tile.GeomType.POINT;
+      return Tile.GeomType.POINT;
     } else if (geometry instanceof LineString) {
-      return VectorTile.Tile.GeomType.LINESTRING;
+      return Tile.GeomType.LINESTRING;
     } else if (geometry instanceof MultiLineString) {
-      return VectorTile.Tile.GeomType.LINESTRING;
+      return Tile.GeomType.LINESTRING;
     } else if (geometry instanceof Polygon) {
-      return VectorTile.Tile.GeomType.POLYGON;
+      return Tile.GeomType.POLYGON;
     } else if (geometry instanceof MultiPolygon) {
-      return VectorTile.Tile.GeomType.POLYGON;
+      return Tile.GeomType.POLYGON;
     } else {
-      return VectorTile.Tile.GeomType.UNKNOWN;
+      return Tile.GeomType.UNKNOWN;
     }
   }
 
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 59a45654..7f3dd2d5 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
@@ -92,11 +92,11 @@ public class VectorTileFunctions {
    * @param vectorTile The tile to transform
    * @return The transformed tile
    */
-  public static ByteBuffer asVectorTile(Tile vectorTile) {
+  public static ByteBuffer asVectorTile(VectorTile vectorTile) {
     ByteBuffer original = new VectorTileEncoder()
-            .encodeTile(vectorTile)
-            .toByteString()
-            .asReadOnlyByteBuffer();
+        .encodeTile(vectorTile)
+        .toByteString()
+        .asReadOnlyByteBuffer();
     ByteBuffer clone = ByteBuffer.allocate(original.capacity());
     original.rewind();// copy from the beginning
     clone.put(original);
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 181d493a..10f4860a 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,8 @@ class VectorTileFunctionsTest {
 
     // Transform the input geometry into a vector tile geometry
     var outputGeom =
-        VectorTileFunctions.asVectorTileGeom(inputGeom, envelope.getEnvelopeInternal(), 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 2f2394a9..c3ed530a 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
@@ -20,7 +20,6 @@ package org.apache.baremaps.vectortile;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
-import java.awt.*;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.file.Files;
@@ -28,7 +27,6 @@ import java.nio.file.Path;
 import java.util.List;
 import java.util.Map;
 import java.util.zip.GZIPInputStream;
-import javax.swing.*;
 import org.junit.jupiter.api.Test;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.GeometryFactory;
@@ -39,7 +37,7 @@ public class VectorTileTest {
 
   @Test
   public void endToEnd() {
-    var tile = new Tile(List.of(
+    var tile = new VectorTile(List.of(
         new Layer("layer", 256, List.of(
             new Feature(1l, Map.of("a", 1.0, "b", "2"),
                 GEOMETRY_FACTORY.createPoint(new Coordinate(1, 2))),
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 a4f75369..dbdc6978 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
@@ -53,11 +53,11 @@ public class VectorTileViewer {
 
   static class TilePanel extends JPanel {
 
-    private final Tile tile;
+    private final VectorTile tile;
 
     private final int extent;
 
-    public TilePanel(Tile tile, int extent) {
+    public TilePanel(VectorTile tile, int extent) {
       this.tile = tile;
       this.extent = extent;
     }