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/06/23 12:33:59 UTC

[incubator-baremaps] branch main updated: Fix export command (#694)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new e82917b8 Fix export command (#694)
e82917b8 is described below

commit e82917b8ba98989bc1832f95ad26583c21a59bd1
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Fri Jun 23 14:33:54 2023 +0200

    Fix export command (#694)
    
    * Fix null pointer exception related to bounds
    
    * Improve SQLite settings
    
    * Add MBTiles benchmark
    
    * Add MBTiles server command
    
    * Refactor utility classes
---
 ...tmap-export.run.xml => basemap-mbtiles.run.xml} |  6 +-
 .run/openstreetmap-export.run.xml                  |  2 +-
 .../baremaps/benchmarks/MBTilesBenchmark.java      | 96 ++++++++++++++++++++++
 .../main/java/org/apache/baremaps/cli/map/Dev.java |  5 +-
 .../baremaps/cli/map/{Serve.java => MBTiles.java}  | 30 +++----
 .../main/java/org/apache/baremaps/cli/map/Map.java |  3 +-
 .../java/org/apache/baremaps/cli/map/Serve.java    |  7 +-
 .../org/apache/baremaps/cli/ogcapi/OgcApi.java     |  4 +-
 .../org/apache/baremaps/tilestore/TileStore.java   | 30 +++++++
 .../mbtiles/{MBTiles.java => MBTilesStore.java}    | 28 ++++++-
 .../tilestore/postgres/PostgresTileStore.java      |  2 +
 .../{postgres => utils}/PostgresUtils.java         | 15 ++--
 .../org/apache/baremaps/utils/SqliteUtils.java     | 37 +++++++++
 .../apache/baremaps/workflow/WorkflowContext.java  |  4 +-
 .../baremaps/workflow/tasks/ExportVectorTiles.java | 34 +++-----
 .../postgres/PostgresRepositoryTest.java           |  2 +-
 .../repository/BlockImporterTest.java              |  4 +-
 .../postgres/metadata/PostgresMetadataTest.java    |  2 +-
 .../{MBTilesTest.java => MBTilesStoreTest.java}    | 15 ++--
 .../org/apache/baremaps/ogcapi/OgcApiTest.java     |  4 +-
 20 files changed, 254 insertions(+), 76 deletions(-)

diff --git a/.run/openstreetmap-export.run.xml b/.run/basemap-mbtiles.run.xml
similarity index 58%
copy from .run/openstreetmap-export.run.xml
copy to .run/basemap-mbtiles.run.xml
index cb53b2d4..6a57530d 100644
--- a/.run/openstreetmap-export.run.xml
+++ b/.run/basemap-mbtiles.run.xml
@@ -1,9 +1,9 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="openstreetmap-export" type="Application" factoryName="Application">
+  <configuration default="false" name="basemap-mbtiles" type="Application" factoryName="Application">
     <option name="MAIN_CLASS_NAME" value="org.apache.baremaps.cli.Baremaps" />
     <module name="baremaps-cli" />
-    <option name="PROGRAM_PARAMETERS" value="map export --tileset tileset.json --repository tiles/" />
-    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/examples/openstreetmap" />
+    <option name="PROGRAM_PARAMETERS" value="map mbtiles --mbtiles $USER_HOME$/Datasets/Baremaps/tiles.mbtiles --tilejson tileset.js --style style.js" />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/basemap" />
     <extension name="coverage">
       <pattern>
         <option name="PATTERN" value="org.apache.baremaps.server.ogcapi.*" />
diff --git a/.run/openstreetmap-export.run.xml b/.run/openstreetmap-export.run.xml
index cb53b2d4..b067f8a0 100644
--- a/.run/openstreetmap-export.run.xml
+++ b/.run/openstreetmap-export.run.xml
@@ -2,7 +2,7 @@
   <configuration default="false" name="openstreetmap-export" type="Application" factoryName="Application">
     <option name="MAIN_CLASS_NAME" value="org.apache.baremaps.cli.Baremaps" />
     <module name="baremaps-cli" />
-    <option name="PROGRAM_PARAMETERS" value="map export --tileset tileset.json --repository tiles/" />
+    <option name="PROGRAM_PARAMETERS" value="map export --tileset tileset.json --repository tiles.mbtiles --mbtiles" />
     <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/examples/openstreetmap" />
     <extension name="coverage">
       <pattern>
diff --git a/baremaps-benchmark/src/main/java/org/apache/baremaps/benchmarks/MBTilesBenchmark.java b/baremaps-benchmark/src/main/java/org/apache/baremaps/benchmarks/MBTilesBenchmark.java
new file mode 100644
index 00000000..61c81265
--- /dev/null
+++ b/baremaps-benchmark/src/main/java/org/apache/baremaps/benchmarks/MBTilesBenchmark.java
@@ -0,0 +1,96 @@
+/*
+ * 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.benchmarks;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+import org.apache.baremaps.tilestore.TileCoord;
+import org.apache.baremaps.tilestore.TileStoreException;
+import org.apache.baremaps.tilestore.mbtiles.MBTilesStore;
+import org.apache.baremaps.utils.SqliteUtils;
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+@State(Scope.Benchmark)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@Fork(value = 1, warmups = 1)
+public class MBTilesBenchmark {
+
+  public static SecureRandom random = new SecureRandom();
+
+  @Param({"10", "100", "1000"})
+  public int iterations;
+
+  private Path file;
+
+  private MBTilesStore mbTilesStore;
+
+  @Setup
+  public void setup() throws IOException, TileStoreException {
+    file = Files.createTempFile(Paths.get("."), "baremaps", ".mbtiles");
+    mbTilesStore = new MBTilesStore(SqliteUtils.createDataSource(file));
+    mbTilesStore.initializeDatabase();
+  }
+
+  @TearDown
+  public void tearDown() throws IOException, TileStoreException {
+    Files.delete(file);
+  }
+
+  @Benchmark
+  @BenchmarkMode(Mode.SingleShotTime)
+  public void writeMBTiles(MBTilesBenchmark benchmark) throws TileStoreException {
+    for (int i = 0; i < benchmark.iterations; i++) {
+      var bytes = new byte[1 << 16];
+      random.nextBytes(bytes);
+      mbTilesStore.write(new TileCoord(0, 0, i), ByteBuffer.wrap(bytes));
+    }
+  }
+
+  @Benchmark
+  @BenchmarkMode(Mode.SingleShotTime)
+  public void writeMBTilesBatch(MBTilesBenchmark benchmark) throws TileStoreException {
+    var coords = new ArrayList<TileCoord>();
+    var buffers = new ArrayList<ByteBuffer>();
+    for (int i = 0; i < benchmark.iterations; i++) {
+      var bytes = new byte[1 << 16];
+      random.nextBytes(bytes);
+      coords.add(new TileCoord(0, 0, i));
+      buffers.add(ByteBuffer.wrap(bytes));
+      if (coords.size() == 100) {
+        random.nextBytes(bytes);
+        mbTilesStore.write(coords, buffers);
+        coords.clear();
+        buffers.clear();
+      }
+    }
+    mbTilesStore.write(coords, buffers);
+    coords.clear();
+    buffers.clear();
+  }
+
+  public static void main(String[] args) throws RunnerException {
+    Options opt =
+        new OptionsBuilder().include(MBTilesBenchmark.class.getSimpleName()).forks(1).build();
+    new Runner(opt).run();
+  }
+}
diff --git a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Dev.java b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Dev.java
index b0daab3c..2c54f526 100644
--- a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Dev.java
+++ b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Dev.java
@@ -23,10 +23,11 @@ import java.util.concurrent.Callable;
 import java.util.function.Supplier;
 import org.apache.baremaps.cli.Options;
 import org.apache.baremaps.config.ConfigReader;
-import org.apache.baremaps.postgres.PostgresUtils;
 import org.apache.baremaps.server.*;
+import org.apache.baremaps.server.CorsFilter;
 import org.apache.baremaps.tilestore.TileStore;
 import org.apache.baremaps.tilestore.postgres.PostgresTileStore;
+import org.apache.baremaps.utils.PostgresUtils;
 import org.apache.baremaps.vectortile.style.Style;
 import org.apache.baremaps.vectortile.tilejson.TileJSON;
 import org.apache.baremaps.vectortile.tileset.Tileset;
@@ -69,7 +70,7 @@ public class Dev implements Callable<Integer> {
     var configReader = new ConfigReader();
     var objectMapper = objectMapper();
     var tileset = objectMapper.readValue(configReader.read(this.tilesetPath), Tileset.class);
-    var datasource = PostgresUtils.dataSource(tileset.getDatabase());
+    var datasource = PostgresUtils.createDataSource(tileset.getDatabase());
 
     var tileStoreType = new TypeLiteral<Supplier<TileStore>>() {};
     var tileStoreSupplier = (Supplier<TileStore>) () -> {
diff --git a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/MBTiles.java
similarity index 81%
copy from baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java
copy to baremaps-cli/src/main/java/org/apache/baremaps/cli/map/MBTiles.java
index e0002b03..04739ed9 100644
--- a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java
+++ b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/MBTiles.java
@@ -23,14 +23,13 @@ import java.util.concurrent.Callable;
 import java.util.function.Supplier;
 import org.apache.baremaps.cli.Options;
 import org.apache.baremaps.config.ConfigReader;
-import org.apache.baremaps.postgres.PostgresUtils;
 import org.apache.baremaps.server.*;
 import org.apache.baremaps.tilestore.TileCache;
 import org.apache.baremaps.tilestore.TileStore;
-import org.apache.baremaps.tilestore.postgres.PostgresTileStore;
+import org.apache.baremaps.tilestore.mbtiles.MBTilesStore;
+import org.apache.baremaps.utils.SqliteUtils;
 import org.apache.baremaps.vectortile.style.Style;
 import org.apache.baremaps.vectortile.tilejson.TileJSON;
-import org.apache.baremaps.vectortile.tileset.Tileset;
 import org.glassfish.hk2.api.TypeLiteral;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 import org.glassfish.jersey.server.ResourceConfig;
@@ -40,10 +39,10 @@ import picocli.CommandLine.Command;
 import picocli.CommandLine.Mixin;
 import picocli.CommandLine.Option;
 
-@Command(name = "serve", description = "Start a tile server with caching capabilities.")
-public class Serve implements Callable<Integer> {
+@Command(name = "mbtiles", description = "Start a mbtiles server with caching capabilities.")
+public class MBTiles implements Callable<Integer> {
 
-  private static final Logger logger = LoggerFactory.getLogger(Serve.class);
+  private static final Logger logger = LoggerFactory.getLogger(MBTiles.class);
 
   @Mixin
   private Options options;
@@ -51,17 +50,18 @@ public class Serve implements Callable<Integer> {
   @Option(names = {"--cache"}, paramLabel = "CACHE", description = "The caffeine cache directive.")
   private String cache = "";
 
-  @Option(names = {"--tileset"}, paramLabel = "TILESET", description = "The tileset file.",
+  @Option(names = {"--mbtiles"}, paramLabel = "MBTILES", description = "The mbtiles file.",
       required = true)
-  private Path tilesetPath;
+  private Path mbtilesPath;
+
+  @Option(names = {"--tilejson"}, paramLabel = "TILEJSON", description = "The tileJSON file.",
+      required = true)
+  private Path tileJSONPath;
 
   @Option(names = {"--style"}, paramLabel = "STYLE", description = "The style file.",
       required = true)
   private Path stylePath;
 
-  @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;
 
@@ -69,12 +69,11 @@ public class Serve implements Callable<Integer> {
   public Integer call() throws Exception {
     var objectMapper = objectMapper();
     var configReader = new ConfigReader();
-    var tileset = objectMapper.readValue(configReader.read(this.tilesetPath), Tileset.class);
     var caffeineSpec = CaffeineSpec.parse(cache);
-    var datasource = PostgresUtils.dataSource(tileset.getDatabase());
 
+    var datasource = SqliteUtils.createDataSource(mbtilesPath);
     var tileStoreSupplierType = new TypeLiteral<Supplier<TileStore>>() {};
-    var tileStore = new PostgresTileStore(datasource, tileset);
+    var tileStore = new MBTilesStore(datasource);
     var tileCache = new TileCache(tileStore, caffeineSpec);
     var tileStoreSupplier = (Supplier<TileStore>) () -> tileCache;
 
@@ -83,9 +82,10 @@ public class Serve implements Callable<Integer> {
     var styleSupplier = (Supplier<Style>) () -> style;
 
     var tileJSONSupplierType = new TypeLiteral<Supplier<TileJSON>>() {};
-    var tileJSON = objectMapper.readValue(configReader.read(tilesetPath), TileJSON.class);
+    var tileJSON = objectMapper.readValue(configReader.read(tileJSONPath), TileJSON.class);
     var tileJSONSupplier = (Supplier<TileJSON>) () -> tileJSON;
 
+    // Configure the application
     var application =
         new ResourceConfig()
             .register(CorsFilter.class)
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 294ba714..4fed8fe9 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
@@ -18,7 +18,8 @@ import picocli.CommandLine;
 import picocli.CommandLine.Command;
 
 @Command(name = "map", description = "Map commands.",
-    subcommands = {Init.class, Export.class, Serve.class, Dev.class, StyleCommand.class},
+    subcommands = {Init.class, Export.class, Serve.class, Dev.class, StyleCommand.class,
+        MBTiles.class},
     sortOptions = false)
 public class Map implements Runnable {
 
diff --git a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java
index e0002b03..62978214 100644
--- a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java
+++ b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Serve.java
@@ -23,11 +23,12 @@ import java.util.concurrent.Callable;
 import java.util.function.Supplier;
 import org.apache.baremaps.cli.Options;
 import org.apache.baremaps.config.ConfigReader;
-import org.apache.baremaps.postgres.PostgresUtils;
 import org.apache.baremaps.server.*;
+import org.apache.baremaps.server.CorsFilter;
 import org.apache.baremaps.tilestore.TileCache;
 import org.apache.baremaps.tilestore.TileStore;
 import org.apache.baremaps.tilestore.postgres.PostgresTileStore;
+import org.apache.baremaps.utils.PostgresUtils;
 import org.apache.baremaps.vectortile.style.Style;
 import org.apache.baremaps.vectortile.tilejson.TileJSON;
 import org.apache.baremaps.vectortile.tileset.Tileset;
@@ -69,10 +70,10 @@ public class Serve implements Callable<Integer> {
   public Integer call() throws Exception {
     var objectMapper = objectMapper();
     var configReader = new ConfigReader();
-    var tileset = objectMapper.readValue(configReader.read(this.tilesetPath), Tileset.class);
     var caffeineSpec = CaffeineSpec.parse(cache);
-    var datasource = PostgresUtils.dataSource(tileset.getDatabase());
 
+    var tileset = objectMapper.readValue(configReader.read(tilesetPath), Tileset.class);
+    var datasource = PostgresUtils.createDataSource(tileset.getDatabase());
     var tileStoreSupplierType = new TypeLiteral<Supplier<TileStore>>() {};
     var tileStore = new PostgresTileStore(datasource, tileset);
     var tileCache = new TileCache(tileStore, caffeineSpec);
diff --git a/baremaps-cli/src/main/java/org/apache/baremaps/cli/ogcapi/OgcApi.java b/baremaps-cli/src/main/java/org/apache/baremaps/cli/ogcapi/OgcApi.java
index ea1a7e24..e596edc4 100644
--- a/baremaps-cli/src/main/java/org/apache/baremaps/cli/ogcapi/OgcApi.java
+++ b/baremaps-cli/src/main/java/org/apache/baremaps/cli/ogcapi/OgcApi.java
@@ -27,11 +27,11 @@ import javax.sql.DataSource;
 import org.apache.baremaps.cli.Options;
 import org.apache.baremaps.config.ConfigReader;
 import org.apache.baremaps.ogcapi.*;
-import org.apache.baremaps.postgres.PostgresUtils;
 import org.apache.baremaps.server.CorsFilter;
 import org.apache.baremaps.tilestore.TileCache;
 import org.apache.baremaps.tilestore.TileStore;
 import org.apache.baremaps.tilestore.postgres.PostgresTileStore;
+import org.apache.baremaps.utils.PostgresUtils;
 import org.apache.baremaps.vectortile.tileset.Tileset;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 import org.glassfish.jersey.media.multipart.MultiPartFeature;
@@ -78,7 +78,7 @@ public class OgcApi implements Callable<Integer> {
     var configReader = new ConfigReader();
     var config = objectMapper.readValue(configReader.read(this.tileset), Tileset.class);
     var caffeineSpec = CaffeineSpec.parse(cache);
-    var dataSource = PostgresUtils.dataSource(database);
+    var dataSource = PostgresUtils.createDataSource(database);
 
     var tileStore = new PostgresTileStore(dataSource, config);
     var tileCache = new TileCache(tileStore, caffeineSpec);
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileStore.java b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileStore.java
index c7288383..926fe088 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileStore.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/TileStore.java
@@ -15,6 +15,8 @@ package org.apache.baremaps.tilestore;
 
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
 
 /** Represents a store for tiles. */
 public interface TileStore {
@@ -28,6 +30,21 @@ public interface TileStore {
    */
   ByteBuffer read(TileCoord tileCoord) throws TileStoreException;
 
+  /**
+   * Reads the content of several tiles.
+   *
+   * @param tileCoords the tile coordinates
+   * @return the content of the tiles
+   * @throws TileStoreException
+   */
+  default List<ByteBuffer> read(List<TileCoord> tileCoords) throws TileStoreException {
+    var blobs = new ArrayList<ByteBuffer>(tileCoords.size());
+    for (var tileCoord : tileCoords) {
+      blobs.add(read(tileCoord));
+    }
+    return blobs;
+  }
+
   /**
    * Writes the content of a tile.
    *
@@ -37,6 +54,19 @@ public interface TileStore {
    */
   void write(TileCoord tileCoord, ByteBuffer blob) throws TileStoreException;
 
+  /**
+   * Writes the content of several tiles.
+   *
+   * @param tileCoords the tile coordinates
+   * @param blobs the content of the tiles
+   * @throws TileStoreException
+   */
+  default void write(List<TileCoord> tileCoords, List<ByteBuffer> blobs) throws TileStoreException {
+    for (int i = 0; i < tileCoords.size(); i++) {
+      write(tileCoords.get(i), blobs.get(i));
+    }
+  }
+
   /**
    * Deletes the content of a tile.
    *
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/mbtiles/MBTiles.java b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/mbtiles/MBTilesStore.java
similarity index 86%
rename from baremaps-core/src/main/java/org/apache/baremaps/tilestore/mbtiles/MBTiles.java
rename to baremaps-core/src/main/java/org/apache/baremaps/tilestore/mbtiles/MBTilesStore.java
index 9cfbdad7..847a2c9a 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/mbtiles/MBTiles.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/mbtiles/MBTilesStore.java
@@ -22,7 +22,9 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import javax.sql.DataSource;
 import org.apache.baremaps.tilestore.TileCoord;
 import org.apache.baremaps.tilestore.TileStore;
 import org.apache.baremaps.tilestore.TileStoreException;
@@ -32,7 +34,7 @@ import org.apache.baremaps.tilestore.TileStoreException;
  * <a href="https://docs.mapbox.com/help/glossary/mbtiles/">MBTiles</a> file format for storing
  * tiles.
  */
-public class MBTiles implements TileStore {
+public class MBTilesStore implements TileStore {
 
   private static final String CREATE_TABLE_METADATA =
       "CREATE TABLE IF NOT EXISTS metadata (name TEXT, value TEXT, PRIMARY KEY (name))";
@@ -58,14 +60,14 @@ public class MBTiles implements TileStore {
 
   private static final String DELETE_METADATA = "DELETE FROM metadata";
 
-  private final org.sqlite.SQLiteDataSource dataSource;
+  private final DataSource dataSource;
 
   /**
    * Constructs an {@code MBTiles} with the provided SQLite datasource.
    *
    * @param dataSource the SQLite datasource
    */
-  public MBTiles(org.sqlite.SQLiteDataSource dataSource) {
+  public MBTilesStore(DataSource dataSource) {
     this.dataSource = dataSource;
   }
 
@@ -104,6 +106,26 @@ public class MBTiles implements TileStore {
     }
   }
 
+  /** {@inheritDoc} */
+  @Override
+  public void write(List<TileCoord> tileCoords, List<ByteBuffer> blobs) throws TileStoreException {
+    try (Connection connection = dataSource.getConnection();
+        PreparedStatement statement = connection.prepareStatement(INSERT_TILE)) {
+      for (int i = 0; i < tileCoords.size(); i++) {
+        TileCoord tileCoord = tileCoords.get(i);
+        ByteBuffer blob = blobs.get(i);
+        statement.setInt(1, tileCoord.z());
+        statement.setInt(2, tileCoord.x());
+        statement.setInt(3, reverseY(tileCoord.y(), tileCoord.z()));
+        statement.setBytes(4, blob.array());
+        statement.addBatch();
+      }
+      statement.executeBatch();
+    } catch (SQLException e) {
+      throw new TileStoreException(e);
+    }
+  }
+
   /** {@inheritDoc} */
   @Override
   public void delete(TileCoord tileCoord) throws TileStoreException {
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/postgres/PostgresTileStore.java b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/postgres/PostgresTileStore.java
index 1e1eb6c3..8f531921 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/tilestore/postgres/PostgresTileStore.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/tilestore/postgres/PostgresTileStore.java
@@ -239,11 +239,13 @@ public class PostgresTileStore implements TileStore {
   }
 
   /** This operation is not supported. */
+  @Override
   public void write(TileCoord tileCoord, ByteBuffer blob) {
     throw new UnsupportedOperationException("The postgis tile store is read only");
   }
 
   /** This operation is not supported. */
+  @Override
   public void delete(TileCoord tileCoord) {
     throw new UnsupportedOperationException("The postgis tile store is read only");
   }
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/postgres/PostgresUtils.java b/baremaps-core/src/main/java/org/apache/baremaps/utils/PostgresUtils.java
similarity index 81%
rename from baremaps-core/src/main/java/org/apache/baremaps/postgres/PostgresUtils.java
rename to baremaps-core/src/main/java/org/apache/baremaps/utils/PostgresUtils.java
index 70f0667a..ab5cf6e3 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/postgres/PostgresUtils.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/utils/PostgresUtils.java
@@ -10,7 +10,7 @@
  * the License.
  */
 
-package org.apache.baremaps.postgres;
+package org.apache.baremaps.utils;
 
 
 
@@ -29,10 +29,11 @@ public final class PostgresUtils {
 
   private PostgresUtils() {}
 
-  public static HikariDataSource dataSource(String host, Integer port, String database,
+  public static HikariDataSource createDataSource(String host, Integer port, String database,
       String username, String password) {
-    return dataSource(String.format("jdbc:postgresql://%s:%s/%s?&user=%s&password=%s", host, port,
-        database, username, password));
+    return createDataSource(
+        String.format("jdbc:postgresql://%s:%s/%s?&user=%s&password=%s", host, port,
+            database, username, password));
   }
 
   /**
@@ -42,8 +43,8 @@ public final class PostgresUtils {
    * @param url the JDBC url
    * @return the data source
    */
-  public static HikariDataSource dataSource(String url) {
-    return dataSource(url, Runtime.getRuntime().availableProcessors());
+  public static HikariDataSource createDataSource(String url) {
+    return createDataSource(url, Runtime.getRuntime().availableProcessors());
   }
 
   /**
@@ -53,7 +54,7 @@ public final class PostgresUtils {
    * @param poolSize the pool size
    * @return the data source
    */
-  public static HikariDataSource dataSource(String url, int poolSize) {
+  public static HikariDataSource createDataSource(String url, int poolSize) {
     if (poolSize < 1) {
       throw new IllegalArgumentException("PoolSize cannot be inferior to 1");
     }
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/utils/SqliteUtils.java b/baremaps-core/src/main/java/org/apache/baremaps/utils/SqliteUtils.java
index 4b937954..f6ea9051 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/utils/SqliteUtils.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/utils/SqliteUtils.java
@@ -15,17 +15,54 @@ package org.apache.baremaps.utils;
 
 
 import com.google.common.io.Resources;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
 import java.io.IOException;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
 import java.sql.Statement;
+import org.sqlite.SQLiteConfig;
+import org.sqlite.SQLiteConfig.JournalMode;
+import org.sqlite.SQLiteConfig.LockingMode;
+import org.sqlite.SQLiteConfig.SynchronousMode;
+import org.sqlite.SQLiteConfig.TempStore;
+import org.sqlite.SQLiteDataSource;
 
 /** A helper class for creating executing sql scripts onto a SQLite database */
 public final class SqliteUtils {
 
+  private SqliteUtils() {}
+
+  /**
+   * Create a SQLite data source.
+   *
+   * @param path the path to the SQLite database
+   * @return the SQLite data source
+   */
+  public static HikariDataSource createDataSource(Path path) {
+    var sqliteConfig = new SQLiteConfig();
+    sqliteConfig.setCacheSize(1000000);
+    sqliteConfig.setPageSize(65536);
+    sqliteConfig.setJournalMode(JournalMode.OFF);
+    sqliteConfig.setLockingMode(LockingMode.EXCLUSIVE);
+    sqliteConfig.setSynchronous(SynchronousMode.OFF);
+    sqliteConfig.setTempStore(TempStore.MEMORY);
+
+    var sqliteDataSource = new SQLiteDataSource();
+    sqliteDataSource.setConfig(sqliteConfig);
+    sqliteDataSource.setUrl("jdbc:sqlite:" + path);
+
+    var hikariConfig = new HikariConfig();
+    hikariConfig.setDataSource(sqliteDataSource);
+    hikariConfig.setMaximumPoolSize(1);
+
+    return new HikariDataSource(hikariConfig);
+  }
+
   /**
    * Executes the queries contained in a resource file.
    *
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/workflow/WorkflowContext.java b/baremaps-core/src/main/java/org/apache/baremaps/workflow/WorkflowContext.java
index 1cc7fab6..eff85f26 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/workflow/WorkflowContext.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/workflow/WorkflowContext.java
@@ -17,7 +17,7 @@ package org.apache.baremaps.workflow;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import javax.sql.DataSource;
-import org.apache.baremaps.postgres.PostgresUtils;
+import org.apache.baremaps.utils.PostgresUtils;
 
 /**
  * A context that is passed to the tasks of a workflow and used to share data between tasks.
@@ -33,7 +33,7 @@ public class WorkflowContext {
    * @return the data source
    */
   public DataSource getDataSource(String database) {
-    return dataSources.computeIfAbsent(database, d -> PostgresUtils.dataSource(d));
+    return dataSources.computeIfAbsent(database, d -> PostgresUtils.createDataSource(d));
   }
 
 }
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/ExportVectorTiles.java b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/ExportVectorTiles.java
index 7828a049..5f3f4910 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/ExportVectorTiles.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/ExportVectorTiles.java
@@ -17,6 +17,7 @@ import static org.apache.baremaps.utils.ObjectMapperUtils.objectMapper;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.IOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.HashMap;
 import java.util.Map;
@@ -27,8 +28,9 @@ import org.apache.baremaps.stream.ProgressLogger;
 import org.apache.baremaps.stream.StreamUtils;
 import org.apache.baremaps.tilestore.*;
 import org.apache.baremaps.tilestore.file.FileTileStore;
-import org.apache.baremaps.tilestore.mbtiles.MBTiles;
+import org.apache.baremaps.tilestore.mbtiles.MBTilesStore;
 import org.apache.baremaps.tilestore.postgres.PostgresTileStore;
+import org.apache.baremaps.utils.SqliteUtils;
 import org.apache.baremaps.vectortile.tileset.Tileset;
 import org.apache.baremaps.vectortile.tileset.TilesetQuery;
 import org.apache.baremaps.workflow.Task;
@@ -36,12 +38,6 @@ import org.apache.baremaps.workflow.WorkflowContext;
 import org.locationtech.jts.geom.Envelope;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.sqlite.SQLiteConfig;
-import org.sqlite.SQLiteConfig.JournalMode;
-import org.sqlite.SQLiteConfig.LockingMode;
-import org.sqlite.SQLiteConfig.SynchronousMode;
-import org.sqlite.SQLiteConfig.TempStore;
-import org.sqlite.SQLiteDataSource;
 
 public record ExportVectorTiles(
     Path tileset,
@@ -61,10 +57,11 @@ public record ExportVectorTiles(
     var sourceTileStore = sourceTileStore(tileset, datasource);
     var targetTileStore = targetTileStore(tileset);
 
-    var envelope =
-        new Envelope(
+    var envelope = tileset.getBounds().size() == 4
+        ? new Envelope(
             tileset.getBounds().get(0), tileset.getBounds().get(2),
-            tileset.getBounds().get(1), tileset.getBounds().get(3));
+            tileset.getBounds().get(1), tileset.getBounds().get(3))
+        : new Envelope(-180, 180, -85.0511, 85.0511);
 
     var count = TileCoord.count(envelope, tileset.getMinzoom(), tileset.getMaxzoom());
 
@@ -81,21 +78,11 @@ public record ExportVectorTiles(
 
   private TileStore targetTileStore(Tileset source) throws TileStoreException, IOException {
     if (mbtiles) {
-      var sqliteConfig = new SQLiteConfig();
-      sqliteConfig.setCacheSize(1000000);
-      sqliteConfig.setJournalMode(JournalMode.OFF);
-      sqliteConfig.setLockingMode(LockingMode.EXCLUSIVE);
-      sqliteConfig.setSynchronous(SynchronousMode.OFF);
-      sqliteConfig.setTempStore(TempStore.MEMORY);
-
-      var dataSource = new SQLiteDataSource();
-      dataSource.setConfig(sqliteConfig);
-      dataSource.setUrl("jdbc:sqlite:" + repository);
-
-      var tilesStore = new MBTiles(dataSource);
+      Files.deleteIfExists(repository);
+      var dataSource = SqliteUtils.createDataSource(repository);
+      var tilesStore = new MBTilesStore(dataSource);
       tilesStore.initializeDatabase();
       tilesStore.writeMetadata(metadata(source));
-
       return tilesStore;
     } else {
       return new FileTileStore(repository);
@@ -143,4 +130,5 @@ public record ExportVectorTiles(
 
     return metadata;
   }
+
 }
diff --git a/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/postgres/PostgresRepositoryTest.java b/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/postgres/PostgresRepositoryTest.java
index 00a1f888..9da06fdf 100644
--- a/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/postgres/PostgresRepositoryTest.java
+++ b/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/postgres/PostgresRepositoryTest.java
@@ -18,8 +18,8 @@ import java.io.IOException;
 import java.sql.Connection;
 import java.sql.SQLException;
 import javax.sql.DataSource;
-import org.apache.baremaps.postgres.PostgresUtils;
 import org.apache.baremaps.testing.PostgresContainerTest;
+import org.apache.baremaps.utils.PostgresUtils;
 import org.junit.jupiter.api.BeforeEach;
 
 public abstract class PostgresRepositoryTest extends PostgresContainerTest {
diff --git a/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/repository/BlockImporterTest.java b/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/repository/BlockImporterTest.java
index 3d3d1440..3b645c43 100644
--- a/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/repository/BlockImporterTest.java
+++ b/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/repository/BlockImporterTest.java
@@ -27,9 +27,9 @@ import org.apache.baremaps.openstreetmap.postgres.PostgresHeaderRepository;
 import org.apache.baremaps.openstreetmap.postgres.PostgresNodeRepository;
 import org.apache.baremaps.openstreetmap.postgres.PostgresRelationRepository;
 import org.apache.baremaps.openstreetmap.postgres.PostgresWayRepository;
-import org.apache.baremaps.postgres.PostgresUtils;
 import org.apache.baremaps.testing.PostgresContainerTest;
 import org.apache.baremaps.testing.TestFiles;
+import org.apache.baremaps.utils.PostgresUtils;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
@@ -44,7 +44,7 @@ class BlockImporterTest extends PostgresContainerTest {
 
   @BeforeEach
   void init() throws SQLException, IOException {
-    dataSource = PostgresUtils.dataSource(jdbcUrl(), 1);
+    dataSource = PostgresUtils.createDataSource(jdbcUrl(), 1);
     headerRepository = new PostgresHeaderRepository(dataSource);
     nodeRepository = new PostgresNodeRepository(dataSource);
     tableRepository = new PostgresWayRepository(dataSource);
diff --git a/baremaps-core/src/test/java/org/apache/baremaps/postgres/metadata/PostgresMetadataTest.java b/baremaps-core/src/test/java/org/apache/baremaps/postgres/metadata/PostgresMetadataTest.java
index cf3e9367..411318ce 100644
--- a/baremaps-core/src/test/java/org/apache/baremaps/postgres/metadata/PostgresMetadataTest.java
+++ b/baremaps-core/src/test/java/org/apache/baremaps/postgres/metadata/PostgresMetadataTest.java
@@ -20,8 +20,8 @@ import java.sql.DatabaseMetaData;
 import java.sql.DriverManager;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import org.apache.baremaps.postgres.PostgresUtils;
 import org.apache.baremaps.testing.PostgresContainerTest;
+import org.apache.baremaps.utils.PostgresUtils;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 
diff --git a/baremaps-core/src/test/java/org/apache/baremaps/tilestore/mbtiles/MBTilesTest.java b/baremaps-core/src/test/java/org/apache/baremaps/tilestore/mbtiles/MBTilesStoreTest.java
similarity index 83%
rename from baremaps-core/src/test/java/org/apache/baremaps/tilestore/mbtiles/MBTilesTest.java
rename to baremaps-core/src/test/java/org/apache/baremaps/tilestore/mbtiles/MBTilesStoreTest.java
index b677e5d5..12abdf82 100644
--- a/baremaps-core/src/test/java/org/apache/baremaps/tilestore/mbtiles/MBTilesTest.java
+++ b/baremaps-core/src/test/java/org/apache/baremaps/tilestore/mbtiles/MBTilesStoreTest.java
@@ -13,7 +13,6 @@
 package org.apache.baremaps.tilestore.mbtiles;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
 import java.nio.file.Files;
@@ -28,7 +27,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.sqlite.SQLiteDataSource;
 
-class MBTilesTest extends TileStoreTest {
+class MBTilesStoreTest extends TileStoreTest {
 
   Path file;
 
@@ -43,27 +42,27 @@ class MBTilesTest extends TileStoreTest {
   }
 
   @Override
-  public MBTiles createTileStore() throws Exception {
+  public MBTilesStore createTileStore() throws Exception {
     SQLiteDataSource dataSource = new SQLiteDataSource();
     String url = "jdbc:sqlite:" + file.toAbsolutePath();
     dataSource.setUrl(url);
-    MBTiles tilesStore = new MBTiles(dataSource);
+    MBTilesStore tilesStore = new MBTilesStore(dataSource);
     tilesStore.initializeDatabase();
     return tilesStore;
   }
 
   @Test
   void readWriteMetadata() throws Exception {
-    MBTiles tileStore = createTileStore();
+    MBTilesStore tileStore = createTileStore();
     Map<String, String> metadata = tileStore.readMetadata();
-    assertTrue(metadata.size() == 0);
+    assertEquals(0, metadata.size());
 
     Map<String, String> m1 = new HashMap<>();
     m1.put("test", "test");
     tileStore.writeMetadata(m1);
 
     Map<String, String> m2 = tileStore.readMetadata();
-    assertTrue(m2.size() == 1);
-    assertEquals(m2.get("test"), "test");
+    assertEquals(1, m2.size());
+    assertEquals("test", m2.get("test"));
   }
 }
diff --git a/baremaps-ogcapi/src/test/java/org/apache/baremaps/ogcapi/OgcApiTest.java b/baremaps-ogcapi/src/test/java/org/apache/baremaps/ogcapi/OgcApiTest.java
index 6cff8fa5..0b8b0dbd 100644
--- a/baremaps-ogcapi/src/test/java/org/apache/baremaps/ogcapi/OgcApiTest.java
+++ b/baremaps-ogcapi/src/test/java/org/apache/baremaps/ogcapi/OgcApiTest.java
@@ -20,9 +20,9 @@ import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import javax.sql.DataSource;
-import org.apache.baremaps.postgres.PostgresUtils;
 import org.apache.baremaps.tilestore.TileStore;
 import org.apache.baremaps.tilestore.postgres.PostgresTileStore;
+import org.apache.baremaps.utils.PostgresUtils;
 import org.apache.baremaps.vectortile.tileset.Tileset;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 import org.glassfish.jersey.client.ClientConfig;
@@ -40,7 +40,7 @@ public abstract class OgcApiTest extends JerseyTest {
   @Override
   protected ResourceConfig configure() {
     // Create a datasource to a throwaway postgis database
-    dataSource = PostgresUtils.dataSource("jdbc:tc:postgis:13-3.1:///baremaps");
+    dataSource = PostgresUtils.createDataSource("jdbc:tc:postgis:13-3.1:///baremaps");
 
     // Initialize the database
     try (var connection = dataSource.getConnection()) {