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 c3cdfced Add support for openstreetmap changes (#801)
c3cdfced is described below

commit c3cdfceda9c93255bf700c6d35f28797502ff8af
Author: Bertil Chapuis <[email protected]>
AuthorDate: Thu Nov 16 17:21:24 2023 +0100

    Add support for openstreetmap changes (#801)
    
    * Add refresh statements for the materialized views
    
    * Add workflow that refresh the materialized views
    
    * Add workflow that update the openstreetmap data
---
 LICENSE                                            |   1 +
 NOTICE                                             |   7 +
 .../database/collection/DataCollectionAdapter.java |   2 +-
 .../function/ChangeEntitiesHandler.java            |   2 +-
 .../postgres/PostgresCoordinateMap.java            | 122 ++++++++------
 .../postgres/PostgresHeaderRepository.java         |  28 ++--
 .../postgres/PostgresNodeRepository.java           |  44 +++--
 .../postgres/PostgresReferenceMap.java             | 117 +++++++------
 .../postgres/PostgresRelationRepository.java       |  32 ++--
 .../postgres/PostgresWayRepository.java            |  43 +++--
 ...ChangeImporter.java => CopyChangeImporter.java} |   7 +-
 ...{ChangeImporter.java => PutChangeImporter.java} |  15 +-
 .../baremaps/openstreetmap/state/StateReader.java  | 186 ++++++++++++++++++++-
 .../storage/postgres/PostgresDataSchema.java       |   9 +-
 .../shapefile/internal/ShapefileByteReader.java    |   8 +-
 .../baremaps/workflow/tasks/ImportOsmOsc.java      |   4 +-
 .../baremaps/workflow/tasks/UpdateOsmDatabase.java |  60 ++++---
 .../baremaps/openstreetmap/OpenStreetMapTest.java  |   2 +-
 .../openstreetmap/state/StateReaderTest.java}      |  35 ++--
 .../baremaps/workflow/tasks/ImportMonacoTest.java  |   2 +-
 .../workflow/tasks/ImportUpdateDataTest.java       |   4 +-
 .../tasks/ImportUpdateLiechtensteinTest.java       |   6 +-
 .../simple/000/000/{001.osc.gz => 002.osc.gz}      | Bin
 .../000/000/{001.state.txt => 002.state.txt}       |   0
 .../simple/{000/000/001.state.txt => state.txt}    |   0
 basemap/{workflow.js => import.js}                 |  53 +++---
 basemap/layers/boundary/globaladm0_clean.sql       |  36 ----
 basemap/layers/boundary/globaladm0_index.sql       |  28 ----
 basemap/layers/boundary/globaladm0_simplify.sql    |  76 ---------
 basemap/layers/boundary/globaladm1_clean.sql       |  36 ----
 basemap/layers/boundary/globaladm1_index.sql       |  28 ----
 basemap/layers/boundary/globaladm1_simplify.sql    |  76 ---------
 basemap/layers/highway/clean.sql                   |  25 ++-
 basemap/layers/highway/prepare.sql                 |   2 -
 .../{route/prepare.sql => highway/refresh.sql}     |  27 ++-
 basemap/layers/landuse/clean.sql                   |   1 +
 basemap/layers/landuse/refresh.sql                 |  62 +++++++
 basemap/layers/leisure/clean.sql                   |   1 +
 basemap/layers/leisure/index.sql                   |   1 +
 basemap/layers/leisure/prepare.sql                 |   1 +
 basemap/layers/leisure/refresh.sql                 |  62 +++++++
 basemap/layers/linestring/clean.sql                |   1 +
 basemap/layers/linestring/index.sql                |   1 +
 basemap/layers/linestring/prepare.sql              |   1 +
 .../linestring/refresh.sql}                        |   3 +-
 .../initialize.sql => layers/member/refresh.sql}   |   3 +-
 basemap/layers/natural/refresh.sql                 |  57 +++++++
 basemap/layers/point/index.sql                     |   1 +
 .../{linestring/prepare.sql => point/refresh.sql}  |  24 +--
 .../initialize.sql => layers/polygon/refresh.sql}  |   3 +-
 basemap/layers/railway/clean.sql                   |  23 +++
 basemap/layers/railway/index.sql                   |   1 +
 basemap/layers/railway/prepare.sql                 |   2 +-
 .../{route/prepare.sql => railway/refresh.sql}     |  27 ++-
 basemap/layers/route/clean.sql                     |  23 +++
 basemap/layers/route/index.sql                     |   1 +
 basemap/layers/route/prepare.sql                   |   1 +
 .../{linestring/prepare.sql => route/refresh.sql}  |  23 +--
 basemap/layers/route/simplify.sql                  |   1 +
 basemap/layers/waterway/clean.sql                  |  23 +++
 basemap/layers/waterway/index.sql                  |   1 +
 basemap/layers/waterway/prepare.sql                |   1 +
 .../{route/prepare.sql => waterway/refresh.sql}    |  27 ++-
 basemap/queries/initialize.sql                     |   1 +
 basemap/queries/ne_index.sql                       |   1 +
 basemap/queries/osm_relations.sql                  |   1 +
 basemap/queries/osm_ways.sql                       |   1 +
 basemap/queries/statistics.sql                     |   1 +
 basemap/refresh.js                                 | 143 ++++++++++++++++
 basemap/update.js                                  |  34 ++++
 70 files changed, 1075 insertions(+), 606 deletions(-)

diff --git a/LICENSE b/LICENSE
index a87d1857..076aef88 100644
--- a/LICENSE
+++ b/LICENSE
@@ -215,3 +215,4 @@
        - Natural Earth, Public Domain
        - GeoNames, Creative Commons Attribution 4.0 License
        - RIPE NCC, RIPE NCC Terms and Conditions
+       - pyosmium, BSD 2-Clause "Simplified" License
diff --git a/NOTICE b/NOTICE
index 461257a3..317477f6 100644
--- a/NOTICE
+++ b/NOTICE
@@ -39,6 +39,13 @@ The derived files are:
 - 
baremaps-core/src/main/java/org/apache/baremaps/database/collection/MonotonicDataMap.java
 - 
baremaps-core/src/main/java/org/apache/baremaps/database/collection/MonotonicFixedSizeDataMap.java
 
+This product includes code derived from pyosmium.
+BSD 2-Clause "Simplified" License.
+Please visit the following URL for the full text of the pyosmium license:
+https://github.com/osmcode/pyosmium/blob/master/LICENSE.TXT
+The derived files are:
+- 
baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/state/StateReader.java
+
 This product includes test data derived from OpenStreetMap.
 Open Data Commons Open Database License (ODbL).
 Please visit the following URL for the full text of the ODbL license:
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/database/collection/DataCollectionAdapter.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/database/collection/DataCollectionAdapter.java
index f202ed9f..be04534d 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/database/collection/DataCollectionAdapter.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/database/collection/DataCollectionAdapter.java
@@ -45,7 +45,7 @@ public class DataCollectionAdapter<S, T> extends 
AbstractDataCollection<T> {
    * {@inheritDoc}
    */
   @Override
-  public Iterator iterator() {
+  public Iterator<T> iterator() {
     return collection.stream().map(this.transformer).iterator();
   }
 
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/ChangeEntitiesHandler.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/ChangeEntitiesHandler.java
index 4d7ecf72..d500cd85 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/ChangeEntitiesHandler.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/ChangeEntitiesHandler.java
@@ -23,7 +23,7 @@ import java.util.function.Consumer;
 import org.apache.baremaps.openstreetmap.model.Change;
 import org.apache.baremaps.openstreetmap.model.Entity;
 
-/** Represents an operation on the entities of changes of different types. */
+/** Represents an operation on the entities of a change. */
 public class ChangeEntitiesHandler implements Consumer<Change> {
 
   private final Consumer<Entity> consumer;
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresCoordinateMap.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresCoordinateMap.java
index 909138d8..7f27b313 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresCoordinateMap.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresCoordinateMap.java
@@ -31,59 +31,81 @@ import org.locationtech.jts.geom.Coordinate;
  */
 public class PostgresCoordinateMap extends DataMap<Long, Coordinate> {
 
-  public static final String SELECT_CONTAINS_KEY = """
-      SELECT 1
-      FROM osm_nodes
-      WHERE id = ? LIMIT 1""";
-
-  public static final String SELECT_CONTAINS_VALUE = """
-      SELECT 1
-      FROM osm_nodes
-      WHERE nodes = ? LIMIT 1""";
-
-  private static final String SELECT_IN = """
-      SELECT id, lon, lat
-      FROM osm_nodes
-      WHERE id
-      WHERE id = ANY (?)""";
-
-  private static final String SELECT_BY_ID = """
-      SELECT lon, lat
-      FROM osm_nodes
-      WHERE id = ?""";
-
-  private static final String SELECT_SIZE = """
-      SELECT count()
-      FROM osm_nodes
-      """;
-
-  private static final String SELECT_KEYS = """
-      SELECT id
-      FROM osm_nodes
-      """;
-
-  private static final String SELECT_VALUES = """
-      SELECT lon, lat
-      FROM osm_nodes
-      """;
-
-  private static final String SELECT_ENTRIES = """
-      SELECT id, lon, lat
-      FROM osm_nodes
-      """;
-
   private final DataSource dataSource;
 
-  /** Constructs a {@link PostgresCoordinateMap}. */
+  public final String selectContainsKey;
+
+  public final String selectContainsValue;
+
+  private final String selectIn;
+
+  private final String selectById;
+
+  private final String selectSize;
+
+  private final String selectKeys;
+
+  private final String selectValues;
+
+  private final String selectEntries;
+
+  /**
+   * Constructs a {@link PostgresCoordinateMap}.
+   */
   public PostgresCoordinateMap(DataSource dataSource) {
+    this(dataSource, "public", "osm_nodes");
+  }
+
+  /**
+   * Constructs a {@link PostgresCoordinateMap}.
+   */
+  public PostgresCoordinateMap(DataSource dataSource, String schema, String 
table) {
     this.dataSource = dataSource;
+    var fullTableName = String.format("%s.%s", schema, table);
+    this.selectContainsKey = String.format("""
+        SELECT 1
+        FROM %1$s
+        WHERE id = ? LIMIT 1
+        """, fullTableName);
+    this.selectContainsValue = String.format("""
+        SELECT 1
+        FROM %1$s
+        WHERE nodes = ? LIMIT 1
+        """, fullTableName);
+    this.selectIn = String.format("""
+        SELECT id, lon, lat
+        FROM %1$s
+        WHERE id = ANY (?)
+        """, fullTableName);
+    this.selectById = String.format("""
+        SELECT lon, lat
+        FROM %1$s
+        WHERE id = ?
+        """, fullTableName);
+    this.selectSize = String.format("""
+        SELECT count()
+        FROM %1$s
+        """, fullTableName);
+    this.selectKeys = String.format("""
+        SELECT id
+        FROM %1$s
+        """, fullTableName);
+    this.selectValues = String.format("""
+        SELECT lon, lat
+        FROM %1$s
+        """, fullTableName);
+    this.selectEntries = String.format("""
+        SELECT id, lon, lat
+        FROM %1$s
+        """, fullTableName);
+
   }
 
   /** {@inheritDoc} */
   @Override
   public Coordinate get(Object key) {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_BY_ID)) {
+        PreparedStatement statement = connection.prepareStatement(selectById)) 
{
       statement.setLong(1, (Long) key);
       try (ResultSet result = statement.executeQuery()) {
         if (result.next()) {
@@ -103,7 +125,7 @@ public class PostgresCoordinateMap extends DataMap<Long, 
Coordinate> {
   @Override
   public List<Coordinate> getAll(List<Long> keys) {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = connection.prepareStatement(SELECT_IN)) {
+        PreparedStatement statement = connection.prepareStatement(selectIn)) {
       statement.setArray(1, connection.createArrayOf("int8", keys.toArray()));
       try (ResultSet result = statement.executeQuery()) {
         Map<Long, Coordinate> nodes = new HashMap<>();
@@ -123,7 +145,7 @@ public class PostgresCoordinateMap extends DataMap<Long, 
Coordinate> {
   @Override
   protected Iterator<Long> keyIterator() {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_KEYS)) {
+        PreparedStatement statement = connection.prepareStatement(selectKeys)) 
{
       ResultSet result = statement.executeQuery();
       return new PostgresIterator<>(result, this::key);
     } catch (SQLException e) {
@@ -142,7 +164,7 @@ public class PostgresCoordinateMap extends DataMap<Long, 
Coordinate> {
   @Override
   protected Iterator<Coordinate> valueIterator() {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_VALUES)) {
+        PreparedStatement statement = 
connection.prepareStatement(selectValues)) {
       ResultSet result = statement.executeQuery();
       return new PostgresIterator<>(result, this::value);
     } catch (SQLException e) {
@@ -163,7 +185,7 @@ public class PostgresCoordinateMap extends DataMap<Long, 
Coordinate> {
   @Override
   protected Iterator<Entry<Long, Coordinate>> entryIterator() {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_ENTRIES)) {
+        PreparedStatement statement = 
connection.prepareStatement(selectEntries)) {
       ResultSet result = statement.executeQuery();
       return new PostgresIterator<>(result, this::entry);
     } catch (SQLException e) {
@@ -185,7 +207,7 @@ public class PostgresCoordinateMap extends DataMap<Long, 
Coordinate> {
   @Override
   public long sizeAsLong() {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_SIZE)) {
+        PreparedStatement statement = connection.prepareStatement(selectSize)) 
{
       try (ResultSet result = statement.executeQuery()) {
         if (result.next()) {
           return result.getLong(1);
@@ -201,7 +223,7 @@ public class PostgresCoordinateMap extends DataMap<Long, 
Coordinate> {
   @Override
   public boolean containsKey(Object key) {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_CONTAINS_KEY)) {
+        PreparedStatement statement = 
connection.prepareStatement(selectContainsKey)) {
       statement.setLong(1, (Long) key);
       try (ResultSet result = statement.executeQuery()) {
         return result.next();
@@ -214,7 +236,7 @@ public class PostgresCoordinateMap extends DataMap<Long, 
Coordinate> {
   @Override
   public boolean containsValue(Object value) {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_CONTAINS_VALUE)) {
+        PreparedStatement statement = 
connection.prepareStatement(selectContainsValue)) {
       statement.setArray(1, connection.createArrayOf("int8", (Long[]) value));
       try (ResultSet result = statement.executeQuery()) {
         return result.next();
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresHeaderRepository.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresHeaderRepository.java
index 08d076da..be6a6be5 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresHeaderRepository.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresHeaderRepository.java
@@ -66,7 +66,8 @@ public class PostgresHeaderRepository implements 
HeaderRepository {
    * @param dataSource
    */
   public PostgresHeaderRepository(DataSource dataSource) {
-    this(dataSource, "osm_headers", "replication_sequence_number", 
"replication_timestamp",
+    this(dataSource, "public", "osm_headers", "replication_sequence_number",
+        "replication_timestamp",
         "replication_url", "source", "writing_program");
   }
 
@@ -74,16 +75,18 @@ public class PostgresHeaderRepository implements 
HeaderRepository {
    * Constructs a {@code PostgresHeaderRepository} with custom parameters.
    *
    * @param dataSource
-   * @param tableName
+   * @param schema
+   * @param table
    * @param replicationSequenceNumberColumn
    * @param replicationTimestampColumn
    * @param replicationUrlColumn
    * @param sourceColumn
    * @param writingProgramColumn
    */
-  public PostgresHeaderRepository(DataSource dataSource, String tableName,
+  public PostgresHeaderRepository(DataSource dataSource, String schema, String 
table,
       String replicationSequenceNumberColumn, String 
replicationTimestampColumn,
       String replicationUrlColumn, String sourceColumn, String 
writingProgramColumn) {
+    var fullTableName = String.format("%1$s.%2$s", schema, table);
     this.dataSource = dataSource;
     this.createTable = String.format("""
         CREATE TABLE IF NOT EXISTS %1$s (
@@ -92,20 +95,21 @@ public class PostgresHeaderRepository implements 
HeaderRepository {
           %4$s text,
           %5$s text,
           %6$s text
-        )""", tableName, replicationSequenceNumberColumn, 
replicationTimestampColumn,
+        )""", fullTableName, replicationSequenceNumberColumn, 
replicationTimestampColumn,
         replicationUrlColumn, sourceColumn, writingProgramColumn);
-    this.dropTable = String.format("DROP TABLE IF EXISTS %1$s CASCADE", 
tableName);
-    this.truncateTable = String.format("TRUNCATE TABLE %1$s", tableName);
+    this.dropTable = String.format("DROP TABLE IF EXISTS %1$s CASCADE", 
fullTableName);
+    this.truncateTable = String.format("TRUNCATE TABLE %1$s", fullTableName);
     this.selectLatest =
-        String.format("SELECT %2$s, %3$s, %4$s, %5$s, %6$s FROM %1$s ORDER BY 
%2$s DESC", tableName,
+        String.format("SELECT %2$s, %3$s, %4$s, %5$s, %6$s FROM %1$s ORDER BY 
%2$s DESC",
+            fullTableName,
             replicationSequenceNumberColumn, replicationTimestampColumn, 
replicationUrlColumn,
             sourceColumn, writingProgramColumn);
     this.select = String.format("SELECT %2$s, %3$s, %4$s, %5$s, %6$s FROM %1$s 
WHERE %2$s = ?",
-        tableName, replicationSequenceNumberColumn, replicationTimestampColumn,
+        fullTableName, replicationSequenceNumberColumn, 
replicationTimestampColumn,
         replicationUrlColumn, sourceColumn, writingProgramColumn);
     this.selectIn =
         String.format("SELECT %2$s, %3$s, %4$s, %5$s, %6$s FROM %1$s WHERE 
%2$s = ANY (?)",
-            tableName, replicationSequenceNumberColumn, 
replicationTimestampColumn,
+            fullTableName, replicationSequenceNumberColumn, 
replicationTimestampColumn,
             replicationUrlColumn, sourceColumn, writingProgramColumn);
     this.insert = String.format("""
         INSERT INTO %1$s (%2$s, %3$s, %4$s, %5$s, %6$s)
@@ -114,12 +118,12 @@ public class PostgresHeaderRepository implements 
HeaderRepository {
         %3$s = excluded.%3$s,
         %4$s = excluded.%4$s,
         %5$s = excluded.%5$s,
-        %6$s = excluded.%6$s""", tableName, replicationSequenceNumberColumn,
+        %6$s = excluded.%6$s""", fullTableName, 
replicationSequenceNumberColumn,
         replicationTimestampColumn, replicationUrlColumn, sourceColumn, 
writingProgramColumn);
-    this.delete = String.format("DELETE FROM %1$s WHERE %2$s = ?", tableName,
+    this.delete = String.format("DELETE FROM %1$s WHERE %2$s = ?", 
fullTableName,
         replicationSequenceNumberColumn);
     this.copy = String.format("COPY %1$s (%2$s, %3$s, %4$s, %5$s, %6$s) FROM 
STDIN BINARY",
-        tableName, replicationSequenceNumberColumn, replicationTimestampColumn,
+        fullTableName, replicationSequenceNumberColumn, 
replicationTimestampColumn,
         replicationUrlColumn, sourceColumn, writingProgramColumn);
   }
 
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresNodeRepository.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresNodeRepository.java
index e54f22e4..4b0a73cc 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresNodeRepository.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresNodeRepository.java
@@ -39,10 +39,14 @@ import org.apache.baremaps.utils.GeometryUtils;
 import org.locationtech.jts.geom.Geometry;
 import org.postgresql.PGConnection;
 import org.postgresql.copy.PGCopyOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /** Provides an implementation of the {@code NodeRepository} baked by 
Postgres. */
 public class PostgresNodeRepository implements NodeRepository {
 
+  private static final Logger logger = 
LoggerFactory.getLogger(PostgresNodeRepository.class);
+
   private final DataSource dataSource;
 
   private final String createTable;
@@ -69,7 +73,8 @@ public class PostgresNodeRepository implements NodeRepository 
{
    * @param dataSource
    */
   public PostgresNodeRepository(DataSource dataSource) {
-    this(dataSource, "osm_nodes", "id", "version", "uid", "timestamp", 
"changeset", "tags", "lon",
+    this(dataSource, "public", "osm_nodes", "id", "version", "uid", 
"timestamp", "changeset",
+        "tags", "lon",
         "lat", "geom");
   }
 
@@ -77,7 +82,8 @@ public class PostgresNodeRepository implements NodeRepository 
{
    * Constructs a {@code PostgresNodeRepository} with custom parameters.
    *
    * @param dataSource
-   * @param tableName
+   * @param schema
+   * @param table
    * @param idColumn
    * @param versionColumn
    * @param uidColumn
@@ -88,9 +94,10 @@ public class PostgresNodeRepository implements 
NodeRepository {
    * @param latitudeColumn
    * @param geometryColumn
    */
-  public PostgresNodeRepository(DataSource dataSource, String tableName, 
String idColumn,
+  public PostgresNodeRepository(DataSource dataSource, String schema, String 
table, String idColumn,
       String versionColumn, String uidColumn, String timestampColumn, String 
changesetColumn,
       String tagsColumn, String longitudeColumn, String latitudeColumn, String 
geometryColumn) {
+    var fullTableName = String.format("%1$s.%2$s", schema, table);
     this.dataSource = dataSource;
     this.createTable = String.format("""
         CREATE TABLE IF NOT EXISTS %1$s
@@ -104,17 +111,19 @@ public class PostgresNodeRepository implements 
NodeRepository {
             %8$s float,
             %9$s float,
             %10$s geometry(point)
-        )""", tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        )""", fullTableName, idColumn, versionColumn, uidColumn, 
timestampColumn, changesetColumn,
         tagsColumn, longitudeColumn, latitudeColumn, geometryColumn);
-    this.dropTable = String.format("DROP TABLE IF EXISTS %1$s CASCADE", 
tableName);
-    this.truncateTable = String.format("TRUNCATE TABLE %1$s", tableName);
+    this.dropTable = String.format("DROP TABLE IF EXISTS %1$s CASCADE", 
fullTableName);
+    this.truncateTable = String.format("TRUNCATE TABLE %1$s", fullTableName);
     this.select = String.format(
         "SELECT %2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, 
st_asbinary(%10$s) FROM %1$s WHERE %2$s = ?",
-        tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn, tagsColumn,
+        fullTableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        tagsColumn,
         longitudeColumn, latitudeColumn, geometryColumn);
     this.selectIn = String.format(
         "SELECT %2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, 
st_asbinary(%10$s) FROM %1$s WHERE %2$s = ANY (?)",
-        tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn, tagsColumn,
+        fullTableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        tagsColumn,
         longitudeColumn, latitudeColumn, geometryColumn);
     this.insert = String.format(
         "INSERT INTO %1$s (%2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, 
%10$s) "
@@ -123,13 +132,15 @@ public class PostgresNodeRepository implements 
NodeRepository {
             + "%4$s = excluded.%4$s, " + "%5$s = excluded.%5$s, " + "%6$s = 
excluded.%6$s, "
             + "%7$s = excluded.%7$s, " + "%8$s = excluded.%8$s, " + "%9$s = 
excluded.%9$s, "
             + "%10$s = excluded.%10$s",
-        tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn, tagsColumn,
+        fullTableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        tagsColumn,
         longitudeColumn, latitudeColumn, geometryColumn);
-    this.delete = String.format("DELETE FROM %1$s WHERE %2$s = ?", tableName, 
idColumn);
-    this.deleteIn = String.format("DELETE FROM %1$s WHERE %2$s = ANY (?)", 
tableName, idColumn);
+    this.delete = String.format("DELETE FROM %1$s WHERE %2$s = ?", 
fullTableName, idColumn);
+    this.deleteIn = String.format("DELETE FROM %1$s WHERE %2$s = ANY (?)", 
fullTableName, idColumn);
     this.copy = String.format(
         "COPY %1$s (%2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, %10$s) 
FROM STDIN BINARY",
-        tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn, tagsColumn,
+        fullTableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        tagsColumn,
         longitudeColumn, latitudeColumn, geometryColumn);
   }
 
@@ -138,6 +149,7 @@ public class PostgresNodeRepository implements 
NodeRepository {
   public void create() throws RepositoryException {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = 
connection.prepareStatement(createTable)) {
+      logger.trace("Creating table: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
@@ -149,6 +161,7 @@ public class PostgresNodeRepository implements 
NodeRepository {
   public void drop() throws RepositoryException {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(dropTable)) {
+      logger.trace("Dropping table: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
@@ -160,6 +173,7 @@ public class PostgresNodeRepository implements 
NodeRepository {
   public void truncate() throws RepositoryException {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = 
connection.prepareStatement(truncateTable)) {
+      logger.trace("Truncating table: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
@@ -172,6 +186,7 @@ public class PostgresNodeRepository implements 
NodeRepository {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(select)) {
       statement.setObject(1, key);
+      logger.trace("Selecting node: {}", statement);
       try (ResultSet result = statement.executeQuery()) {
         if (result.next()) {
           return getValue(result);
@@ -193,6 +208,7 @@ public class PostgresNodeRepository implements 
NodeRepository {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(selectIn)) {
       statement.setArray(1, connection.createArrayOf("int8", keys.toArray()));
+      logger.trace("Selecting nodes: {}", statement);
       try (ResultSet result = statement.executeQuery()) {
         Map<Long, Node> values = new HashMap<>();
         while (result.next()) {
@@ -212,6 +228,7 @@ public class PostgresNodeRepository implements 
NodeRepository {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(insert)) {
       setValue(statement, value);
+      logger.trace("Inserting node: {}", statement);
       statement.execute();
     } catch (SQLException | JsonProcessingException e) {
       throw new RepositoryException(e);
@@ -229,6 +246,7 @@ public class PostgresNodeRepository implements 
NodeRepository {
       for (Node value : values) {
         statement.clearParameters();
         setValue(statement, value);
+        logger.trace("Inserting node: {}", statement);
         statement.addBatch();
       }
       statement.executeBatch();
@@ -243,6 +261,7 @@ public class PostgresNodeRepository implements 
NodeRepository {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(delete)) {
       statement.setObject(1, key);
+      logger.trace("Deleting node: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
@@ -258,6 +277,7 @@ public class PostgresNodeRepository implements 
NodeRepository {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(deleteIn)) {
       statement.setArray(1, connection.createArrayOf("int8", keys.toArray()));
+      logger.trace("Deleting nodes: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresReferenceMap.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresReferenceMap.java
index 87eb0574..08b86c6b 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresReferenceMap.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresReferenceMap.java
@@ -34,60 +34,79 @@ import org.apache.baremaps.database.collection.DataMap;
  */
 public class PostgresReferenceMap extends DataMap<Long, List<Long>> {
 
-  public static final String SELECT_CONTAINS_KEY = """
-      SELECT 1
-      FROM osm_ways
-      WHERE id = ? LIMIT 1""";
-
-  public static final String SELECT_CONTAINS_VALUE = """
-      SELECT 1
-      FROM osm_ways
-      WHERE nodes = ? LIMIT 1""";
-
-  private static final String SELECT_IN = """
-      SELECT id, nodes
-      FROM osm_ways
-      WHERE id
-      WHERE id = ANY (?)""";
-
-  private static final String SELECT_BY_ID = """
-      SELECT nodes
-      FROM osm_ways
-      WHERE id = ?""";
-
-  private static final String SELECT_SIZE = """
-      SELECT count()
-      FROM osm_ways
-      """;
-
-  private static final String SELECT_KEYS = """
-      SELECT id
-      FROM osm_ways
-      """;
-
-  private static final String SELECT_VALUES = """
-      SELECT nodes
-      FROM osm_ways
-      """;
-
-  private static final String SELECT_ENTRIES = """
-      SELECT id, nodes
-      FROM osm_ways
-      """;
-
   private final DataSource dataSource;
 
+  public final String selectContainsKey;
+
+  public final String selectContainsValue;
+
+  private final String selectIn;
+
+  private final String selectById;
+
+  private final String selectSize;
+
+  private final String selectKeys;
+
+  private final String selectValues;
+
+  private final String selectEntries;
+
   /**
    * Constructs a {@code PostgresReferenceMap}.
    */
   public PostgresReferenceMap(DataSource dataSource) {
+    this(dataSource, "public", "osm_ways");
+  }
+
+  /**
+   * Constructs a {@code PostgresReferenceMap}.
+   */
+  public PostgresReferenceMap(DataSource dataSource, String schema, String 
table) {
     this.dataSource = dataSource;
+    var fullTableName = String.format("%s.%s", schema, table);
+    this.selectContainsKey = String.format("""
+        SELECT 1
+        FROM %1$s
+        WHERE id = ? LIMIT 1
+        """, fullTableName);
+    this.selectContainsValue = String.format("""
+        SELECT 1
+        FROM %1$s
+        WHERE nodes = ? LIMIT 1
+        """, fullTableName);
+    this.selectIn = String.format("""
+        SELECT id, nodes
+        FROM %1$s
+        WHERE id = ANY (?)
+        """, fullTableName);
+    this.selectById = String.format("""
+        SELECT nodes
+        FROM %1$s
+        WHERE id = ?
+        """, fullTableName);
+    this.selectSize = String.format("""
+        SELECT count()
+        FROM %1$s
+        """, fullTableName);
+    this.selectKeys = String.format("""
+        SELECT id
+        FROM %1$s
+        """, fullTableName);
+    this.selectValues = String.format("""
+        SELECT nodes
+        FROM %1$s
+        """, fullTableName);
+    this.selectEntries = String.format("""
+        SELECT id, nodes
+        FROM %1$s
+        """, fullTableName);
   }
 
   @Override
   public long sizeAsLong() {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_SIZE)) {
+        PreparedStatement statement = connection.prepareStatement(selectSize)) 
{
       try (ResultSet result = statement.executeQuery()) {
         if (result.next()) {
           return result.getLong(1);
@@ -103,7 +122,7 @@ public class PostgresReferenceMap extends DataMap<Long, 
List<Long>> {
   @Override
   public boolean containsKey(Object key) {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_CONTAINS_KEY)) {
+        PreparedStatement statement = 
connection.prepareStatement(selectContainsKey)) {
       statement.setLong(1, (Long) key);
       try (ResultSet result = statement.executeQuery()) {
         return result.next();
@@ -116,7 +135,7 @@ public class PostgresReferenceMap extends DataMap<Long, 
List<Long>> {
   @Override
   public boolean containsValue(Object value) {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_CONTAINS_VALUE)) {
+        PreparedStatement statement = 
connection.prepareStatement(selectContainsValue)) {
       statement.setArray(1, connection.createArrayOf("int8", (Long[]) value));
       try (ResultSet result = statement.executeQuery()) {
         return result.next();
@@ -132,7 +151,7 @@ public class PostgresReferenceMap extends DataMap<Long, 
List<Long>> {
   @Override
   public List<Long> get(Object key) {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_BY_ID)) {
+        PreparedStatement statement = connection.prepareStatement(selectById)) 
{
       statement.setLong(1, (Long) key);
       try (ResultSet result = statement.executeQuery()) {
         if (result.next()) {
@@ -153,7 +172,7 @@ public class PostgresReferenceMap extends DataMap<Long, 
List<Long>> {
   @Override
   public List<List<Long>> getAll(List<Long> keys) {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = connection.prepareStatement(SELECT_IN)) {
+        PreparedStatement statement = connection.prepareStatement(selectIn)) {
       statement.setArray(1, connection.createArrayOf("int8", keys.toArray()));
       try (ResultSet result = statement.executeQuery()) {
         Map<Long, List<Long>> referenceMap = new HashMap<>();
@@ -173,7 +192,7 @@ public class PostgresReferenceMap extends DataMap<Long, 
List<Long>> {
   @Override
   protected Iterator<Long> keyIterator() {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_KEYS)) {
+        PreparedStatement statement = connection.prepareStatement(selectKeys)) 
{
       ResultSet result = statement.executeQuery();
       return new PostgresIterator<>(result, this::key);
     } catch (SQLException e) {
@@ -192,7 +211,7 @@ public class PostgresReferenceMap extends DataMap<Long, 
List<Long>> {
   @Override
   protected Iterator<List<Long>> valueIterator() {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_VALUES)) {
+        PreparedStatement statement = 
connection.prepareStatement(selectValues)) {
       ResultSet result = statement.executeQuery();
       return new PostgresIterator<>(result, this::value);
     } catch (SQLException e) {
@@ -212,7 +231,7 @@ public class PostgresReferenceMap extends DataMap<Long, 
List<Long>> {
   @Override
   protected Iterator<Entry<Long, List<Long>>> entryIterator() {
     try (Connection connection = dataSource.getConnection();
-        PreparedStatement statement = 
connection.prepareStatement(SELECT_ENTRIES)) {
+        PreparedStatement statement = 
connection.prepareStatement(selectEntries)) {
       ResultSet result = statement.executeQuery();
       return new PostgresIterator<>(result, this::entry);
     } catch (SQLException e) {
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresRelationRepository.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresRelationRepository.java
index 3e48679e..32f4055e 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresRelationRepository.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresRelationRepository.java
@@ -68,7 +68,8 @@ public class PostgresRelationRepository implements 
RelationRepository {
    * @param dataSource
    */
   public PostgresRelationRepository(DataSource dataSource) {
-    this(dataSource, "osm_relations", "id", "version", "uid", "timestamp", 
"changeset", "tags",
+    this(dataSource, "public", "osm_relations", "id", "version", "uid", 
"timestamp", "changeset",
+        "tags",
         "member_refs", "member_types", "member_roles", "geom");
   }
 
@@ -76,7 +77,8 @@ public class PostgresRelationRepository implements 
RelationRepository {
    * Constructs a {@code PostgresRelationRepository} with custom parameters.
    *
    * @param dataSource
-   * @param tableName
+   * @param schema
+   * @param table
    * @param idColumn
    * @param versionColumn
    * @param uidColumn
@@ -88,10 +90,12 @@ public class PostgresRelationRepository implements 
RelationRepository {
    * @param memberRoles
    * @param geometryColumn
    */
-  public PostgresRelationRepository(DataSource dataSource, String tableName, 
String idColumn,
+  public PostgresRelationRepository(DataSource dataSource, String schema, 
String table,
+      String idColumn,
       String versionColumn, String uidColumn, String timestampColumn, String 
changesetColumn,
       String tagsColumn, String memberRefs, String memberTypes, String 
memberRoles,
       String geometryColumn) {
+    var fullTableName = String.format("%1$s.%2$s", schema, table);
     this.dataSource = dataSource;
     this.createTable = String.format("""
         CREATE TABLE IF NOT EXISTS %1$s (
@@ -105,17 +109,19 @@ public class PostgresRelationRepository implements 
RelationRepository {
           %9$s int[],
           %10$s text[],
           %11$s geometry
-        )""", tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        )""", fullTableName, idColumn, versionColumn, uidColumn, 
timestampColumn, changesetColumn,
         tagsColumn, memberRefs, memberTypes, memberRoles, geometryColumn);
-    this.dropTable = String.format("DROP TABLE IF EXISTS %1$s CASCADE", 
tableName);
-    this.truncateTable = String.format("TRUNCATE TABLE %1$s", tableName);
+    this.dropTable = String.format("DROP TABLE IF EXISTS %1$s CASCADE", 
fullTableName);
+    this.truncateTable = String.format("TRUNCATE TABLE %1$s", fullTableName);
     this.select = String.format(
         "SELECT %2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, %10$s, 
st_asbinary(%11$s) FROM %1$s WHERE %2$s = ?",
-        tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn, tagsColumn,
+        fullTableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        tagsColumn,
         memberRefs, memberTypes, memberRoles, geometryColumn);
     this.selectIn = String.format(
         "SELECT %2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, %10$s, 
st_asbinary(%11$s) FROM %1$s WHERE %2$s = ANY (?)",
-        tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn, tagsColumn,
+        fullTableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        tagsColumn,
         memberRefs, memberTypes, memberRoles, geometryColumn);
     this.insert = String.format("""
         INSERT INTO %1$s (%2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, 
%10$s, %11$s)
@@ -129,13 +135,15 @@ public class PostgresRelationRepository implements 
RelationRepository {
         %8$s = excluded.%8$s,
         %9$s = excluded.%9$s,
         %10$s = excluded.%10$s,
-        %11$s = excluded.%11$s""", tableName, idColumn, versionColumn, 
uidColumn, timestampColumn,
+        %11$s = excluded.%11$s""", fullTableName, idColumn, versionColumn, 
uidColumn,
+        timestampColumn,
         changesetColumn, tagsColumn, memberRefs, memberTypes, memberRoles, 
geometryColumn);
-    this.delete = String.format("DELETE FROM %1$s WHERE %2$s = ?", tableName, 
idColumn);
-    this.deleteIn = String.format("DELETE FROM %1$s WHERE %2$s = ANY (?)", 
tableName, idColumn);
+    this.delete = String.format("DELETE FROM %1$s WHERE %2$s = ?", 
fullTableName, idColumn);
+    this.deleteIn = String.format("DELETE FROM %1$s WHERE %2$s = ANY (?)", 
fullTableName, idColumn);
     this.copy = String.format(
         "COPY %1$s (%2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s, %10$s, 
%11$s) FROM STDIN BINARY",
-        tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn, tagsColumn,
+        fullTableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        tagsColumn,
         memberRefs, memberTypes, memberRoles, geometryColumn);
   }
 
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresWayRepository.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresWayRepository.java
index 0e8d6124..5af4a1c9 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresWayRepository.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/postgres/PostgresWayRepository.java
@@ -42,10 +42,14 @@ import org.apache.baremaps.utils.GeometryUtils;
 import org.locationtech.jts.geom.Geometry;
 import org.postgresql.PGConnection;
 import org.postgresql.copy.PGCopyOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /** Provides an implementation of the {@code WayRepository} baked by Postgres. 
*/
 public class PostgresWayRepository implements WayRepository {
 
+  private static final Logger logger = 
LoggerFactory.getLogger(PostgresWayRepository.class);
+
   private final DataSource dataSource;
 
   private final String createTable;
@@ -72,7 +76,8 @@ public class PostgresWayRepository implements WayRepository {
    * @param dataSource the datasource
    */
   public PostgresWayRepository(DataSource dataSource) {
-    this(dataSource, "osm_ways", "id", "version", "uid", "timestamp", 
"changeset", "tags", "nodes",
+    this(dataSource, "public", "osm_ways", "id", "version", "uid", 
"timestamp", "changeset", "tags",
+        "nodes",
         "geom");
   }
 
@@ -80,7 +85,8 @@ public class PostgresWayRepository implements WayRepository {
    * Constructs a {@code PostgresWayRepository} with custom parameters.
    *
    * @param dataSource
-   * @param tableName
+   * @param schema
+   * @param table
    * @param idColumn
    * @param versionColumn
    * @param uidColumn
@@ -90,9 +96,10 @@ public class PostgresWayRepository implements WayRepository {
    * @param nodesColumn
    * @param geometryColumn
    */
-  public PostgresWayRepository(DataSource dataSource, String tableName, String 
idColumn,
+  public PostgresWayRepository(DataSource dataSource, String schema, String 
table, String idColumn,
       String versionColumn, String uidColumn, String timestampColumn, String 
changesetColumn,
       String tagsColumn, String nodesColumn, String geometryColumn) {
+    var fullTableName = String.format("%1$s.%2$s", schema, table);
     this.dataSource = dataSource;
     this.createTable = String.format("""
         CREATE TABLE IF NOT EXISTS %1$s (
@@ -104,17 +111,19 @@ public class PostgresWayRepository implements 
WayRepository {
           %7$s jsonb,
           %8$s int8[],
           %9$s geometry
-        )""", tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        )""", fullTableName, idColumn, versionColumn, uidColumn, 
timestampColumn, changesetColumn,
         tagsColumn, nodesColumn, geometryColumn);
-    this.dropTable = String.format("DROP TABLE IF EXISTS %1$s CASCADE", 
tableName);
-    this.truncateTable = String.format("TRUNCATE TABLE %1$s", tableName);
+    this.dropTable = String.format("DROP TABLE IF EXISTS %1$s CASCADE", 
fullTableName);
+    this.truncateTable = String.format("TRUNCATE TABLE %1$s", fullTableName);
     this.select = String.format(
         "SELECT %2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, st_asbinary(%9$s) 
FROM %1$s WHERE %2$s = ?",
-        tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn, tagsColumn,
+        fullTableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        tagsColumn,
         nodesColumn, geometryColumn);
     this.selectIn = String.format(
         "SELECT %2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, st_asbinary(%9$s) 
FROM %1$s WHERE %2$s = ANY (?)",
-        tableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn, tagsColumn,
+        fullTableName, idColumn, versionColumn, uidColumn, timestampColumn, 
changesetColumn,
+        tagsColumn,
         nodesColumn, geometryColumn);
     this.insert = String.format("""
         INSERT INTO %1$s (%2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s)
@@ -126,12 +135,13 @@ public class PostgresWayRepository implements 
WayRepository {
         %6$s = excluded.%6$s,
         %7$s = excluded.%7$s,
         %8$s = excluded.%8$s,
-        %9$s = excluded.%9$s""", tableName, idColumn, versionColumn, 
uidColumn, timestampColumn,
+        %9$s = excluded.%9$s""", fullTableName, idColumn, versionColumn, 
uidColumn, timestampColumn,
         changesetColumn, tagsColumn, nodesColumn, geometryColumn);
-    this.delete = String.format("DELETE FROM %1$s WHERE %2$s = ?", tableName, 
idColumn);
-    this.deleteIn = String.format("DELETE FROM %1$s WHERE %2$s = ANY (?)", 
tableName, idColumn);
+    this.delete = String.format("DELETE FROM %1$s WHERE %2$s = ?", 
fullTableName, idColumn);
+    this.deleteIn = String.format("DELETE FROM %1$s WHERE %2$s = ANY (?)", 
fullTableName, idColumn);
     this.copy = String.format(
-        "COPY %1$s (%2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s) FROM STDIN 
BINARY", tableName,
+        "COPY %1$s (%2$s, %3$s, %4$s, %5$s, %6$s, %7$s, %8$s, %9$s) FROM STDIN 
BINARY",
+        fullTableName,
         idColumn, versionColumn, uidColumn, timestampColumn, changesetColumn, 
tagsColumn,
         nodesColumn, geometryColumn);
   }
@@ -141,6 +151,7 @@ public class PostgresWayRepository implements WayRepository 
{
   public void create() throws RepositoryException {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = 
connection.prepareStatement(createTable)) {
+      logger.trace("Creating table: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
@@ -152,6 +163,7 @@ public class PostgresWayRepository implements WayRepository 
{
   public void drop() throws RepositoryException {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(dropTable)) {
+      logger.trace("Dropping table: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
@@ -163,6 +175,7 @@ public class PostgresWayRepository implements WayRepository 
{
   public void truncate() throws RepositoryException {
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = 
connection.prepareStatement(truncateTable)) {
+      logger.trace("Truncating table: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
@@ -175,6 +188,7 @@ public class PostgresWayRepository implements WayRepository 
{
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(select)) {
       statement.setObject(1, key);
+      logger.trace("Selecting way: {}", statement);
       try (ResultSet result = statement.executeQuery()) {
         if (result.next()) {
           return getValue(result);
@@ -196,6 +210,7 @@ public class PostgresWayRepository implements WayRepository 
{
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(selectIn)) {
       statement.setArray(1, connection.createArrayOf("int8", keys.toArray()));
+      logger.trace("Selecting ways: {}", statement);
       try (ResultSet result = statement.executeQuery()) {
         Map<Long, Way> values = new HashMap<>();
         while (result.next()) {
@@ -215,6 +230,7 @@ public class PostgresWayRepository implements WayRepository 
{
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(insert)) {
       setValue(statement, value);
+      logger.trace("Inserting way: {}", statement);
       statement.execute();
     } catch (SQLException | JsonProcessingException e) {
       throw new RepositoryException(e);
@@ -232,6 +248,7 @@ public class PostgresWayRepository implements WayRepository 
{
       for (Way value : values) {
         statement.clearParameters();
         setValue(statement, value);
+        logger.trace("Inserting way: {}", statement);
         statement.addBatch();
       }
       statement.executeBatch();
@@ -246,6 +263,7 @@ public class PostgresWayRepository implements WayRepository 
{
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(delete)) {
       statement.setObject(1, key);
+      logger.trace("Deleting way: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
@@ -261,6 +279,7 @@ public class PostgresWayRepository implements WayRepository 
{
     try (Connection connection = dataSource.getConnection();
         PreparedStatement statement = connection.prepareStatement(deleteIn)) {
       statement.setArray(1, connection.createArrayOf("int8", keys.toArray()));
+      logger.trace("Deleting ways: {}", statement);
       statement.execute();
     } catch (SQLException e) {
       throw new RepositoryException(e);
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/ChangeImporter.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/CopyChangeImporter.java
similarity index 97%
copy from 
baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/ChangeImporter.java
copy to 
baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/CopyChangeImporter.java
index afe16078..c84288a2 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/ChangeImporter.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/CopyChangeImporter.java
@@ -18,7 +18,6 @@
 package org.apache.baremaps.openstreetmap.repository;
 
 
-
 import java.util.function.Consumer;
 import org.apache.baremaps.openstreetmap.model.Change;
 import org.apache.baremaps.openstreetmap.model.Node;
@@ -28,9 +27,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /** A consumer for importing OpenStreetMap changes in a database. */
-public class ChangeImporter implements Consumer<Change> {
+public class CopyChangeImporter implements Consumer<Change> {
 
-  private static final Logger logger = 
LoggerFactory.getLogger(ChangeImporter.class);
+  private static final Logger logger = 
LoggerFactory.getLogger(CopyChangeImporter.class);
 
   private final Repository<Long, Node> nodeRepository;
   private final Repository<Long, Way> wayRepository;
@@ -43,7 +42,7 @@ public class ChangeImporter implements Consumer<Change> {
    * @param wayRepository the way table
    * @param relationRepository the relation table
    */
-  public ChangeImporter(
+  public CopyChangeImporter(
       Repository<Long, Node> nodeRepository,
       Repository<Long, Way> wayRepository,
       Repository<Long, Relation> relationRepository) {
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/ChangeImporter.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/PutChangeImporter.java
similarity index 89%
rename from 
baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/ChangeImporter.java
rename to 
baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/PutChangeImporter.java
index afe16078..9ff23dd2 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/ChangeImporter.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/repository/PutChangeImporter.java
@@ -28,9 +28,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /** A consumer for importing OpenStreetMap changes in a database. */
-public class ChangeImporter implements Consumer<Change> {
+public class PutChangeImporter implements Consumer<Change> {
 
-  private static final Logger logger = 
LoggerFactory.getLogger(ChangeImporter.class);
+  private static final Logger logger = 
LoggerFactory.getLogger(PutChangeImporter.class);
 
   private final Repository<Long, Node> nodeRepository;
   private final Repository<Long, Way> wayRepository;
@@ -43,7 +43,7 @@ public class ChangeImporter implements Consumer<Change> {
    * @param wayRepository the way table
    * @param relationRepository the relation table
    */
-  public ChangeImporter(
+  public PutChangeImporter(
       Repository<Long, Node> nodeRepository,
       Repository<Long, Way> wayRepository,
       Repository<Long, Relation> relationRepository) {
@@ -75,18 +75,15 @@ public class ChangeImporter implements Consumer<Change> {
         case CREATE, MODIFY -> {
           if (!nodes.isEmpty()) {
             logger.trace("Creating {} nodes", nodes.size());
-            nodeRepository.delete(nodeIds);
-            nodeRepository.copy(nodes);
+            nodeRepository.put(nodes);
           }
           if (!ways.isEmpty()) {
             logger.trace("Creating {} ways", ways.size());
-            wayRepository.delete(wayIds);
-            wayRepository.copy(ways);
+            wayRepository.put(ways);
           }
           if (!relations.isEmpty()) {
             logger.trace("Creating {} relations", relations.size());
-            relationRepository.delete(relationIds);
-            relationRepository.copy(relations);
+            relationRepository.put(relations);
           }
         }
         case DELETE -> {
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/state/StateReader.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/state/StateReader.java
index 9d8e2294..ef617103 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/state/StateReader.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/state/StateReader.java
@@ -18,27 +18,75 @@
 package org.apache.baremaps.openstreetmap.state;
 
 
-
 import com.google.common.io.CharStreams;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 import org.apache.baremaps.openstreetmap.model.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+/**
+ * Utility class for reading OSM state files. This code has been adapted from 
pyosmium (BSD 2-Clause
+ * "Simplified" License).
+ */
 public class StateReader {
 
+  private static final Logger logger = 
LoggerFactory.getLogger(StateReader.class);
+
+  private final String replicationUrl;
+
+  private final boolean balancedSearch;
+
+  private final int retries;
+
+  /**
+   * Constructs a {@code StateReader}.
+   */
+  public StateReader() {
+    this("https://planet.osm.org/replication/hour";, true, 2);
+  }
+
+  /**
+   * Constructs a {@code StateReader}.
+   *
+   * @param replicationUrl the replication URL
+   * @param balancedSearch whether to use a balanced search
+   */
+  public StateReader(String replicationUrl, boolean balancedSearch) {
+    this(replicationUrl, balancedSearch, 2);
+  }
+
+  /**
+   * Constructs a {@code StateReader}.
+   *
+   * @param replicationUrl the replication URL
+   * @param balancedSearch whether to use a balanced search
+   * @param retries the number of retries
+   */
+  public StateReader(String replicationUrl, boolean balancedSearch, int 
retries) {
+    this.replicationUrl = replicationUrl;
+    this.balancedSearch = balancedSearch;
+    this.retries = retries;
+  }
+
   /**
    * Parse an OSM state file.
    *
    * @param input the OpenStreetMap state file
    * @return the state
    */
-  public State state(InputStream input) throws IOException {
+  public State readState(InputStream input) throws IOException {
     InputStreamReader reader = new InputStreamReader(input, 
StandardCharsets.UTF_8);
     Map<String, String> map = new HashMap<>();
     for (String line : CharStreams.readLines(reader)) {
@@ -52,4 +100,138 @@ public class StateReader {
     LocalDateTime timestamp = 
LocalDateTime.parse(map.get("timestamp").replace("\\", ""), format);
     return new State(sequenceNumber, timestamp);
   }
+
+  /**
+   * Get the state corresponding to the given timestamp.
+   *
+   * @param timestamp the timestamp
+   * @return the state
+   */
+  public Optional<State> getStateFromTimestamp(LocalDateTime timestamp) {
+    var upper = getState(Optional.empty());
+    if (upper.isEmpty()) {
+      return Optional.empty();
+    }
+    if (timestamp.isAfter(upper.get().getTimestamp()) || 
upper.get().getSequenceNumber() <= 0) {
+      return upper;
+    }
+    var lower = Optional.<State>empty();
+    var lowerId = Optional.of(0L);
+    while (lower.isEmpty()) {
+      lower = getState(lowerId);
+      if (lower.isPresent() && lower.get().getTimestamp().isAfter(timestamp)) {
+        if (lower.get().getSequenceNumber() == 0
+            || lower.get().getSequenceNumber() + 1 >= 
upper.get().getSequenceNumber()) {
+          return lower;
+        }
+        upper = lower;
+        lower = Optional.empty();
+        lowerId = Optional.of(0L);
+      }
+      if (lower.isEmpty()) {
+        var newId = (lowerId.get() + upper.get().getSequenceNumber()) / 2;
+        if (newId <= lowerId.get()) {
+          return upper;
+        }
+        lowerId = Optional.of(newId);
+      }
+    }
+    long baseSplitId;
+    while (true) {
+      if (balancedSearch) {
+        baseSplitId = ((lower.get().getSequenceNumber() + 
upper.get().getSequenceNumber()) / 2);
+      } else {
+        var tsInt = upper.get().getTimestamp().toEpochSecond(ZoneOffset.UTC)
+            - lower.get().getTimestamp().toEpochSecond(ZoneOffset.UTC);
+        var seqInt = upper.get().getSequenceNumber() - 
lower.get().getSequenceNumber();
+        var goal = timestamp.getSecond() - 
lower.get().getTimestamp().getSecond();
+        baseSplitId = lower.get().getSequenceNumber() + (long) Math.ceil(goal 
* seqInt / tsInt);
+        if (baseSplitId >= upper.get().getSequenceNumber()) {
+          baseSplitId = upper.get().getSequenceNumber() - 1;
+        }
+      }
+      var split = getState(Optional.of(baseSplitId));
+      if (split.isEmpty()) {
+        var splitId = baseSplitId - 1;
+        while (split.isEmpty() && splitId > lower.get().getSequenceNumber()) {
+          split = getState(Optional.of(splitId));
+          splitId--;
+        }
+      }
+      if (split.isEmpty()) {
+        var splitId = baseSplitId + 1;
+        while (split.isEmpty() && splitId < upper.get().getSequenceNumber()) {
+          split = getState(Optional.of(splitId));
+          splitId++;
+        }
+      }
+      if (split.isEmpty()) {
+        return lower;
+      }
+      if (split.get().getTimestamp().isBefore(timestamp)) {
+        lower = split;
+      } else {
+        upper = split;
+      }
+      if (lower.get().getSequenceNumber() + 1 >= 
upper.get().getSequenceNumber()) {
+        return lower;
+      }
+    }
+  }
+
+  /**
+   * Get the state corresponding to the given sequence number.
+   *
+   * @param sequenceNumber the sequence number
+   * @return the state
+   */
+  public Optional<State> getState(Optional<Long> sequenceNumber) {
+    for (int i = 0; i < retries + 1; i++) {
+      try (var inputStream = getStateUrl(sequenceNumber).openStream()) {
+        var state = new StateReader().readState(inputStream);
+        return Optional.of(state);
+      } catch (Exception e) {
+        logger.error("Error while reading state file", e);
+      }
+    }
+    return Optional.empty();
+  }
+
+  /**
+   * Get the URL of the state file corresponding to the given sequence number.
+   *
+   * @param sequenceNumber the sequence number
+   * @return the URL
+   * @throws MalformedURLException if the URL is malformed
+   */
+  public URL getStateUrl(Optional<Long> sequenceNumber) throws 
MalformedURLException {
+    if (sequenceNumber.isPresent()) {
+
+      var s = String.format("%09d", sequenceNumber.get());
+      var uri =
+          String.format("%s/%s/%s/%s.%s", replicationUrl, s.substring(0, 3), 
s.substring(3, 6),
+              s.substring(6, 9), "state.txt");
+      return URI.create(uri).toURL();
+    } else {
+      return new URL(replicationUrl + "/state.txt");
+    }
+  }
+
+  /**
+   * Get the URL of a replication file.
+   *
+   * @param replicationUrl the replication URL
+   * @param sequenceNumber the sequence number
+   * @param extension the extension
+   * @return the URL
+   * @throws MalformedURLException if the URL is malformed
+   */
+  public URL getUrl(String replicationUrl, Long sequenceNumber, String 
extension)
+      throws MalformedURLException {
+    var s = String.format("%09d", sequenceNumber);
+    var uri = String.format("%s/%s/%s/%s.%s", replicationUrl, s.substring(0, 
3), s.substring(3, 6),
+        s.substring(6, 9), extension);
+    return URI.create(uri).toURL();
+  }
+
 }
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/postgres/PostgresDataSchema.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/postgres/PostgresDataSchema.java
index 242e03d9..55e6c741 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/postgres/PostgresDataSchema.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/postgres/PostgresDataSchema.java
@@ -261,19 +261,12 @@ public class PostgresDataSchema implements DataSchema {
       case LONG -> new LongValueHandler<Long>();
       case FLOAT -> new FloatValueHandler<Float>();
       case DOUBLE -> new DoubleValueHandler<Double>();
-      case GEOMETRY -> new GeometryValueHandler();
-      case POINT -> new GeometryValueHandler();
-      case MULTIPOINT -> new GeometryValueHandler();
-      case LINESTRING -> new GeometryValueHandler();
-      case MULTILINESTRING -> new GeometryValueHandler();
-      case POLYGON -> new GeometryValueHandler();
-      case MULTIPOLYGON -> new GeometryValueHandler();
-      case GEOMETRYCOLLECTION -> new GeometryValueHandler();
       case INET4_ADDRESS -> new Inet4AddressValueHandler();
       case INET6_ADDRESS -> new Inet6AddressValueHandler();
       case LOCAL_DATE -> new LocalDateValueHandler();
       case LOCAL_TIME -> new LocalTimeValueHandler();
       case LOCAL_DATE_TIME -> new LocalDateTimeValueHandler();
+      case GEOMETRY, POINT, MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON, 
MULTIPOLYGON, GEOMETRYCOLLECTION -> new GeometryValueHandler();
       default -> throw new IllegalArgumentException("Unsupported type: " + 
type);
     };
   }
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/shapefile/internal/ShapefileByteReader.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/shapefile/internal/ShapefileByteReader.java
index 62b3cc26..45af573e 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/shapefile/internal/ShapefileByteReader.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/shapefile/internal/ShapefileByteReader.java
@@ -327,10 +327,10 @@ public class ShapefileByteReader extends CommonByteReader 
{
    * @param row the row to fill.
    */
   private void loadPolygonRow(DataRow row) {
-    double xmin = getByteBuffer().getDouble();
-    double ymin = getByteBuffer().getDouble();
-    double xmax = getByteBuffer().getDouble();
-    double ymax = getByteBuffer().getDouble();
+    /* double xmin = */ getByteBuffer().getDouble();
+    /* double ymin = */ getByteBuffer().getDouble();
+    /* double xmax = */ getByteBuffer().getDouble();
+    /* double ymax = */ getByteBuffer().getDouble();
 
     int numParts = getByteBuffer().getInt();
     int numPoints = getByteBuffer().getInt();
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/ImportOsmOsc.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/ImportOsmOsc.java
index 84eef9ee..b16fff32 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/ImportOsmOsc.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/ImportOsmOsc.java
@@ -31,7 +31,7 @@ import org.apache.baremaps.database.type.PairDataType;
 import org.apache.baremaps.database.type.geometry.LonLatDataType;
 import org.apache.baremaps.openstreetmap.function.*;
 import org.apache.baremaps.openstreetmap.postgres.*;
-import org.apache.baremaps.openstreetmap.repository.ChangeImporter;
+import org.apache.baremaps.openstreetmap.repository.CopyChangeImporter;
 import org.apache.baremaps.openstreetmap.xml.XmlChangeReader;
 import org.apache.baremaps.utils.Compression;
 import org.apache.baremaps.utils.FileUtils;
@@ -100,7 +100,7 @@ public record ImportOsmOsc(
         .andThen(buildGeometry)
         .andThen(reprojectGeometry);
     var prepareChange = consumeThenReturn(new 
ChangeEntitiesHandler(prepareGeometries));
-    var importChange = new ChangeImporter(nodeRepository, wayRepository, 
relationRepository);
+    var importChange = new CopyChangeImporter(nodeRepository, wayRepository, 
relationRepository);
 
     try (var changeInputStream =
         new 
BufferedInputStream(compression.decompress(Files.newInputStream(path)))) {
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/UpdateOsmDatabase.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/UpdateOsmDatabase.java
index 0b195a27..1a2005d2 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/UpdateOsmDatabase.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/UpdateOsmDatabase.java
@@ -20,9 +20,6 @@ package org.apache.baremaps.workflow.tasks;
 import static org.apache.baremaps.stream.ConsumerUtils.consumeThenReturn;
 
 import java.io.BufferedInputStream;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
 import java.util.List;
 import java.util.zip.GZIPInputStream;
 import org.apache.baremaps.database.collection.DataMap;
@@ -40,7 +37,7 @@ import 
org.apache.baremaps.openstreetmap.postgres.PostgresReferenceMap;
 import org.apache.baremaps.openstreetmap.postgres.PostgresRelationRepository;
 import org.apache.baremaps.openstreetmap.postgres.PostgresWayRepository;
 import org.apache.baremaps.openstreetmap.repository.*;
-import org.apache.baremaps.openstreetmap.repository.ChangeImporter;
+import org.apache.baremaps.openstreetmap.repository.PutChangeImporter;
 import org.apache.baremaps.openstreetmap.state.StateReader;
 import org.apache.baremaps.openstreetmap.xml.XmlChangeReader;
 import org.apache.baremaps.workflow.Task;
@@ -49,10 +46,15 @@ import org.locationtech.jts.geom.Coordinate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public record UpdateOsmDatabase(Object database, Integer databaseSrid) 
implements Task {
+public record UpdateOsmDatabase(Object database, Integer databaseSrid,
+    String replicationUrl) implements Task {
 
   private static final Logger logger = 
LoggerFactory.getLogger(UpdateOsmDatabase.class);
 
+  public UpdateOsmDatabase(Object database, Integer databaseSrid) {
+    this(database, databaseSrid, null);
+  }
+
   @Override
   public void execute(WorkflowContext context) throws Exception {
     var datasource = context.getDataSource(database);
@@ -69,43 +71,57 @@ public record UpdateOsmDatabase(Object database, Integer 
databaseSrid) implement
         nodeRepository,
         wayRepository,
         relationRepository,
-        databaseSrid);
+        databaseSrid,
+        replicationUrl);
   }
 
   public static void execute(DataMap<Long, Coordinate> coordinateMap,
       DataMap<Long, List<Long>> referenceMap,
       HeaderRepository headerRepository, Repository<Long, Node> nodeRepository,
       Repository<Long, Way> wayRepository, Repository<Long, Relation> 
relationRepository,
-      int srid) throws Exception {
+      Integer databaseSrid,
+      String replicationUrl) throws Exception {
+
     var header = headerRepository.selectLatest();
-    var replicationUrl = header.getReplicationUrl();
-    var sequenceNumber = header.getReplicationSequenceNumber() + 1;
+
+    // If the replicationUrl is not provided, use the one from the latest 
header.
+    if (replicationUrl == null) {
+      replicationUrl = header.getReplicationUrl();
+    }
+
+    var stateReader = new StateReader(replicationUrl, true);
+    var sequenceNumber = header.getReplicationSequenceNumber();
+
+    // If the replicationTimestamp is not provided, guess it from the 
replication timestamp.
+    if (sequenceNumber <= 0) {
+      var replicationTimestamp = header.getReplicationTimestamp();
+      var state = stateReader.getStateFromTimestamp(replicationTimestamp);
+      if (state.isPresent()) {
+        sequenceNumber = state.get().getSequenceNumber();
+      }
+    }
+
+    var nextSequenceNumber = sequenceNumber + 1;
+    var changeUrl = stateReader.getUrl(replicationUrl, nextSequenceNumber, 
"osc.gz");
+    logger.info("Updating the database with the changeset: {}", changeUrl);
 
     var createGeometry = new EntityGeometryBuilder(coordinateMap, 
referenceMap);
-    var reprojectGeometry = new EntityProjectionTransformer(4326, srid);
+    var reprojectGeometry = new EntityProjectionTransformer(4326, 
databaseSrid);
     var prepareGeometries = new 
ChangeEntitiesHandler(createGeometry.andThen(reprojectGeometry));
     var prepareChange = consumeThenReturn(prepareGeometries);
-    var saveChange = new ChangeImporter(nodeRepository, wayRepository, 
relationRepository);
+    var importChange = new PutChangeImporter(nodeRepository, wayRepository, 
relationRepository);
 
-    var changeUrl = resolve(replicationUrl, sequenceNumber, "osc.gz");
     try (var changeInputStream =
         new GZIPInputStream(new BufferedInputStream(changeUrl.openStream()))) {
-      new 
XmlChangeReader().stream(changeInputStream).map(prepareChange).forEach(saveChange);
+      new 
XmlChangeReader().stream(changeInputStream).map(prepareChange).forEach(importChange);
     }
 
-    var stateUrl = resolve(replicationUrl, sequenceNumber, "state.txt");
+    var stateUrl = stateReader.getUrl(replicationUrl, nextSequenceNumber, 
"state.txt");
     try (var stateInputStream = new 
BufferedInputStream(stateUrl.openStream())) {
-      var state = new StateReader().state(stateInputStream);
+      var state = new StateReader().readState(stateInputStream);
       headerRepository.put(new Header(state.getSequenceNumber(), 
state.getTimestamp(),
           header.getReplicationUrl(), header.getSource(), 
header.getWritingProgram()));
     }
   }
 
-  public static URL resolve(String replicationUrl, Long sequenceNumber, String 
extension)
-      throws MalformedURLException {
-    var s = String.format("%09d", sequenceNumber);
-    var uri = String.format("%s/%s/%s/%s.%s", replicationUrl, s.substring(0, 
3), s.substring(3, 6),
-        s.substring(6, 9), extension);
-    return URI.create(uri).toURL();
-  }
 }
diff --git 
a/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/OpenStreetMapTest.java
 
b/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/OpenStreetMapTest.java
index ddb90f0c..c8d57544 100644
--- 
a/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/OpenStreetMapTest.java
+++ 
b/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/OpenStreetMapTest.java
@@ -123,7 +123,7 @@ class OpenStreetMapTest {
   @Test
   void monacoStateTxt() throws URISyntaxException, IOException {
     try (InputStream inputStream = Files.newInputStream(MONACO_STATE_TXT)) {
-      State state = new StateReader().state(inputStream);
+      State state = new StateReader().readState(inputStream);
       assertEquals(2788, state.getSequenceNumber());
       assertEquals(LocalDateTime.parse("2020-11-10T21:42:03"), 
state.getTimestamp());
     }
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/ChangeEntitiesHandler.java
 
b/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/state/StateReaderTest.java
similarity index 52%
copy from 
baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/ChangeEntitiesHandler.java
copy to 
baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/state/StateReaderTest.java
index 4d7ecf72..c8fab379 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/ChangeEntitiesHandler.java
+++ 
b/baremaps-core/src/test/java/org/apache/baremaps/openstreetmap/state/StateReaderTest.java
@@ -15,32 +15,21 @@
  * limitations under the License.
  */
 
-package org.apache.baremaps.openstreetmap.function;
+package org.apache.baremaps.openstreetmap.state;
 
+import static org.junit.jupiter.api.Assertions.*;
 
+import java.time.LocalDateTime;
+import org.junit.Ignore;
+import org.junit.jupiter.api.Test;
 
-import java.util.function.Consumer;
-import org.apache.baremaps.openstreetmap.model.Change;
-import org.apache.baremaps.openstreetmap.model.Entity;
+class StateReaderTest {
 
-/** Represents an operation on the entities of changes of different types. */
-public class ChangeEntitiesHandler implements Consumer<Change> {
-
-  private final Consumer<Entity> consumer;
-
-  /**
-   * Constructs a consumer that applies the specified consumer to all the 
entities of a {@code
-   * Change}.
-   *
-   * @param consumer
-   */
-  public ChangeEntitiesHandler(Consumer<Entity> consumer) {
-    this.consumer = consumer;
-  }
-
-  /** {@inheritDoc} */
-  @Override
-  public void accept(Change change) {
-    change.getEntities().forEach(consumer);
+  @Test
+  @Ignore
+  void getStateFromTimestamp() {
+    var reader = new StateReader();
+    var state = 
reader.getStateFromTimestamp(LocalDateTime.now().minusDays(10));
+    System.out.println(state.get().getSequenceNumber());
   }
 }
diff --git 
a/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportMonacoTest.java
 
b/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportMonacoTest.java
index 85796989..c9985d67 100644
--- 
a/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportMonacoTest.java
+++ 
b/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportMonacoTest.java
@@ -76,7 +76,7 @@ class ImportMonacoTest extends PostgresRepositoryTest {
       new DiffService(coordinateMap, referenceMap, headerRepository, 
nodeRepository, wayRepository,
           relationRepository, 3857, 14).call();
       UpdateOsmDatabase.execute(coordinateMap, referenceMap, headerRepository, 
nodeRepository,
-          wayRepository, relationRepository, 3857);
+          wayRepository, relationRepository, 3857, null);
       long nextReplicationSequenceNumber =
           headerRepository.selectLatest().getReplicationSequenceNumber();
       assertEquals(replicationSequenceNumber + 1, 
nextReplicationSequenceNumber);
diff --git 
a/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportUpdateDataTest.java
 
b/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportUpdateDataTest.java
index 60e517ab..7ccb43e1 100644
--- 
a/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportUpdateDataTest.java
+++ 
b/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportUpdateDataTest.java
@@ -64,7 +64,7 @@ class ImportUpdateDataTest extends PostgresRepositoryTest {
     ImportOsmPbf.execute(SIMPLE_DATA_OSM_PBF, coordinateMap, referenceMap, 
headerRepository,
         nodeRepository, wayRepository, relationRepository, 3857);
 
-    headerRepository.put(new Header(0l, LocalDateTime.of(2020, 1, 1, 0, 0, 0, 
0),
+    headerRepository.put(new Header(1l, LocalDateTime.of(2020, 1, 1, 0, 0, 0, 
0),
         "file:///" + SIMPLE_DATA_DIR, "", ""));
 
     // Check node importation
@@ -96,7 +96,7 @@ class ImportUpdateDataTest extends PostgresRepositoryTest {
     // Update the database
     UpdateOsmDatabase.execute(new PostgresCoordinateMap(dataSource()),
         new PostgresReferenceMap(dataSource()), headerRepository, 
nodeRepository, wayRepository,
-        relationRepository, 3857);
+        relationRepository, 3857, null);
 
     // Check deletions
     assertNull(nodeRepository.get(0l));
diff --git 
a/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportUpdateLiechtensteinTest.java
 
b/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportUpdateLiechtensteinTest.java
index dfc8585c..a8d44aa4 100644
--- 
a/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportUpdateLiechtensteinTest.java
+++ 
b/baremaps-core/src/test/java/org/apache/baremaps/workflow/tasks/ImportUpdateLiechtensteinTest.java
@@ -77,7 +77,7 @@ class ImportUpdateLiechtensteinTest extends 
PostgresRepositoryTest {
     // Update the database
     UpdateOsmDatabase.execute(coordinateMap, referenceMap, headerRepository, 
nodeRepository,
         wayRepository,
-        relationRepository, 3857);
+        relationRepository, 3857, null);
     assertEquals(2435l, 
headerRepository.selectLatest().getReplicationSequenceNumber());
 
     assertEquals(2, new DiffService(coordinateMap, referenceMap, 
headerRepository, nodeRepository,
@@ -85,7 +85,7 @@ class ImportUpdateLiechtensteinTest extends 
PostgresRepositoryTest {
 
     UpdateOsmDatabase.execute(coordinateMap, referenceMap, headerRepository, 
nodeRepository,
         wayRepository,
-        relationRepository, 3857);
+        relationRepository, 3857, null);
     assertEquals(2436l, 
headerRepository.selectLatest().getReplicationSequenceNumber());
 
     assertEquals(0, new DiffService(coordinateMap, referenceMap, 
headerRepository, nodeRepository,
@@ -93,7 +93,7 @@ class ImportUpdateLiechtensteinTest extends 
PostgresRepositoryTest {
 
     UpdateOsmDatabase.execute(coordinateMap, referenceMap, headerRepository, 
nodeRepository,
         wayRepository,
-        relationRepository, 3857);
+        relationRepository, 3857, null);
     assertEquals(2437l, 
headerRepository.selectLatest().getReplicationSequenceNumber());
   }
 }
diff --git a/baremaps-core/src/test/resources/simple/000/000/001.osc.gz 
b/baremaps-core/src/test/resources/simple/000/000/002.osc.gz
similarity index 100%
rename from baremaps-core/src/test/resources/simple/000/000/001.osc.gz
rename to baremaps-core/src/test/resources/simple/000/000/002.osc.gz
diff --git a/baremaps-core/src/test/resources/simple/000/000/001.state.txt 
b/baremaps-core/src/test/resources/simple/000/000/002.state.txt
similarity index 100%
copy from baremaps-core/src/test/resources/simple/000/000/001.state.txt
copy to baremaps-core/src/test/resources/simple/000/000/002.state.txt
diff --git a/baremaps-core/src/test/resources/simple/000/000/001.state.txt 
b/baremaps-core/src/test/resources/simple/state.txt
similarity index 100%
rename from baremaps-core/src/test/resources/simple/000/000/001.state.txt
rename to baremaps-core/src/test/resources/simple/state.txt
diff --git a/basemap/workflow.js b/basemap/import.js
similarity index 99%
rename from basemap/workflow.js
rename to basemap/import.js
index bf2f3c2d..fec1e00a 100644
--- a/basemap/workflow.js
+++ b/basemap/import.js
@@ -132,6 +132,7 @@ export default {
           "database": config.database,
           "databaseSrid": 3857,
           "replaceExisting": true,
+          "cleanCache": true,
         },
       ]
     },
@@ -348,118 +349,118 @@ export default {
       ]
     },
     {
-      "id": "openstreetmap-natural",
-      "needs": ["openstreetmap-polygon"],
+      "id": "openstreetmap-waterway",
+      "needs": [
+        "openstreetmap-linestring"
+      ],
       "tasks": [
         {
           "type": "ExecuteSql",
-          "file": "layers/natural/clean.sql",
+          "file": "layers/waterway/clean.sql",
           "database": config.database,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/natural/prepare.sql",
+          "file": "layers/waterway/prepare.sql",
           "database": config.database,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/natural/simplify.sql",
+          "file": "layers/waterway/simplify.sql",
           "database": config.database,
           "parallel": true,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/natural/index.sql",
+          "file": "layers/waterway/index.sql",
           "database": config.database,
           "parallel": true
         },
       ]
     },
     {
-      "id": "openstreetmap-landuse",
-      "needs": [
-          "openstreetmap-polygon"
-      ],
+      "id": "openstreetmap-natural",
+      "needs": ["openstreetmap-polygon"],
       "tasks": [
         {
           "type": "ExecuteSql",
-          "file": "layers/landuse/clean.sql",
+          "file": "layers/natural/clean.sql",
           "database": config.database,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/landuse/prepare.sql",
+          "file": "layers/natural/prepare.sql",
           "database": config.database,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/landuse/simplify.sql",
+          "file": "layers/natural/simplify.sql",
           "database": config.database,
           "parallel": true,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/landuse/index.sql",
+          "file": "layers/natural/index.sql",
           "database": config.database,
           "parallel": true
         },
       ]
     },
     {
-      "id": "openstreetmap-leisure",
+      "id": "openstreetmap-landuse",
       "needs": [
-        "openstreetmap-polygon"
+          "openstreetmap-polygon"
       ],
       "tasks": [
         {
           "type": "ExecuteSql",
-          "file": "layers/leisure/clean.sql",
+          "file": "layers/landuse/clean.sql",
           "database": config.database,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/leisure/prepare.sql",
+          "file": "layers/landuse/prepare.sql",
           "database": config.database,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/leisure/simplify.sql",
+          "file": "layers/landuse/simplify.sql",
           "database": config.database,
           "parallel": true,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/leisure/index.sql",
+          "file": "layers/landuse/index.sql",
           "database": config.database,
           "parallel": true
         },
       ]
     },
     {
-      "id": "openstreetmap-waterway",
+      "id": "openstreetmap-leisure",
       "needs": [
-          "openstreetmap-linestring"
+        "openstreetmap-polygon"
       ],
       "tasks": [
         {
           "type": "ExecuteSql",
-          "file": "layers/waterway/clean.sql",
+          "file": "layers/leisure/clean.sql",
           "database": config.database,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/waterway/prepare.sql",
+          "file": "layers/leisure/prepare.sql",
           "database": config.database,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/waterway/simplify.sql",
+          "file": "layers/leisure/simplify.sql",
           "database": config.database,
           "parallel": true,
         },
         {
           "type": "ExecuteSql",
-          "file": "layers/waterway/index.sql",
+          "file": "layers/leisure/index.sql",
           "database": config.database,
           "parallel": true
         },
diff --git a/basemap/layers/boundary/globaladm0_clean.sql 
b/basemap/layers/boundary/globaladm0_clean.sql
deleted file mode 100644
index a729669e..00000000
--- a/basemap/layers/boundary/globaladm0_clean.sql
+++ /dev/null
@@ -1,36 +0,0 @@
--- 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.
-DROP VIEW IF EXISTS globaladm0_z20 CASCADE;
-DROP VIEW IF EXISTS globaladm0_z19 CASCADE;
-DROP VIEW IF EXISTS globaladm0_z18 CASCADE;
-DROP VIEW IF EXISTS globaladm0_z17 CASCADE;
-DROP VIEW IF EXISTS globaladm0_z16 CASCADE;
-DROP VIEW IF EXISTS globaladm0_z15 CASCADE;
-DROP VIEW IF EXISTS globaladm0_z14 CASCADE;
-DROP VIEW IF EXISTS globaladm0_z13 CASCADE;
-
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z12 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z11 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z10 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z9 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z8 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z7 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z6 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z5 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z4 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z3 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z2 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z1 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm0_z0 CASCADE;
diff --git a/basemap/layers/boundary/globaladm0_index.sql 
b/basemap/layers/boundary/globaladm0_index.sql
deleted file mode 100644
index bfbeeb8d..00000000
--- a/basemap/layers/boundary/globaladm0_index.sql
+++ /dev/null
@@ -1,28 +0,0 @@
--- 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.
-CREATE INDEX IF NOT EXISTS globaladm0_index ON "globalADM0" USING SPGIST(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z12_index ON globaladm0_z12 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z11_index ON globaladm0_z11 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z10_index ON globaladm0_z10 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z9_index ON globaladm0_z9 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z8_index ON globaladm0_z8 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z7_index ON globaladm0_z7 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z6_index ON globaladm0_z6 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z5_index ON globaladm0_z5 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z4_index ON globaladm0_z4 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z3_index ON globaladm0_z3 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z2_index ON globaladm0_z2 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z1_index ON globaladm0_z1 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm0_z0_index ON globaladm0_z0 USING SPGIST 
(geom);
diff --git a/basemap/layers/boundary/globaladm0_simplify.sql 
b/basemap/layers/boundary/globaladm0_simplify.sql
deleted file mode 100644
index c6d42f3c..00000000
--- a/basemap/layers/boundary/globaladm0_simplify.sql
+++ /dev/null
@@ -1,76 +0,0 @@
--- 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.
-CREATE VIEW globaladm0_z20 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM0";
-
-CREATE VIEW globaladm0_z19 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM0";
-
-CREATE VIEW globaladm0_z18 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM0";
-
-CREATE VIEW globaladm0_z17 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM0";
-
-CREATE VIEW globaladm0_z16 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM0";
-
-CREATE VIEW globaladm0_z15 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM0";
-
-CREATE VIEW globaladm0_z14 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM0";
-
-CREATE VIEW globaladm0_z13 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z12 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 12)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z11 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 11)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z10 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 10)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z9 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 9)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z8 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 8)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z7 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 7)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z6 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 6)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z5 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 5)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z4 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 4)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z3 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 3)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z2 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 2)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z1 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 1)) AS geom FROM 
"globalADM0";
-
-CREATE MATERIALIZED VIEW globaladm0_z0 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 0)) AS geom FROM 
"globalADM0";
diff --git a/basemap/layers/boundary/globaladm1_clean.sql 
b/basemap/layers/boundary/globaladm1_clean.sql
deleted file mode 100644
index a6b2377b..00000000
--- a/basemap/layers/boundary/globaladm1_clean.sql
+++ /dev/null
@@ -1,36 +0,0 @@
--- 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.
-DROP VIEW IF EXISTS globaladm1_z20 CASCADE;
-DROP VIEW IF EXISTS globaladm1_z19 CASCADE;
-DROP VIEW IF EXISTS globaladm1_z18 CASCADE;
-DROP VIEW IF EXISTS globaladm1_z17 CASCADE;
-DROP VIEW IF EXISTS globaladm1_z16 CASCADE;
-DROP VIEW IF EXISTS globaladm1_z15 CASCADE;
-DROP VIEW IF EXISTS globaladm1_z14 CASCADE;
-DROP VIEW IF EXISTS globaladm1_z13 CASCADE;
-
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z12 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z11 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z10 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z9 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z8 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z7 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z6 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z5 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z4 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z3 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z2 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z1 CASCADE;
-DROP MATERIALIZED VIEW IF EXISTS globaladm1_z0 CASCADE;
diff --git a/basemap/layers/boundary/globaladm1_index.sql 
b/basemap/layers/boundary/globaladm1_index.sql
deleted file mode 100644
index 3dfe8d30..00000000
--- a/basemap/layers/boundary/globaladm1_index.sql
+++ /dev/null
@@ -1,28 +0,0 @@
--- 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.
-CREATE INDEX IF NOT EXISTS globaladm1_index ON "globalADM1" USING SPGIST(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z12_index ON globaladm1_z12 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z11_index ON globaladm1_z11 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z10_index ON globaladm1_z10 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z9_index ON globaladm1_z9 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z8_index ON globaladm1_z8 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z7_index ON globaladm1_z7 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z6_index ON globaladm1_z6 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z5_index ON globaladm1_z5 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z4_index ON globaladm1_z4 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z3_index ON globaladm1_z3 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z2_index ON globaladm1_z2 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z1_index ON globaladm1_z1 USING SPGIST 
(geom);
-CREATE INDEX IF NOT EXISTS globaladm1_z0_index ON globaladm1_z0 USING SPGIST 
(geom);
diff --git a/basemap/layers/boundary/globaladm1_simplify.sql 
b/basemap/layers/boundary/globaladm1_simplify.sql
deleted file mode 100644
index 86696433..00000000
--- a/basemap/layers/boundary/globaladm1_simplify.sql
+++ /dev/null
@@ -1,76 +0,0 @@
--- 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.
-CREATE VIEW globaladm1_z20 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM1";
-
-CREATE VIEW globaladm1_z19 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM1";
-
-CREATE VIEW globaladm1_z18 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM1";
-
-CREATE VIEW globaladm1_z17 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM1";
-
-CREATE VIEW globaladm1_z16 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM1";
-
-CREATE VIEW globaladm1_z15 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM1";
-
-CREATE VIEW globaladm1_z14 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM1";
-
-CREATE VIEW globaladm1_z13 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z12 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 12)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z11 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 11)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z10 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 10)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z9 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 9)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z8 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 8)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z7 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 7)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z6 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 6)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z5 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 5)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z4 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 4)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z3 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 3)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z2 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 2)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z1 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 1)) AS geom FROM 
"globalADM1";
-
-CREATE MATERIALIZED VIEW globaladm1_z0 AS
-SELECT fid, "shapeGroup" as shapegroup, "shapeType" as shapetype, 
st_simplifypreservetopology(geom, 78270 / power(2, 0)) AS geom FROM 
"globalADM1";
diff --git a/basemap/layers/highway/clean.sql b/basemap/layers/highway/clean.sql
index 8350fac8..b3fa7321 100644
--- a/basemap/layers/highway/clean.sql
+++ b/basemap/layers/highway/clean.sql
@@ -12,4 +12,27 @@
 -- 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.
-DROP MATERIALIZED VIEW IF EXISTS osm_highway CASCADE;
+
+DROP VIEW IF EXISTS osm_highway CASCADE;
+
+DROP VIEW IF EXISTS osm_highway_z20 CASCADE;
+DROP VIEW IF EXISTS osm_highway_z19 CASCADE;
+DROP VIEW IF EXISTS osm_highway_z18 CASCADE;
+DROP VIEW IF EXISTS osm_highway_z17 CASCADE;
+DROP VIEW IF EXISTS osm_highway_z16 CASCADE;
+DROP VIEW IF EXISTS osm_highway_z15 CASCADE;
+DROP VIEW IF EXISTS osm_highway_z14 CASCADE;
+DROP VIEW IF EXISTS osm_highway_z13 CASCADE;
+
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z12 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z11 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z10 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z9 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z8 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z7 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z6 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z5 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z4 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z3 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z2 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_highway_z1 CASCADE;
diff --git a/basemap/layers/highway/prepare.sql 
b/basemap/layers/highway/prepare.sql
index dff5eb1c..b29d130d 100644
--- a/basemap/layers/highway/prepare.sql
+++ b/basemap/layers/highway/prepare.sql
@@ -12,8 +12,6 @@
 -- 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.
-DROP MATERIALIZED VIEW IF EXISTS osm_highway CASCADE;
-
 CREATE MATERIALIZED VIEW osm_highway AS
 WITH
     -- Filter the linestrings
diff --git a/basemap/layers/route/prepare.sql 
b/basemap/layers/highway/refresh.sql
similarity index 59%
copy from basemap/layers/route/prepare.sql
copy to basemap/layers/highway/refresh.sql
index cc25bd0b..cfb9bc4a 100644
--- a/basemap/layers/route/prepare.sql
+++ b/basemap/layers/highway/refresh.sql
@@ -12,19 +12,18 @@
 -- 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.
-DROP MATERIALIZED VIEW IF EXISTS osm_route CASCADE;
 
-CREATE MATERIALIZED VIEW osm_route AS
-SELECT id, tags, geom
-FROM (
-   SELECT
-       min(id) as id,
-       jsonb_build_object('route', tags -> 'route') as tags,
-       (st_dump(st_linemerge(st_collect(geom)))).geom as geom
-   FROM osm_ways
-   WHERE tags ->> 'route' IN ('light_rail', 'monorail', 'rail', 'subway', 
'tram')
-   AND NOT tags ? 'service'
-   GROUP BY tags -> 'route'
-) AS merge;
+REFRESH MATERIALIZED VIEW osm_highway;
 
-CREATE INDEX IF NOT EXISTS osm_route_geom_index ON osm_route USING SPGIST 
(geom);
+REFRESH MATERIALIZED VIEW osm_highway_z12;
+REFRESH MATERIALIZED VIEW osm_highway_z11;
+REFRESH MATERIALIZED VIEW osm_highway_z10;
+REFRESH MATERIALIZED VIEW osm_highway_z9;
+REFRESH MATERIALIZED VIEW osm_highway_z8;
+REFRESH MATERIALIZED VIEW osm_highway_z7;
+REFRESH MATERIALIZED VIEW osm_highway_z6;
+REFRESH MATERIALIZED VIEW osm_highway_z5;
+REFRESH MATERIALIZED VIEW osm_highway_z4;
+REFRESH MATERIALIZED VIEW osm_highway_z3;
+REFRESH MATERIALIZED VIEW osm_highway_z2;
+REFRESH MATERIALIZED VIEW osm_highway_z1;
\ No newline at end of file
diff --git a/basemap/layers/landuse/clean.sql b/basemap/layers/landuse/clean.sql
index 294ce311..1032d5c6 100644
--- a/basemap/layers/landuse/clean.sql
+++ b/basemap/layers/landuse/clean.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 DROP MATERIALIZED VIEW IF EXISTS osm_landuse_filtered CASCADE;
 DROP MATERIALIZED VIEW IF EXISTS osm_landuse_clustered CASCADE;
 DROP MATERIALIZED VIEW IF EXISTS osm_landuse_grouped CASCADE;
diff --git a/basemap/layers/landuse/refresh.sql 
b/basemap/layers/landuse/refresh.sql
new file mode 100644
index 00000000..0cfb37f5
--- /dev/null
+++ b/basemap/layers/landuse/refresh.sql
@@ -0,0 +1,62 @@
+-- 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.
+
+REFRESH MATERIALIZED VIEW osm_landuse_filtered;
+REFRESH MATERIALIZED VIEW osm_landuse_clustered;
+REFRESH MATERIALIZED VIEW osm_landuse_grouped;
+REFRESH MATERIALIZED VIEW osm_landuse_buffered;
+REFRESH MATERIALIZED VIEW osm_landuse_exploded;
+REFRESH MATERIALIZED VIEW osm_landuse;
+
+REFRESH MATERIALIZED VIEW osm_landuse_xl_filtered;
+REFRESH MATERIALIZED VIEW osm_landuse_xl_clustered;
+REFRESH MATERIALIZED VIEW osm_landuse_xl_grouped;
+REFRESH MATERIALIZED VIEW osm_landuse_xl_buffered;
+REFRESH MATERIALIZED VIEW osm_landuse_xl_exploded;
+REFRESH MATERIALIZED VIEW osm_landuse_xl;
+
+REFRESH MATERIALIZED VIEW osm_landuse_l_filtered;
+REFRESH MATERIALIZED VIEW osm_landuse_l_clustered;
+REFRESH MATERIALIZED VIEW osm_landuse_l_grouped;
+REFRESH MATERIALIZED VIEW osm_landuse_l_buffered;
+REFRESH MATERIALIZED VIEW osm_landuse_l_exploded;
+REFRESH MATERIALIZED VIEW osm_landuse_l;
+
+REFRESH MATERIALIZED VIEW osm_landuse_m_filtered;
+REFRESH MATERIALIZED VIEW osm_landuse_m_clustered;
+REFRESH MATERIALIZED VIEW osm_landuse_m_grouped;
+REFRESH MATERIALIZED VIEW osm_landuse_m_buffered;
+REFRESH MATERIALIZED VIEW osm_landuse_m_exploded;
+REFRESH MATERIALIZED VIEW osm_landuse_m;
+
+REFRESH MATERIALIZED VIEW osm_landuse_s_filtered;
+REFRESH MATERIALIZED VIEW osm_landuse_s_clustered;
+REFRESH MATERIALIZED VIEW osm_landuse_s_grouped;
+REFRESH MATERIALIZED VIEW osm_landuse_s_buffered;
+REFRESH MATERIALIZED VIEW osm_landuse_s_exploded;
+REFRESH MATERIALIZED VIEW osm_landuse_s;
+
+REFRESH MATERIALIZED VIEW osm_landuse_z12;
+REFRESH MATERIALIZED VIEW osm_landuse_z11;
+REFRESH MATERIALIZED VIEW osm_landuse_z10;
+REFRESH MATERIALIZED VIEW osm_landuse_z9;
+REFRESH MATERIALIZED VIEW osm_landuse_z8;
+REFRESH MATERIALIZED VIEW osm_landuse_z7;
+REFRESH MATERIALIZED VIEW osm_landuse_z6;
+REFRESH MATERIALIZED VIEW osm_landuse_z5;
+REFRESH MATERIALIZED VIEW osm_landuse_z4;
+REFRESH MATERIALIZED VIEW osm_landuse_z3;
+REFRESH MATERIALIZED VIEW osm_landuse_z2;
+REFRESH MATERIALIZED VIEW osm_landuse_z1;
diff --git a/basemap/layers/leisure/clean.sql b/basemap/layers/leisure/clean.sql
index 9b97c9dc..de58d7bb 100644
--- a/basemap/layers/leisure/clean.sql
+++ b/basemap/layers/leisure/clean.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 DROP MATERIALIZED VIEW IF EXISTS osm_leisure_filtered CASCADE;
 DROP MATERIALIZED VIEW IF EXISTS osm_leisure_clustered CASCADE;
 DROP MATERIALIZED VIEW IF EXISTS osm_leisure_grouped CASCADE;
diff --git a/basemap/layers/leisure/index.sql b/basemap/layers/leisure/index.sql
index 5efaf447..3029ef15 100644
--- a/basemap/layers/leisure/index.sql
+++ b/basemap/layers/leisure/index.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 CREATE INDEX IF NOT EXISTS osm_leisure_geom_z1_index ON osm_leisure_z1 USING 
SPGIST (geom);
 CREATE INDEX IF NOT EXISTS osm_leisure_geom_z2_index ON osm_leisure_z2 USING 
SPGIST (geom);
 CREATE INDEX IF NOT EXISTS osm_leisure_geom_z3_index ON osm_leisure_z3 USING 
SPGIST (geom);
diff --git a/basemap/layers/leisure/prepare.sql 
b/basemap/layers/leisure/prepare.sql
index 75b99c3f..aba4c2ab 100644
--- a/basemap/layers/leisure/prepare.sql
+++ b/basemap/layers/leisure/prepare.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 CREATE MATERIALIZED VIEW osm_leisure_filtered AS
 SELECT
     tags -> 'leisure' AS leisure,
diff --git a/basemap/layers/leisure/refresh.sql 
b/basemap/layers/leisure/refresh.sql
new file mode 100644
index 00000000..93a62c5d
--- /dev/null
+++ b/basemap/layers/leisure/refresh.sql
@@ -0,0 +1,62 @@
+-- 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.
+
+REFRESH MATERIALIZED VIEW osm_leisure_filtered;
+REFRESH MATERIALIZED VIEW osm_leisure_clustered;
+REFRESH MATERIALIZED VIEW osm_leisure_grouped;
+REFRESH MATERIALIZED VIEW osm_leisure_buffered;
+REFRESH MATERIALIZED VIEW osm_leisure_exploded;
+REFRESH MATERIALIZED VIEW osm_leisure;
+
+REFRESH MATERIALIZED VIEW osm_leisure_xl_filtered;
+REFRESH MATERIALIZED VIEW osm_leisure_xl_clustered;
+REFRESH MATERIALIZED VIEW osm_leisure_xl_grouped;
+REFRESH MATERIALIZED VIEW osm_leisure_xl_buffered;
+REFRESH MATERIALIZED VIEW osm_leisure_xl_exploded;
+REFRESH MATERIALIZED VIEW osm_leisure_xl;
+
+REFRESH MATERIALIZED VIEW osm_leisure_l_filtered;
+REFRESH MATERIALIZED VIEW osm_leisure_l_clustered;
+REFRESH MATERIALIZED VIEW osm_leisure_l_grouped;
+REFRESH MATERIALIZED VIEW osm_leisure_l_buffered;
+REFRESH MATERIALIZED VIEW osm_leisure_l_exploded;
+REFRESH MATERIALIZED VIEW osm_leisure_l;
+
+REFRESH MATERIALIZED VIEW osm_leisure_m_filtered;
+REFRESH MATERIALIZED VIEW osm_leisure_m_clustered;
+REFRESH MATERIALIZED VIEW osm_leisure_m_grouped;
+REFRESH MATERIALIZED VIEW osm_leisure_m_buffered;
+REFRESH MATERIALIZED VIEW osm_leisure_m_exploded;
+REFRESH MATERIALIZED VIEW osm_leisure_m;
+
+REFRESH MATERIALIZED VIEW osm_leisure_s_filtered;
+REFRESH MATERIALIZED VIEW osm_leisure_s_clustered;
+REFRESH MATERIALIZED VIEW osm_leisure_s_grouped;
+REFRESH MATERIALIZED VIEW osm_leisure_s_buffered;
+REFRESH MATERIALIZED VIEW osm_leisure_s_exploded;
+REFRESH MATERIALIZED VIEW osm_leisure_s;
+
+REFRESH MATERIALIZED VIEW osm_leisure_z12;
+REFRESH MATERIALIZED VIEW osm_leisure_z11;
+REFRESH MATERIALIZED VIEW osm_leisure_z10;
+REFRESH MATERIALIZED VIEW osm_leisure_z9;
+REFRESH MATERIALIZED VIEW osm_leisure_z8;
+REFRESH MATERIALIZED VIEW osm_leisure_z7;
+REFRESH MATERIALIZED VIEW osm_leisure_z6;
+REFRESH MATERIALIZED VIEW osm_leisure_z5;
+REFRESH MATERIALIZED VIEW osm_leisure_z4;
+REFRESH MATERIALIZED VIEW osm_leisure_z3;
+REFRESH MATERIALIZED VIEW osm_leisure_z2;
+REFRESH MATERIALIZED VIEW osm_leisure_z1;
diff --git a/basemap/layers/linestring/clean.sql 
b/basemap/layers/linestring/clean.sql
index 11f941d8..a7a69450 100644
--- a/basemap/layers/linestring/clean.sql
+++ b/basemap/layers/linestring/clean.sql
@@ -12,4 +12,5 @@
 -- 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.
+
 DROP MATERIALIZED VIEW IF EXISTS osm_linestring CASCADE;
diff --git a/basemap/layers/linestring/index.sql 
b/basemap/layers/linestring/index.sql
index edb77ae7..6c82ea12 100644
--- a/basemap/layers/linestring/index.sql
+++ b/basemap/layers/linestring/index.sql
@@ -12,5 +12,6 @@
 -- 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.
+
 CREATE INDEX IF NOT EXISTS osm_linestring_tags_index ON osm_linestring USING 
gin (tags);
 CREATE INDEX IF NOT EXISTS osm_linestring_geom_index ON osm_linestring USING 
gist (geom);
\ No newline at end of file
diff --git a/basemap/layers/linestring/prepare.sql 
b/basemap/layers/linestring/prepare.sql
index e86180d0..fb7b1cf3 100644
--- a/basemap/layers/linestring/prepare.sql
+++ b/basemap/layers/linestring/prepare.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 DROP MATERIALIZED VIEW IF EXISTS osm_linestring CASCADE;
 
 CREATE MATERIALIZED VIEW osm_linestring AS
diff --git a/basemap/queries/initialize.sql 
b/basemap/layers/linestring/refresh.sql
similarity index 94%
copy from basemap/queries/initialize.sql
copy to basemap/layers/linestring/refresh.sql
index aa233f62..f665292e 100644
--- a/basemap/queries/initialize.sql
+++ b/basemap/layers/linestring/refresh.sql
@@ -12,4 +12,5 @@
 -- 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.
-CREATE EXTENSION IF NOT EXISTS postgis;
+
+REFRESH MATERIALIZED VIEW osm_linestring;
diff --git a/basemap/queries/initialize.sql b/basemap/layers/member/refresh.sql
similarity index 95%
copy from basemap/queries/initialize.sql
copy to basemap/layers/member/refresh.sql
index aa233f62..1056b2b2 100644
--- a/basemap/queries/initialize.sql
+++ b/basemap/layers/member/refresh.sql
@@ -12,4 +12,5 @@
 -- 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.
-CREATE EXTENSION IF NOT EXISTS postgis;
+
+REFRESH MATERIALIZED VIEW osm_member;
diff --git a/basemap/layers/natural/refresh.sql 
b/basemap/layers/natural/refresh.sql
new file mode 100644
index 00000000..215d35ab
--- /dev/null
+++ b/basemap/layers/natural/refresh.sql
@@ -0,0 +1,57 @@
+-- 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.
+
+REFRESH MATERIALIZED VIEW osm_natural_filtered;
+REFRESH MATERIALIZED VIEW osm_natural_clustered;
+REFRESH MATERIALIZED VIEW osm_natural_grouped;
+REFRESH MATERIALIZED VIEW osm_natural_buffered;
+REFRESH MATERIALIZED VIEW osm_natural_exploded;
+REFRESH MATERIALIZED VIEW osm_natural;
+
+REFRESH MATERIALIZED VIEW osm_natural_xl_filtered;
+REFRESH MATERIALIZED VIEW osm_natural_xl_clustered;
+REFRESH MATERIALIZED VIEW osm_natural_xl_grouped;
+REFRESH MATERIALIZED VIEW osm_natural_xl_buffered;
+REFRESH MATERIALIZED VIEW osm_natural_xl_exploded;
+REFRESH MATERIALIZED VIEW osm_natural_xl;
+
+-- Why don't we have a natural_l view?
+
+REFRESH MATERIALIZED VIEW osm_natural_m_filtered;
+REFRESH MATERIALIZED VIEW osm_natural_m_clustered;
+REFRESH MATERIALIZED VIEW osm_natural_m_grouped;
+REFRESH MATERIALIZED VIEW osm_natural_m_buffered;
+REFRESH MATERIALIZED VIEW osm_natural_m_exploded;
+REFRESH MATERIALIZED VIEW osm_natural_m;
+
+REFRESH MATERIALIZED VIEW osm_natural_s_filtered;
+REFRESH MATERIALIZED VIEW osm_natural_s_clustered;
+REFRESH MATERIALIZED VIEW osm_natural_s_grouped;
+REFRESH MATERIALIZED VIEW osm_natural_s_buffered;
+REFRESH MATERIALIZED VIEW osm_natural_s_exploded;
+REFRESH MATERIALIZED VIEW osm_natural_s;
+
+REFRESH MATERIALIZED VIEW osm_natural_z12;
+REFRESH MATERIALIZED VIEW osm_natural_z11;
+REFRESH MATERIALIZED VIEW osm_natural_z10;
+REFRESH MATERIALIZED VIEW osm_natural_z9;
+REFRESH MATERIALIZED VIEW osm_natural_z8;
+REFRESH MATERIALIZED VIEW osm_natural_z7;
+REFRESH MATERIALIZED VIEW osm_natural_z6;
+REFRESH MATERIALIZED VIEW osm_natural_z5;
+REFRESH MATERIALIZED VIEW osm_natural_z4;
+REFRESH MATERIALIZED VIEW osm_natural_z3;
+REFRESH MATERIALIZED VIEW osm_natural_z2;
+REFRESH MATERIALIZED VIEW osm_natural_z1;
diff --git a/basemap/layers/point/index.sql b/basemap/layers/point/index.sql
index 9706058d..1c9c24e8 100644
--- a/basemap/layers/point/index.sql
+++ b/basemap/layers/point/index.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 CREATE INDEX IF NOT EXISTS osm_point_geom_z13_index ON osm_point_z13 USING 
gist (geom);
 CREATE INDEX IF NOT EXISTS osm_point_geom_z12_index ON osm_point_z12 USING 
gist (geom);
 CREATE INDEX IF NOT EXISTS osm_point_geom_z11_index ON osm_point_z11 USING 
gist (geom);
diff --git a/basemap/layers/linestring/prepare.sql 
b/basemap/layers/point/refresh.sql
similarity index 60%
copy from basemap/layers/linestring/prepare.sql
copy to basemap/layers/point/refresh.sql
index e86180d0..981821ba 100644
--- a/basemap/layers/linestring/prepare.sql
+++ b/basemap/layers/point/refresh.sql
@@ -12,15 +12,17 @@
 -- 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.
-DROP MATERIALIZED VIEW IF EXISTS osm_linestring CASCADE;
 
-CREATE MATERIALIZED VIEW osm_linestring AS
-SELECT id, tags, geom, changeset
-FROM osm_ways
-LEFT JOIN osm_member ON id = member_ref
-WHERE ST_GeometryType(osm_ways.geom) = 'ST_LineString'
-  AND tags != '{}'
-  AND member_ref IS NULL;
-
-CREATE INDEX IF NOT EXISTS osm_linestring_tags_index ON osm_linestring USING 
gin (tags);
-CREATE INDEX IF NOT EXISTS osm_linestring_geom_index ON osm_linestring USING 
gist (geom);
+REFRESH MATERIALIZED VIEW osm_point_z13;
+REFRESH MATERIALIZED VIEW osm_point_z12;
+REFRESH MATERIALIZED VIEW osm_point_z11;
+REFRESH MATERIALIZED VIEW osm_point_z10;
+REFRESH MATERIALIZED VIEW osm_point_z9;
+REFRESH MATERIALIZED VIEW osm_point_z8;
+REFRESH MATERIALIZED VIEW osm_point_z7;
+REFRESH MATERIALIZED VIEW osm_point_z6;
+REFRESH MATERIALIZED VIEW osm_point_z5;
+REFRESH MATERIALIZED VIEW osm_point_z4;
+REFRESH MATERIALIZED VIEW osm_point_z3;
+REFRESH MATERIALIZED VIEW osm_point_z2;
+REFRESH MATERIALIZED VIEW osm_point_z1;
\ No newline at end of file
diff --git a/basemap/queries/initialize.sql b/basemap/layers/polygon/refresh.sql
similarity index 95%
copy from basemap/queries/initialize.sql
copy to basemap/layers/polygon/refresh.sql
index aa233f62..580d6f9e 100644
--- a/basemap/queries/initialize.sql
+++ b/basemap/layers/polygon/refresh.sql
@@ -12,4 +12,5 @@
 -- 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.
-CREATE EXTENSION IF NOT EXISTS postgis;
+
+REFRESH MATERIALIZED VIEW osm_polygon;
\ No newline at end of file
diff --git a/basemap/layers/railway/clean.sql b/basemap/layers/railway/clean.sql
index 5cbdfd6f..28bb55fb 100644
--- a/basemap/layers/railway/clean.sql
+++ b/basemap/layers/railway/clean.sql
@@ -12,4 +12,27 @@
 -- 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.
+
 DROP MATERIALIZED VIEW IF EXISTS osm_railway CASCADE;
+
+DROP VIEW IF EXISTS osm_railway_z20 CASCADE;
+DROP VIEW IF EXISTS osm_railway_z19 CASCADE;
+DROP VIEW IF EXISTS osm_railway_z18 CASCADE;
+DROP VIEW IF EXISTS osm_railway_z17 CASCADE;
+DROP VIEW IF EXISTS osm_railway_z16 CASCADE;
+DROP VIEW IF EXISTS osm_railway_z15 CASCADE;
+DROP VIEW IF EXISTS osm_railway_z14 CASCADE;
+DROP VIEW IF EXISTS osm_railway_z13 CASCADE;
+
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z12 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z11 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z10 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z9 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z8 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z7 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z6 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z5 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z4 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z3 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z2 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_railway_z1 CASCADE;
diff --git a/basemap/layers/railway/index.sql b/basemap/layers/railway/index.sql
index 34a6c2a1..df2d0d5b 100644
--- a/basemap/layers/railway/index.sql
+++ b/basemap/layers/railway/index.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 CREATE INDEX IF NOT EXISTS osm_railway_geom_z12_index ON osm_railway_z12 USING 
SPGIST (geom);
 CREATE INDEX IF NOT EXISTS osm_railway_geom_z11_index ON osm_railway_z11 USING 
SPGIST (geom);
 CREATE INDEX IF NOT EXISTS osm_railway_geom_z10_index ON osm_railway_z10 USING 
SPGIST (geom);
diff --git a/basemap/layers/railway/prepare.sql 
b/basemap/layers/railway/prepare.sql
index c9222865..7ccfa2a3 100644
--- a/basemap/layers/railway/prepare.sql
+++ b/basemap/layers/railway/prepare.sql
@@ -12,7 +12,7 @@
 -- 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.
-DROP MATERIALIZED VIEW IF EXISTS osm_railway CASCADE;
+
 CREATE MATERIALIZED VIEW osm_railway AS
 SELECT id, tags, geom
 FROM (
diff --git a/basemap/layers/route/prepare.sql 
b/basemap/layers/railway/refresh.sql
similarity index 59%
copy from basemap/layers/route/prepare.sql
copy to basemap/layers/railway/refresh.sql
index cc25bd0b..81f52c13 100644
--- a/basemap/layers/route/prepare.sql
+++ b/basemap/layers/railway/refresh.sql
@@ -12,19 +12,18 @@
 -- 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.
-DROP MATERIALIZED VIEW IF EXISTS osm_route CASCADE;
 
-CREATE MATERIALIZED VIEW osm_route AS
-SELECT id, tags, geom
-FROM (
-   SELECT
-       min(id) as id,
-       jsonb_build_object('route', tags -> 'route') as tags,
-       (st_dump(st_linemerge(st_collect(geom)))).geom as geom
-   FROM osm_ways
-   WHERE tags ->> 'route' IN ('light_rail', 'monorail', 'rail', 'subway', 
'tram')
-   AND NOT tags ? 'service'
-   GROUP BY tags -> 'route'
-) AS merge;
+REFRESH MATERIALIZED VIEW osm_railway;
 
-CREATE INDEX IF NOT EXISTS osm_route_geom_index ON osm_route USING SPGIST 
(geom);
+REFRESH MATERIALIZED VIEW osm_railway_z12;
+REFRESH MATERIALIZED VIEW osm_railway_z11;
+REFRESH MATERIALIZED VIEW osm_railway_z10;
+REFRESH MATERIALIZED VIEW osm_railway_z9;
+REFRESH MATERIALIZED VIEW osm_railway_z8;
+REFRESH MATERIALIZED VIEW osm_railway_z7;
+REFRESH MATERIALIZED VIEW osm_railway_z6;
+REFRESH MATERIALIZED VIEW osm_railway_z5;
+REFRESH MATERIALIZED VIEW osm_railway_z4;
+REFRESH MATERIALIZED VIEW osm_railway_z3;
+REFRESH MATERIALIZED VIEW osm_railway_z2;
+REFRESH MATERIALIZED VIEW osm_railway_z1;
diff --git a/basemap/layers/route/clean.sql b/basemap/layers/route/clean.sql
index 711f993c..1b5358ae 100644
--- a/basemap/layers/route/clean.sql
+++ b/basemap/layers/route/clean.sql
@@ -12,4 +12,27 @@
 -- 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.
+
 DROP MATERIALIZED VIEW IF EXISTS osm_route CASCADE;
+
+DROP VIEW IF EXISTS osm_route_z20 CASCADE;
+DROP VIEW IF EXISTS osm_route_z19 CASCADE;
+DROP VIEW IF EXISTS osm_route_z18 CASCADE;
+DROP VIEW IF EXISTS osm_route_z17 CASCADE;
+DROP VIEW IF EXISTS osm_route_z16 CASCADE;
+DROP VIEW IF EXISTS osm_route_z15 CASCADE;
+DROP VIEW IF EXISTS osm_route_z14 CASCADE;
+DROP VIEW IF EXISTS osm_route_z13 CASCADE;
+
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z12 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z11 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z10 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z9 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z8 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z7 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z6 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z5 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z4 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z3 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z2 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_route_z1 CASCADE;
diff --git a/basemap/layers/route/index.sql b/basemap/layers/route/index.sql
index 0f5eabd3..3b2a8ac9 100644
--- a/basemap/layers/route/index.sql
+++ b/basemap/layers/route/index.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 CREATE INDEX IF NOT EXISTS osm_route_geom_z12_index ON osm_route_z12 USING 
SPGIST (geom);
 CREATE INDEX IF NOT EXISTS osm_route_geom_z11_index ON osm_route_z11 USING 
SPGIST (geom);
 CREATE INDEX IF NOT EXISTS osm_route_geom_z10_index ON osm_route_z10 USING 
SPGIST (geom);
diff --git a/basemap/layers/route/prepare.sql b/basemap/layers/route/prepare.sql
index cc25bd0b..a06761fb 100644
--- a/basemap/layers/route/prepare.sql
+++ b/basemap/layers/route/prepare.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 DROP MATERIALIZED VIEW IF EXISTS osm_route CASCADE;
 
 CREATE MATERIALIZED VIEW osm_route AS
diff --git a/basemap/layers/linestring/prepare.sql 
b/basemap/layers/route/refresh.sql
similarity index 60%
copy from basemap/layers/linestring/prepare.sql
copy to basemap/layers/route/refresh.sql
index e86180d0..a25194f9 100644
--- a/basemap/layers/linestring/prepare.sql
+++ b/basemap/layers/route/refresh.sql
@@ -12,15 +12,18 @@
 -- 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.
-DROP MATERIALIZED VIEW IF EXISTS osm_linestring CASCADE;
 
-CREATE MATERIALIZED VIEW osm_linestring AS
-SELECT id, tags, geom, changeset
-FROM osm_ways
-LEFT JOIN osm_member ON id = member_ref
-WHERE ST_GeometryType(osm_ways.geom) = 'ST_LineString'
-  AND tags != '{}'
-  AND member_ref IS NULL;
+REFRESH MATERIALIZED VIEW osm_route;
 
-CREATE INDEX IF NOT EXISTS osm_linestring_tags_index ON osm_linestring USING 
gin (tags);
-CREATE INDEX IF NOT EXISTS osm_linestring_geom_index ON osm_linestring USING 
gist (geom);
+REFRESH MATERIALIZED VIEW osm_route_z12;
+REFRESH MATERIALIZED VIEW osm_route_z11;
+REFRESH MATERIALIZED VIEW osm_route_z10;
+REFRESH MATERIALIZED VIEW osm_route_z9;
+REFRESH MATERIALIZED VIEW osm_route_z8;
+REFRESH MATERIALIZED VIEW osm_route_z7;
+REFRESH MATERIALIZED VIEW osm_route_z6;
+REFRESH MATERIALIZED VIEW osm_route_z5;
+REFRESH MATERIALIZED VIEW osm_route_z4;
+REFRESH MATERIALIZED VIEW osm_route_z3;
+REFRESH MATERIALIZED VIEW osm_route_z2;
+REFRESH MATERIALIZED VIEW osm_route_z1;
diff --git a/basemap/layers/route/simplify.sql 
b/basemap/layers/route/simplify.sql
index f1766c65..ef528424 100644
--- a/basemap/layers/route/simplify.sql
+++ b/basemap/layers/route/simplify.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 CREATE VIEW osm_route_z20 AS
 SELECT id, tags, geom FROM osm_route;
 
diff --git a/basemap/layers/waterway/clean.sql 
b/basemap/layers/waterway/clean.sql
index 3726734f..b5c2bfdb 100644
--- a/basemap/layers/waterway/clean.sql
+++ b/basemap/layers/waterway/clean.sql
@@ -12,4 +12,27 @@
 -- 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.
+
 DROP MATERIALIZED VIEW IF EXISTS osm_waterway CASCADE;
+
+DROP VIEW IF EXISTS osm_waterway_z20 CASCADE;
+DROP VIEW IF EXISTS osm_waterway_z19 CASCADE;
+DROP VIEW IF EXISTS osm_waterway_z18 CASCADE;
+DROP VIEW IF EXISTS osm_waterway_z17 CASCADE;
+DROP VIEW IF EXISTS osm_waterway_z16 CASCADE;
+DROP VIEW IF EXISTS osm_waterway_z15 CASCADE;
+DROP VIEW IF EXISTS osm_waterway_z14 CASCADE;
+DROP VIEW IF EXISTS osm_waterway_z13 CASCADE;
+
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z12 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z11 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z10 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z9 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z8 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z7 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z6 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z5 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z4 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z3 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z2 CASCADE;
+DROP MATERIALIZED VIEW IF EXISTS osm_waterway_z1 CASCADE;
diff --git a/basemap/layers/waterway/index.sql 
b/basemap/layers/waterway/index.sql
index 81ddb5c1..06e1484b 100644
--- a/basemap/layers/waterway/index.sql
+++ b/basemap/layers/waterway/index.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 CREATE INDEX IF NOT EXISTS osm_waterway_geom_z12_index ON osm_waterway_z12 
USING SPGIST (geom);
 CREATE INDEX IF NOT EXISTS osm_waterway_geom_z11_index ON osm_waterway_z11 
USING SPGIST (geom);
 CREATE INDEX IF NOT EXISTS osm_waterway_geom_z10_index ON osm_waterway_z10 
USING SPGIST (geom);
diff --git a/basemap/layers/waterway/prepare.sql 
b/basemap/layers/waterway/prepare.sql
index 54c8928a..1e079d36 100644
--- a/basemap/layers/waterway/prepare.sql
+++ b/basemap/layers/waterway/prepare.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 DROP MATERIALIZED VIEW IF EXISTS osm_waterway CASCADE;
 
 CREATE MATERIALIZED VIEW osm_waterway AS
diff --git a/basemap/layers/route/prepare.sql 
b/basemap/layers/waterway/refresh.sql
similarity index 58%
copy from basemap/layers/route/prepare.sql
copy to basemap/layers/waterway/refresh.sql
index cc25bd0b..b18034ba 100644
--- a/basemap/layers/route/prepare.sql
+++ b/basemap/layers/waterway/refresh.sql
@@ -12,19 +12,18 @@
 -- 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.
-DROP MATERIALIZED VIEW IF EXISTS osm_route CASCADE;
 
-CREATE MATERIALIZED VIEW osm_route AS
-SELECT id, tags, geom
-FROM (
-   SELECT
-       min(id) as id,
-       jsonb_build_object('route', tags -> 'route') as tags,
-       (st_dump(st_linemerge(st_collect(geom)))).geom as geom
-   FROM osm_ways
-   WHERE tags ->> 'route' IN ('light_rail', 'monorail', 'rail', 'subway', 
'tram')
-   AND NOT tags ? 'service'
-   GROUP BY tags -> 'route'
-) AS merge;
+REFRESH MATERIALIZED VIEW osm_waterway;
 
-CREATE INDEX IF NOT EXISTS osm_route_geom_index ON osm_route USING SPGIST 
(geom);
+REFRESH MATERIALIZED VIEW osm_waterway_z12;
+REFRESH MATERIALIZED VIEW osm_waterway_z11;
+REFRESH MATERIALIZED VIEW osm_waterway_z10;
+REFRESH MATERIALIZED VIEW osm_waterway_z9;
+REFRESH MATERIALIZED VIEW osm_waterway_z8;
+REFRESH MATERIALIZED VIEW osm_waterway_z7;
+REFRESH MATERIALIZED VIEW osm_waterway_z6;
+REFRESH MATERIALIZED VIEW osm_waterway_z5;
+REFRESH MATERIALIZED VIEW osm_waterway_z4;
+REFRESH MATERIALIZED VIEW osm_waterway_z3;
+REFRESH MATERIALIZED VIEW osm_waterway_z2;
+REFRESH MATERIALIZED VIEW osm_waterway_z1;
diff --git a/basemap/queries/initialize.sql b/basemap/queries/initialize.sql
index aa233f62..99c99ac7 100644
--- a/basemap/queries/initialize.sql
+++ b/basemap/queries/initialize.sql
@@ -12,4 +12,5 @@
 -- 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.
+
 CREATE EXTENSION IF NOT EXISTS postgis;
diff --git a/basemap/queries/ne_index.sql b/basemap/queries/ne_index.sql
index 0eec2c7a..3c70fc01 100644
--- a/basemap/queries/ne_index.sql
+++ b/basemap/queries/ne_index.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 CREATE INDEX IF NOT EXISTS 
ne_10m_admin_0_antarctic_claim_limit_lines_geom_index ON 
ne_10m_admin_0_antarctic_claim_limit_lines USING SPGIST(geom);
 CREATE INDEX IF NOT EXISTS ne_10m_admin_0_antarctic_claims_geom_index ON 
ne_10m_admin_0_antarctic_claims USING SPGIST(geom);
 CREATE INDEX IF NOT EXISTS 
ne_10m_admin_0_boundary_lines_disputed_areas_geom_index ON 
ne_10m_admin_0_boundary_lines_disputed_areas USING SPGIST(geom);
diff --git a/basemap/queries/osm_relations.sql 
b/basemap/queries/osm_relations.sql
index b26882a5..a1711bea 100644
--- a/basemap/queries/osm_relations.sql
+++ b/basemap/queries/osm_relations.sql
@@ -12,5 +12,6 @@
 -- 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.
+
 CREATE INDEX IF NOT EXISTS osm_relations_tags_index ON osm_relations USING gin 
(tags);
 CREATE INDEX IF NOT EXISTS osm_relations_geom_index ON osm_relations USING 
gist (geom);
diff --git a/basemap/queries/osm_ways.sql b/basemap/queries/osm_ways.sql
index a835bfa7..54ae022c 100644
--- a/basemap/queries/osm_ways.sql
+++ b/basemap/queries/osm_ways.sql
@@ -12,5 +12,6 @@
 -- 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.
+
 CREATE INDEX IF NOT EXISTS osm_ways_geom_index ON osm_ways USING gist (geom);
 CREATE INDEX IF NOT EXISTS osm_ways_tags_index ON osm_ways USING gin (tags);
\ No newline at end of file
diff --git a/basemap/queries/statistics.sql b/basemap/queries/statistics.sql
index 5cecc41d..38e1ff2c 100644
--- a/basemap/queries/statistics.sql
+++ b/basemap/queries/statistics.sql
@@ -12,6 +12,7 @@
 -- 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.
+
 SELECT oid::regclass::text  AS objectname
      , relkind   AS objecttype
      , reltuples AS entries
diff --git a/basemap/refresh.js b/basemap/refresh.js
new file mode 100644
index 00000000..7afa917f
--- /dev/null
+++ b/basemap/refresh.js
@@ -0,0 +1,143 @@
+/**
+ 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.
+ **/
+import config from "./config.js";
+
+export default {
+    "steps": [
+        {
+            "id": "openstreetmap-member",
+            "needs": [],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/member/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-point",
+            "needs": ["openstreetmap-member"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/point/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-linestring",
+            "needs": ["openstreetmap-member"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/linestring/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-polygon",
+            "needs": ["openstreetmap-member"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/polygon/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-highway",
+            "needs": ["openstreetmap-linestring"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/highway/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-railway",
+            "needs": ["openstreetmap-linestring"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/railway/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-route",
+            "needs": ["openstreetmap-linestring"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/route/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-waterway",
+            "needs": ["openstreetmap-linestring"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/waterway/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-natural",
+            "needs": ["openstreetmap-polygon"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/natural/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-landuse",
+            "needs": ["openstreetmap-polygon"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/landuse/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+        {
+            "id": "openstreetmap-leisure",
+            "needs": ["openstreetmap-polygon"],
+            "tasks": [
+                {
+                    "type": "ExecuteSql",
+                    "file": "layers/leisure/refresh.sql",
+                    "database": config.database,
+                },
+            ]
+        },
+    ]
+}
diff --git a/basemap/update.js b/basemap/update.js
new file mode 100644
index 00000000..ad38deae
--- /dev/null
+++ b/basemap/update.js
@@ -0,0 +1,34 @@
+/**
+ 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.
+ **/
+import config from "./config.js";
+
+export default {
+    "steps": [
+        {
+            "id": "openstreetmap-data",
+            "needs": [],
+            "tasks": [
+                {
+                    "type": "UpdateOsmDatabase",
+                    "replicationUrl": 
"https://planet.osm.org/replication/hour/";,
+                    "database": config.database,
+                    "databaseSrid": 3857,
+                },
+            ]
+        },
+    ]
+}


Reply via email to