Search in sources :

Example 1 with BoundingBox

use of de.ii.xtraplatform.crs.domain.BoundingBox in project ldproxy by interactive-instruments.

the class TilesHelper method buildTileSet.

// TODO: move to TileSet as static of()
/**
 * generate the tile set metadata according to the OGC Tile Matrix Set standard (version 2.0.0, draft from June 2021)
 * @param api the API
 * @param tileMatrixSet the tile matrix set
 * @param zoomLevels the range of zoom levels
 * @param center the center point
 * @param collectionId the collection, empty = all collections in the dataset
 * @param dataType vector, map or coverage
 * @param links links to include in the object
 * @param uriCustomizer optional URI of the resource
 * @param limitsGenerator helper to generate the limits for each zoom level based on the bbox of the data
 * @param providers helper to access feature providers
 * @return the tile set metadata
 */
public static TileSet buildTileSet(OgcApi api, TileMatrixSet tileMatrixSet, MinMax zoomLevels, List<Double> center, Optional<String> collectionId, TileSet.DataType dataType, List<Link> links, Optional<URICustomizer> uriCustomizer, CrsTransformerFactory crsTransformerFactory, TileMatrixSetLimitsGenerator limitsGenerator, FeaturesCoreProviders providers, EntityRegistry entityRegistry) {
    OgcApiDataV2 apiData = api.getData();
    Builder builder = ImmutableTileSet.builder().dataType(dataType);
    builder.tileMatrixSetId(tileMatrixSet.getId());
    if (tileMatrixSet.getURI().isPresent())
        builder.tileMatrixSetURI(tileMatrixSet.getURI().get().toString());
    else
        builder.tileMatrixSet(tileMatrixSet.getTileMatrixSetData());
    uriCustomizer.ifPresent(uriCustomizer1 -> builder.tileMatrixSetDefinition(uriCustomizer1.removeLastPathSegments(collectionId.isPresent() ? 3 : 1).clearParameters().ensureLastPathSegments("tileMatrixSets", tileMatrixSet.getId()).toString()));
    if (Objects.isNull(zoomLevels))
        builder.tileMatrixSetLimits(ImmutableList.of());
    else
        builder.tileMatrixSetLimits(collectionId.isPresent() ? limitsGenerator.getCollectionTileMatrixSetLimits(api, collectionId.get(), tileMatrixSet, zoomLevels) : limitsGenerator.getTileMatrixSetLimits(api, tileMatrixSet, zoomLevels));
    try {
        BoundingBox boundingBox = api.getSpatialExtent(collectionId).orElse(tileMatrixSet.getBoundingBoxCrs84(crsTransformerFactory));
        builder.boundingBox(ImmutableTilesBoundingBox.builder().lowerLeft(BigDecimal.valueOf(boundingBox.getXmin()).setScale(7, RoundingMode.HALF_UP), BigDecimal.valueOf(boundingBox.getYmin()).setScale(7, RoundingMode.HALF_UP)).upperRight(BigDecimal.valueOf(boundingBox.getXmax()).setScale(7, RoundingMode.HALF_UP), BigDecimal.valueOf(boundingBox.getYmax()).setScale(7, RoundingMode.HALF_UP)).crs(OgcCrs.CRS84.toUriString()).build());
    } catch (CrsTransformationException e) {
    // ignore, just skip the boundingBox
    }
    if ((Objects.nonNull(zoomLevels) && zoomLevels.getDefault().isPresent()) || !center.isEmpty()) {
        ImmutableTilePoint.Builder builder2 = new ImmutableTilePoint.Builder();
        if (Objects.nonNull(zoomLevels))
            zoomLevels.getDefault().ifPresent(def -> builder2.tileMatrix(String.valueOf(def)));
        if (!center.isEmpty())
            builder2.coordinates(center);
        builder.centerPoint(builder2.build());
    }
    // prepare a map with the JSON schemas of the feature collections used in the style
    JsonSchemaCache schemas = new SchemaCacheTileSet(() -> entityRegistry.getEntitiesForType(Codelist.class));
    Map<String, JsonSchemaDocument> schemaMap = collectionId.isPresent() ? apiData.getCollectionData(collectionId.get()).filter(collectionData -> {
        Optional<TilesConfiguration> config = collectionData.getExtension(TilesConfiguration.class);
        return collectionData.getEnabled() && config.isPresent() && config.get().isEnabled();
    }).map(collectionData -> {
        Optional<FeatureSchema> schema = providers.getFeatureSchema(apiData, collectionData);
        if (schema.isPresent())
            return ImmutableMap.of(collectionId.get(), schemas.getSchema(schema.get(), apiData, collectionData, Optional.empty()));
        return null;
    }).filter(Objects::nonNull).orElse(ImmutableMap.of()) : apiData.getCollections().entrySet().stream().filter(entry -> {
        Optional<TilesConfiguration> config = entry.getValue().getExtension(TilesConfiguration.class);
        return entry.getValue().getEnabled() && config.isPresent() && config.get().isMultiCollectionEnabled();
    }).map(entry -> {
        Optional<FeatureSchema> schema = providers.getFeatureSchema(apiData, entry.getValue());
        if (schema.isPresent())
            return new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), schemas.getSchema(schema.get(), apiData, entry.getValue(), Optional.empty()));
        return null;
    }).filter(Objects::nonNull).collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
    // TODO: replace with SchemaDeriverTileLayers
    schemaMap.entrySet().stream().forEach(entry -> {
        String collectionId2 = entry.getKey();
        FeatureTypeConfigurationOgcApi collectionData = apiData.getCollections().get(collectionId2);
        JsonSchemaDocument schema = entry.getValue();
        ImmutableTileLayer.Builder builder2 = ImmutableTileLayer.builder().id(collectionId2).title(collectionData.getLabel()).description(collectionData.getDescription()).dataType(dataType);
        collectionData.getExtension(TilesConfiguration.class).map(config -> config.getZoomLevelsDerived().get(tileMatrixSet.getId())).ifPresent(minmax -> builder2.minTileMatrix(String.valueOf(minmax.getMin())).maxTileMatrix(String.valueOf(minmax.getMax())));
        final JsonSchema geometry = schema.getProperties().get("geometry");
        if (Objects.nonNull(geometry)) {
            String geomAsString = geometry.toString();
            boolean point = geomAsString.contains("GeoJSON Point") || geomAsString.contains("GeoJSON MultiPoint");
            boolean line = geomAsString.contains("GeoJSON LineString") || geomAsString.contains("GeoJSON MultiLineString");
            boolean polygon = geomAsString.contains("GeoJSON Polygon") || geomAsString.contains("GeoJSON MultiPolygon");
            if (point && !line && !polygon)
                builder2.geometryType(TileLayer.GeometryType.points);
            else if (!point && line && !polygon)
                builder2.geometryType(TileLayer.GeometryType.lines);
            else if (!point && !line && polygon)
                builder2.geometryType(TileLayer.GeometryType.polygons);
        }
        final JsonSchemaObject properties = (JsonSchemaObject) schema.getProperties().get("properties");
        builder2.propertiesSchema(ImmutableJsonSchemaObject.builder().required(properties.getRequired()).properties(properties.getProperties()).patternProperties(properties.getPatternProperties()).build());
        builder.addLayers(builder2.build());
    });
    builder.links(links);
    return builder.build();
}
Also used : ImmutableTileSet(de.ii.ogcapi.tiles.domain.ImmutableTileSet) SimpleFeatureGeometry(de.ii.xtraplatform.geometries.domain.SimpleFeatureGeometry) CrsTransformationException(de.ii.xtraplatform.crs.domain.CrsTransformationException) Link(de.ii.ogcapi.foundation.domain.Link) LoggerFactory(org.slf4j.LoggerFactory) EpsgCrs(de.ii.xtraplatform.crs.domain.EpsgCrs) JsonSchemaObject(de.ii.ogcapi.features.core.domain.JsonSchemaObject) VectorLayer(de.ii.ogcapi.tiles.domain.VectorLayer) TileLayer(de.ii.ogcapi.tiles.domain.TileLayer) BigDecimal(java.math.BigDecimal) JsonSchemaDocument(de.ii.ogcapi.features.core.domain.JsonSchemaDocument) Locale(java.util.Locale) Map(java.util.Map) JsonSchemaCache(de.ii.ogcapi.features.core.domain.JsonSchemaCache) FeatureSchema(de.ii.xtraplatform.features.domain.FeatureSchema) RoundingMode(java.math.RoundingMode) FeaturesCoreProviders(de.ii.ogcapi.features.core.domain.FeaturesCoreProviders) ImmutableMap(com.google.common.collect.ImmutableMap) ImmutableVectorLayer(de.ii.ogcapi.tiles.domain.ImmutableVectorLayer) OgcApi(de.ii.ogcapi.foundation.domain.OgcApi) Codelist(de.ii.xtraplatform.codelists.domain.Codelist) Collectors(java.util.stream.Collectors) TilePoint(de.ii.ogcapi.tiles.domain.TilePoint) Objects(java.util.Objects) List(java.util.List) OgcApiDataV2(de.ii.ogcapi.foundation.domain.OgcApiDataV2) TileMatrixSet(de.ii.ogcapi.tiles.domain.tileMatrixSet.TileMatrixSet) CrsTransformerFactory(de.ii.xtraplatform.crs.domain.CrsTransformerFactory) Optional(java.util.Optional) BoundingBox(de.ii.xtraplatform.crs.domain.BoundingBox) FeatureTypeConfigurationOgcApi(de.ii.ogcapi.foundation.domain.FeatureTypeConfigurationOgcApi) EntityRegistry(de.ii.xtraplatform.store.domain.entities.EntityRegistry) TileMatrixSetLimits(de.ii.ogcapi.tiles.domain.tileMatrixSet.TileMatrixSetLimits) CrsTransformer(de.ii.xtraplatform.crs.domain.CrsTransformer) TileSet(de.ii.ogcapi.tiles.domain.TileSet) AtomicReference(java.util.concurrent.atomic.AtomicReference) TilesConfiguration(de.ii.ogcapi.tiles.domain.TilesConfiguration) ImmutableTileLayer(de.ii.ogcapi.tiles.domain.ImmutableTileLayer) ImmutableTilePoint(de.ii.ogcapi.tiles.domain.ImmutableTilePoint) TileMatrixSetLimitsGenerator(de.ii.ogcapi.tiles.domain.tileMatrixSet.TileMatrixSetLimitsGenerator) SchemaBase(de.ii.xtraplatform.features.domain.SchemaBase) ImmutableList(com.google.common.collect.ImmutableList) OgcCrs(de.ii.xtraplatform.crs.domain.OgcCrs) ImmutableFields(de.ii.ogcapi.tiles.domain.ImmutableFields) Builder(de.ii.ogcapi.tiles.domain.ImmutableTileSet.Builder) Logger(org.slf4j.Logger) ImmutableJsonSchemaObject(de.ii.ogcapi.features.core.domain.ImmutableJsonSchemaObject) MinMax(de.ii.ogcapi.tiles.domain.MinMax) TilesBoundingBox(de.ii.ogcapi.tiles.domain.tileMatrixSet.TilesBoundingBox) URICustomizer(de.ii.ogcapi.foundation.domain.URICustomizer) FeaturesCoreConfiguration(de.ii.ogcapi.features.core.domain.FeaturesCoreConfiguration) GeoJsonConfiguration(de.ii.ogcapi.features.geojson.domain.GeoJsonConfiguration) JsonSchema(de.ii.ogcapi.features.core.domain.JsonSchema) ImmutableTilesBoundingBox(de.ii.ogcapi.tiles.domain.tileMatrixSet.ImmutableTilesBoundingBox) AbstractMap(java.util.AbstractMap) SchemaInfo(de.ii.ogcapi.features.core.domain.SchemaInfo) Codelist(de.ii.xtraplatform.codelists.domain.Codelist) Builder(de.ii.ogcapi.tiles.domain.ImmutableTileSet.Builder) JsonSchema(de.ii.ogcapi.features.core.domain.JsonSchema) TilesConfiguration(de.ii.ogcapi.tiles.domain.TilesConfiguration) ImmutableTilePoint(de.ii.ogcapi.tiles.domain.ImmutableTilePoint) FeatureTypeConfigurationOgcApi(de.ii.ogcapi.foundation.domain.FeatureTypeConfigurationOgcApi) ImmutableTileLayer(de.ii.ogcapi.tiles.domain.ImmutableTileLayer) BoundingBox(de.ii.xtraplatform.crs.domain.BoundingBox) TilesBoundingBox(de.ii.ogcapi.tiles.domain.tileMatrixSet.TilesBoundingBox) ImmutableTilesBoundingBox(de.ii.ogcapi.tiles.domain.tileMatrixSet.ImmutableTilesBoundingBox) JsonSchemaDocument(de.ii.ogcapi.features.core.domain.JsonSchemaDocument) Optional(java.util.Optional) OgcApiDataV2(de.ii.ogcapi.foundation.domain.OgcApiDataV2) JsonSchemaCache(de.ii.ogcapi.features.core.domain.JsonSchemaCache) CrsTransformationException(de.ii.xtraplatform.crs.domain.CrsTransformationException) Objects(java.util.Objects) JsonSchemaObject(de.ii.ogcapi.features.core.domain.JsonSchemaObject) ImmutableJsonSchemaObject(de.ii.ogcapi.features.core.domain.ImmutableJsonSchemaObject)

Example 2 with BoundingBox

use of de.ii.xtraplatform.crs.domain.BoundingBox in project ldproxy by interactive-instruments.

the class TileCacheImpl method deleteTilesMbtiles.

private void deleteTilesMbtiles(OgcApi api, Optional<String> collectionId, Map<String, MinMax> zoomLevels, Map<String, BoundingBox> boundingBoxes) throws SQLException, IOException {
    for (Map.Entry<String, MinMax> tileSet : zoomLevels.entrySet()) {
        TileMatrixSet tileMatrixSet = getTileMatrixSetById(tileSet.getKey());
        MinMax levels = tileSet.getValue();
        BoundingBox bbox = boundingBoxes.get(tileSet.getKey());
        // first the dataset tiles
        deleteTilesMbtiles(api, Optional.empty(), tileMatrixSet, levels, bbox);
        if (collectionId.isPresent()) {
            // also the single collection tiles for the collection
            deleteTilesMbtiles(api, collectionId, tileMatrixSet, levels, bbox);
        } else {
            // all single collection tiles
            for (String colId : api.getData().getCollections().keySet()) {
                deleteTilesMbtiles(api, Optional.of(colId), tileMatrixSet, levels, bbox);
            }
        }
    }
}
Also used : TileMatrixSet(de.ii.ogcapi.tiles.domain.tileMatrixSet.TileMatrixSet) BoundingBox(de.ii.xtraplatform.crs.domain.BoundingBox) Map(java.util.Map) HashMap(java.util.HashMap) MinMax(de.ii.ogcapi.tiles.domain.MinMax)

Example 3 with BoundingBox

use of de.ii.xtraplatform.crs.domain.BoundingBox in project ldproxy by interactive-instruments.

the class CapabilityFeaturesCore method computeBbox.

private Optional<BoundingBox> computeBbox(OgcApiDataV2 apiData, String collectionId) {
    FeatureTypeConfigurationOgcApi collectionData = apiData.getCollections().get(collectionId);
    Optional<FeatureProvider2> featureProvider = providers.getFeatureProvider(apiData, collectionData);
    if (featureProvider.map(FeatureProvider2::supportsExtents).orElse(false)) {
        Optional<BoundingBox> spatialExtent = featureProvider.get().extents().getSpatialExtent(collectionId);
        if (spatialExtent.isPresent()) {
            BoundingBox boundingBox = spatialExtent.get();
            if (!boundingBox.getEpsgCrs().equals(OgcCrs.CRS84) && !boundingBox.getEpsgCrs().equals(OgcCrs.CRS84h)) {
                Optional<CrsTransformer> transformer = crsTransformerFactory.getTransformer(boundingBox.getEpsgCrs(), OgcCrs.CRS84);
                if (transformer.isPresent()) {
                    try {
                        boundingBox = transformer.get().transformBoundingBox(boundingBox);
                    } catch (CrsTransformationException e) {
                        LOGGER.error("Error while computing spatial extent of collection '{}' while transforming the CRS of the bounding box: {}", collectionId, e.getMessage());
                        return Optional.empty();
                    }
                }
            }
            return Optional.of(boundingBox);
        }
    }
    return Optional.empty();
}
Also used : FeatureTypeConfigurationOgcApi(de.ii.ogcapi.foundation.domain.FeatureTypeConfigurationOgcApi) FeatureProvider2(de.ii.xtraplatform.features.domain.FeatureProvider2) BoundingBox(de.ii.xtraplatform.crs.domain.BoundingBox) CrsTransformationException(de.ii.xtraplatform.crs.domain.CrsTransformationException) CrsTransformer(de.ii.xtraplatform.crs.domain.CrsTransformer)

Example 4 with BoundingBox

use of de.ii.xtraplatform.crs.domain.BoundingBox in project ldproxy by interactive-instruments.

the class EndpointCollection method onStartup.

@Override
public ValidationResult onStartup(OgcApi api, MODE apiValidation) {
    ValidationResult result = super.onStartup(api, apiValidation);
    if (apiValidation == MODE.NONE)
        return result;
    ImmutableValidationResult.Builder builder = ImmutableValidationResult.builder().from(result).mode(apiValidation);
    for (FeatureTypeConfigurationOgcApi collectionData : api.getData().getCollections().values()) {
        builder = FoundationValidator.validateLinks(builder, collectionData.getAdditionalLinks(), "/collections/" + collectionData.getId());
        Optional<String> persistentUriTemplate = collectionData.getPersistentUriTemplate();
        if (persistentUriTemplate.isPresent()) {
            Pattern valuePattern = Pattern.compile("\\{\\{[\\w\\.]+( ?\\| ?[\\w]+(:'[^']*')*)*\\}\\}");
            Matcher matcher = valuePattern.matcher(persistentUriTemplate.get());
            if (!matcher.find()) {
                builder.addStrictErrors(MessageFormat.format("Persistent URI template ''{0}'' in collection ''{1}'' does not have a valid value pattern.", persistentUriTemplate.get(), collectionData.getId()));
            }
        }
        Optional<CollectionExtent> extent = api.getData().getExtent(collectionData.getId());
        if (extent.isPresent()) {
            Optional<BoundingBox> spatial = extent.get().getSpatial();
            if (spatial.isPresent() && Objects.nonNull(spatial.get())) {
                BoundingBox bbox = spatial.get();
                if (!ImmutableSet.of(4326, 4979).contains(bbox.getEpsgCrs().getCode()) || bbox.getEpsgCrs().getForceAxisOrder() != EpsgCrs.Force.LON_LAT) {
                    builder.addStrictErrors(MessageFormat.format("The spatial extent in collection ''{0}'' must be in CRS84 or CRS84h. Found: ''{1}, {2}''.", collectionData.getId(), bbox.getEpsgCrs().toSimpleString(), bbox.getEpsgCrs().getForceAxisOrder()));
                }
                if (bbox.getXmin() < -180.0 || bbox.getXmin() > 180.0) {
                    builder.addStrictErrors(MessageFormat.format("The spatial extent in collection ''{0}'' has a longitude value that is not between -180 and 180. Found: ''{1}''.", collectionData.getId(), bbox.getXmin()));
                }
                if (bbox.getXmax() < -180.0 || bbox.getXmax() > 180.0) {
                    builder.addStrictErrors(MessageFormat.format("The spatial extent in collection ''{0}'' has a longitude value that is not between -180 and 180. Found: ''{1}''.", collectionData.getId(), bbox.getXmax()));
                }
                if (bbox.getYmin() < -90.0 || bbox.getYmin() > 90.0) {
                    builder.addStrictErrors(MessageFormat.format("The spatial extent in collection ''{0}'' has a latitude value that is not between -90 and 90. Found: ''{1}''.", collectionData.getId(), bbox.getYmin()));
                }
                if (bbox.getYmax() < -90.0 || bbox.getYmax() > 90.0) {
                    builder.addStrictErrors(MessageFormat.format("The spatial extent in collection ''{0}'' has a latitude value that is not between -90 and 90. Found: ''{1}''.", collectionData.getId(), bbox.getYmax()));
                }
                if (bbox.getYmax() < bbox.getYmin()) {
                    builder.addStrictErrors(MessageFormat.format("The spatial extent in collection ''{0}'' has a maxmimum latitude value ''{1}'' that is lower than the minimum value ''{2}''.", collectionData.getId(), bbox.getYmax(), bbox.getYmin()));
                }
            }
            Optional<TemporalExtent> temporal = extent.get().getTemporal();
            if (temporal.isPresent() && Objects.nonNull(temporal.get())) {
                long start = Objects.nonNull(temporal.get().getStart()) ? temporal.get().getStart() : Long.MIN_VALUE;
                long end = Objects.nonNull(temporal.get().getEnd()) ? temporal.get().getEnd() : Long.MAX_VALUE;
                if (end < start) {
                    builder.addStrictErrors(MessageFormat.format("The temporal extent in collection ''{0}'' has an end ''{1}'' before the start ''{2}''.", collectionData.getId(), Instant.ofEpochMilli(end).truncatedTo(ChronoUnit.SECONDS).toString(), Instant.ofEpochMilli(start).truncatedTo(ChronoUnit.SECONDS).toString()));
                }
            }
        }
    }
    return builder.build();
}
Also used : Pattern(java.util.regex.Pattern) Matcher(java.util.regex.Matcher) ImmutableValidationResult(de.ii.xtraplatform.store.domain.entities.ImmutableValidationResult) ImmutableValidationResult(de.ii.xtraplatform.store.domain.entities.ImmutableValidationResult) ValidationResult(de.ii.xtraplatform.store.domain.entities.ValidationResult) CollectionExtent(de.ii.ogcapi.foundation.domain.CollectionExtent) FeatureTypeConfigurationOgcApi(de.ii.ogcapi.foundation.domain.FeatureTypeConfigurationOgcApi) TemporalExtent(de.ii.ogcapi.foundation.domain.TemporalExtent) BoundingBox(de.ii.xtraplatform.crs.domain.BoundingBox)

Example 5 with BoundingBox

use of de.ii.xtraplatform.crs.domain.BoundingBox in project ldproxy by interactive-instruments.

the class AbstractTileMatrixSet method getTileMatrixSetData.

@Override
public TileMatrixSetData getTileMatrixSetData() {
    if (Objects.nonNull(data)) {
        return data;
    }
    BoundingBox bbox = getBoundingBox();
    data = ImmutableTileMatrixSetData.builder().id(getId()).title(getTitle()).description(getDescription()).keywords(getKeywords()).orderedAxes(getOrderedAxes()).crs(getCrs().toUriString()).uri(getURI()).wellKnownScaleSet(getWellKnownScaleSet()).boundingBox(ImmutableTilesBoundingBox.builder().crs(getCrs().toUriString()).lowerLeft(getBigDecimal(bbox.getXmin()), getBigDecimal(bbox.getYmin())).upperRight(getBigDecimal(bbox.getXmax()), getBigDecimal(bbox.getYmax())).build()).tileMatrices(getTileMatrices(getMinLevel(), getMaxLevel())).build();
    return data;
}
Also used : ImmutableTilesBoundingBox(de.ii.ogcapi.tiles.domain.tileMatrixSet.ImmutableTilesBoundingBox) BoundingBox(de.ii.xtraplatform.crs.domain.BoundingBox)

Aggregations

BoundingBox (de.ii.xtraplatform.crs.domain.BoundingBox)16 AutoBind (com.github.azahnen.dagger.annotations.AutoBind)6 FeatureTypeConfigurationOgcApi (de.ii.ogcapi.foundation.domain.FeatureTypeConfigurationOgcApi)6 OgcApi (de.ii.ogcapi.foundation.domain.OgcApi)6 OgcApiDataV2 (de.ii.ogcapi.foundation.domain.OgcApiDataV2)6 List (java.util.List)6 Map (java.util.Map)6 Optional (java.util.Optional)6 Inject (javax.inject.Inject)6 Singleton (javax.inject.Singleton)6 ImmutableList (com.google.common.collect.ImmutableList)5 TileMatrixSet (de.ii.ogcapi.tiles.domain.tileMatrixSet.TileMatrixSet)5 Objects (java.util.Objects)5 Collectors (java.util.stream.Collectors)5 FeaturesCoreConfiguration (de.ii.ogcapi.features.core.domain.FeaturesCoreConfiguration)4 FeaturesCoreProviders (de.ii.ogcapi.features.core.domain.FeaturesCoreProviders)4 ExtensionRegistry (de.ii.ogcapi.foundation.domain.ExtensionRegistry)4 TemporalExtent (de.ii.ogcapi.foundation.domain.TemporalExtent)4 TileSet (de.ii.ogcapi.tiles.domain.TileSet)4 TilesConfiguration (de.ii.ogcapi.tiles.domain.TilesConfiguration)4