diff --git a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Dev.java b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Dev.java index 2c54f5263..aefc4a680 100644 --- a/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Dev.java +++ b/baremaps-cli/src/main/java/org/apache/baremaps/cli/map/Dev.java @@ -30,6 +30,7 @@ import org.apache.baremaps.utils.PostgresUtils; import org.apache.baremaps.vectortile.style.Style; import org.apache.baremaps.vectortile.tilejson.TileJSON; +import org.apache.baremaps.vectortile.tilejsonextended.TileJSONExtended; import org.apache.baremaps.vectortile.tileset.Tileset; import org.glassfish.hk2.api.TypeLiteral; import org.glassfish.hk2.utilities.binding.AbstractBinder; @@ -103,12 +104,23 @@ public Integer call() throws Exception { } }; + var tileJSONExtendedSupplierType = new TypeLiteral>() {}; + var tileJSONExtendedSupplier = (Supplier) () -> { + try { + var config = configReader.read(tilesetPath); + return objectMapper.readValue(config, TileJSONExtended.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + }; + var application = new ResourceConfig() .register(CorsFilter.class) .register(ChangeResource.class) .register(TileResource.class) .register(StyleResource.class) .register(TilesetResource.class) + .register(TileJsonExtendedResource.class) .register(ChangeResource.class) .register(ClassPathResource.class) .register(newContextResolver(objectMapper)) @@ -122,6 +134,7 @@ protected void configure() { bind(tileStoreSupplier).to(tileStoreType); bind(styleSupplier).to(styleSupplierType); bind(tileJSONSupplier).to(tileJSONSupplierType); + bind(tileJSONExtendedSupplier).to(tileJSONExtendedSupplierType); } }); diff --git a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejson/TileJSON.java b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejson/TileJSON.java index 03d65147b..c2f4cfdac 100644 --- a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejson/TileJSON.java +++ b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejson/TileJSON.java @@ -32,11 +32,12 @@ * https://github.com/mapbox/tilejson-spec */ public class TileJSON { + @JsonProperty("tilejson") String tilejson; @JsonProperty("tiles") List tiles; @JsonProperty("vector_layers") - List vectorLayers; + List vectorLayers; @JsonProperty("attribution") String attribution; @JsonProperty("bounds") @@ -71,7 +72,12 @@ public void setTiles(List tiles) { this.tiles = tiles; } - public List getVectorLayers() { + public void setVectorLayers( + List vectorLayers) { + this.vectorLayers = vectorLayers; + } + + public List getVectorLayers() { return vectorLayers; } diff --git a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejson/VectorLayer.java b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejson/VectorLayer.java index 46e89f65b..78a48c241 100644 --- a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejson/VectorLayer.java +++ b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejson/VectorLayer.java @@ -15,10 +15,35 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Map; -public record VectorLayer( - @JsonProperty("id") String id, - @JsonProperty("fields") Map fields, - @JsonProperty("description") String description, - @JsonProperty("maxzoom") Integer maxzoom, - @JsonProperty("minzoom") Integer minzoom) { +public class VectorLayer { + @JsonProperty("id") + String id; + @JsonProperty("fields") + Map fields; + @JsonProperty("description") + String description; + @JsonProperty("maxzoom") + Integer maxzoom; + @JsonProperty("minzoom") + Integer minzoom; + + public String getId() { + return id; + } + + public Map getFields() { + return fields; + } + + public String getDescription() { + return description; + } + + public Integer getMaxzoom() { + return maxzoom; + } + + public Integer getMinzoom() { + return minzoom; + } } diff --git a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejsonextended/TileJSONExtended.java b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejsonextended/TileJSONExtended.java new file mode 100644 index 000000000..7479d2a09 --- /dev/null +++ b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejsonextended/TileJSONExtended.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.baremaps.vectortile.tilejsonextended; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import java.util.List; +import org.apache.baremaps.vectortile.tilejson.TileJSON; + + +/** + * Implementation of TileJSON with custom additional fields for baremaps. + */ +public class TileJSONExtended extends TileJSON { + + @JsonProperty("vector_layers") + List vectorLayersExtended; + + @JsonGetter("vector_layers") + public List getVectorLayersExtended() { + return this.vectorLayersExtended; + } + + @JsonSetter("vector_layers") + public void setVectorLayersExtended(List vectorLayersExtended) { + this.vectorLayersExtended = vectorLayersExtended; + super.setVectorLayers(vectorLayersExtended); + } + +} diff --git a/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejsonextended/VectorLayerExtended.java b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejsonextended/VectorLayerExtended.java new file mode 100644 index 000000000..2dc62b00b --- /dev/null +++ b/baremaps-core/src/main/java/org/apache/baremaps/vectortile/tilejsonextended/VectorLayerExtended.java @@ -0,0 +1,29 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.baremaps.vectortile.tilejsonextended; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.List; +import org.apache.baremaps.vectortile.tilejson.VectorLayer; +import org.apache.baremaps.vectortile.tileset.TilesetQuery; + +public class VectorLayerExtended extends VectorLayer { + + @JsonProperty("queries") + List queries = new ArrayList<>(); + + public List getQueries() { + return queries; + } +} diff --git a/baremaps-core/src/test/java/org/apache/baremaps/testing/TestFiles.java b/baremaps-core/src/test/java/org/apache/baremaps/testing/TestFiles.java index 450c0acec..581acfc6c 100644 --- a/baremaps-core/src/test/java/org/apache/baremaps/testing/TestFiles.java +++ b/baremaps-core/src/test/java/org/apache/baremaps/testing/TestFiles.java @@ -50,6 +50,8 @@ public class TestFiles { public static final Path STYLE_JS = resolve("style.js"); + public static final Path TILESET_JSON = resolve("style.js"); + public static Path resolve(String resource) { Path cwd = Path.of("").toAbsolutePath(); Path pathFromRoot = Path.of("baremaps-core", "src", "test", "resources", resource); diff --git a/baremaps-core/src/test/java/org/apache/baremaps/vectortile/TileSetTest.java b/baremaps-core/src/test/java/org/apache/baremaps/vectortile/TileSetTest.java index cd058359f..b74b7ae89 100644 --- a/baremaps-core/src/test/java/org/apache/baremaps/vectortile/TileSetTest.java +++ b/baremaps-core/src/test/java/org/apache/baremaps/vectortile/TileSetTest.java @@ -15,7 +15,9 @@ import static org.apache.baremaps.utils.ObjectMapperUtils.objectMapper; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; @@ -23,6 +25,7 @@ import java.nio.file.Path; import org.apache.baremaps.config.ConfigReader; import org.apache.baremaps.vectortile.tilejson.TileJSON; +import org.apache.baremaps.vectortile.tilejsonextended.TileJSONExtended; import org.apache.baremaps.vectortile.tileset.Tileset; import org.junit.Test; @@ -46,7 +49,7 @@ public void testBasemapJSConfig() throws IOException { assertEquals("jdbc:postgresql://localhost:5432/baremaps?&user=baremaps&password=baremaps", tileSet.getDatabase()); - assertEquals("aerialway", tileJSON.getVectorLayers().get(0).id()); + assertEquals("aerialway", tileJSON.getVectorLayers().get(0).getId()); } @Test @@ -55,10 +58,40 @@ public void validateTileset() throws IOException { var tileSet = objectMapper.readValue(resourceFile(tilesetFile), Tileset.class); // Mapping to a POJO strictly following TileJSON specifications for API clients. var tileJSON = objectMapper.readValue(resourceFile(tilesetFile), TileJSON.class); + // Mapping to a POJO following TileJSON with extended fields specific to baremaps + var tileJSONExtended = + objectMapper.readValue(resourceFile(tilesetFile), TileJSONExtended.class); + + // Assert on Deserial assertEquals("jdbc:postgresql://localhost:5432/baremaps?&user=baremaps&password=baremaps", tileSet.getDatabase()); - assertEquals("aeroway", tileJSON.getVectorLayers().get(0).id()); + assertEquals("aeroway", tileJSON.getVectorLayers().get(0).getId()); + assertEquals("aeroway", tileJSONExtended.getVectorLayers().get(0).getId()); + assertEquals("aeroway", tileJSONExtended.getVectorLayersExtended().get(0).getId()); + assertEquals("SELECT id, tags, geom FROM osm_nodes WHERE tags ? 'aeroway'", + tileJSONExtended.getVectorLayersExtended().get(0).getQueries().get(0).getSql()); + + + var jsonTileSet = objectMapper.writeValueAsString(tileSet); + var jsonTileJSON = objectMapper.writeValueAsString(tileJSON); + var jsonTileJSONExtended = objectMapper.writeValueAsString(tileJSONExtended); + + // Password is only in internal POJO TileSet + assertTrue(jsonTileSet.contains("password=baremaps")); + assertFalse(jsonTileJSON.contains("password=baremaps")); + assertFalse(jsonTileJSONExtended.contains("password=baremaps")); + + // Queries are only in internal TileSet and exposed TileJSONExtended + assertTrue(jsonTileSet.contains("SELECT id")); + assertFalse(jsonTileJSON.contains("SELECT id")); + assertTrue(jsonTileJSONExtended.contains("SELECT id")); + + + // Validating the Deserial/Serial/Deserial is equals to Deserial/Serial + var tileJSONExtendedCopy = objectMapper.readValue(jsonTileJSONExtended, TileJSONExtended.class); + assertEquals(objectMapper.writeValueAsString(tileJSONExtended), + objectMapper.writeValueAsString(tileJSONExtendedCopy)); } @Test @@ -68,10 +101,11 @@ public void validateSpecificationExample() throws IOException { // Mapping to a POJO strictly following TileJSON specifications for API clients. var tileJSON = objectMapper.readValue(resourceFile(referenceFile), TileJSON.class); + assertNull(tileSet.getDatabase()); - assertEquals("telephone", tileJSON.getVectorLayers().get(0).id()); + assertEquals("telephone", tileJSON.getVectorLayers().get(0).getId()); assertEquals("the phone number", tileJSON.getVectorLayers().stream() - .filter(vl -> vl.id().equals("telephone")) - .findFirst().get().fields().get("phone_number")); + .filter(vl -> vl.getId().equals("telephone")) + .findFirst().get().getFields().get("phone_number")); } } diff --git a/baremaps-server/src/main/java/org/apache/baremaps/server/TileJsonExtendedResource.java b/baremaps-server/src/main/java/org/apache/baremaps/server/TileJsonExtendedResource.java new file mode 100644 index 000000000..c880174fe --- /dev/null +++ b/baremaps-server/src/main/java/org/apache/baremaps/server/TileJsonExtendedResource.java @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.baremaps.server; + +import java.util.function.Supplier; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.GET; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.baremaps.vectortile.tilejsonextended.TileJSONExtended; + +/** + * A resource that provides access to the tileJSON extended version + */ +@Singleton +@javax.ws.rs.Path("/") +public class TileJsonExtendedResource { + + private final Supplier tileJSONSupplier; + + @Inject + public TileJsonExtendedResource(Supplier tileJSONSupplier) { + this.tileJSONSupplier = tileJSONSupplier; + } + + @GET + @javax.ws.rs.Path("tiles-extended.json") + @Produces(MediaType.APPLICATION_JSON) + public TileJSONExtended getTileset() { + return tileJSONSupplier.get(); + } + +}