use of de.ii.ogcapi.tiles.domain.TileFormatExtension in project ldproxy by interactive-instruments.
the class TilesQueriesHandlerImpl method getTileSetResponse.
private Response getTileSetResponse(QueryInputTileSet queryInput, ApiRequestContext requestContext) {
OgcApi api = requestContext.getApi();
OgcApiDataV2 apiData = api.getData();
String tileMatrixSetId = queryInput.getTileMatrixSetId();
Optional<String> collectionId = queryInput.getCollectionId();
String definitionPath = queryInput.getPath();
String path = collectionId.map(value -> definitionPath.replace("{collectionId}", value)).orElse(definitionPath).replace("{tileMatrixSetId}", tileMatrixSetId);
TileSetFormatExtension outputFormat = api.getOutputFormat(TileSetFormatExtension.class, requestContext.getMediaType(), path, collectionId).orElseThrow(() -> new NotAcceptableException(MessageFormat.format("The requested media type ''{0}'' is not supported for this resource.", requestContext.getMediaType())));
List<TileFormatExtension> tileFormats = extensionRegistry.getExtensionsForType(TileFormatExtension.class).stream().filter(format -> collectionId.map(s -> format.isApplicable(apiData, s, definitionPath)).orElseGet(() -> format.isApplicable(apiData, definitionPath))).collect(Collectors.toUnmodifiableList());
DataType dataType = tileFormats.stream().map(TileFormatExtension::getDataType).findAny().orElseThrow(() -> new NotFoundException("No encoding found for this tile set."));
final TilesLinkGenerator tilesLinkGenerator = new TilesLinkGenerator();
List<Link> links = tilesLinkGenerator.generateTileSetLinks(requestContext.getUriCustomizer(), requestContext.getMediaType(), requestContext.getAlternateMediaTypes(), tileFormats, i18n, requestContext.getLanguage());
MinMax zoomLevels = queryInput.getZoomLevels();
List<Double> center = queryInput.getCenter();
TileSet tileset = TilesHelper.buildTileSet(api, getTileMatrixSetById(tileMatrixSetId), zoomLevels, center, collectionId, dataType, links, Optional.of(requestContext.getUriCustomizer().copy()), crsTransformerFactory, limitsGenerator, providers, entityRegistry);
Date lastModified = getLastModified(queryInput, requestContext.getApi());
EntityTag etag = !outputFormat.getMediaType().type().equals(MediaType.TEXT_HTML_TYPE) || (collectionId.isEmpty() ? apiData.getExtension(HtmlConfiguration.class) : apiData.getExtension(HtmlConfiguration.class, collectionId.get())).map(HtmlConfiguration::getSendEtags).orElse(false) ? getEtag(tileset, TileSet.FUNNEL, outputFormat) : null;
Response.ResponseBuilder response = evaluatePreconditions(requestContext, lastModified, etag);
if (Objects.nonNull(response))
return response.build();
return prepareSuccessResponse(requestContext, queryInput.getIncludeLinkHeader() ? links : null, lastModified, etag, queryInput.getCacheControl().orElse(null), queryInput.getExpires().orElse(null), null, true, String.format("%s.%s", tileset.getTileMatrixSetId(), outputFormat.getMediaType().fileExtension())).entity(outputFormat.getTileSetEntity(tileset, apiData, collectionId, requestContext)).build();
}
use of de.ii.ogcapi.tiles.domain.TileFormatExtension in project ldproxy by interactive-instruments.
the class TileProviderFeatures method getQueryInput.
@Override
@JsonIgnore
@Value.Derived
public QueryInput getQueryInput(OgcApiDataV2 apiData, URICustomizer uriCustomizer, Map<String, String> queryParameters, List<OgcApiQueryParameter> allowedParameters, QueryInput genericInput, Tile tile) {
if (!tile.getFeatureProvider().map(FeatureProvider2::supportsQueries).orElse(false)) {
throw new IllegalStateException("Tile cannot be generated. The feature provider does not support feature queries.");
}
TileFormatExtension outputFormat = tile.getOutputFormat();
List<String> collections = tile.getCollectionIds();
if (collections.isEmpty()) {
return new ImmutableQueryInputTileEmpty.Builder().from(genericInput).tile(tile).build();
}
if (!(outputFormat instanceof TileFormatWithQuerySupportExtension))
throw new RuntimeException(String.format("Unexpected tile format without query support. Found: %s", outputFormat.getClass().getSimpleName()));
// first execute the information that is passed as processing parameters (e.g., "properties")
Map<String, Object> processingParameters = new HashMap<>();
for (OgcApiQueryParameter parameter : allowedParameters) {
processingParameters = parameter.transformContext(null, processingParameters, queryParameters, apiData);
}
if (tile.isDatasetTile()) {
if (!outputFormat.canMultiLayer() && collections.size() > 1)
throw new NotAcceptableException("The requested tile format supports only a single layer. Please select only a single collection.");
Map<String, Tile> singleLayerTileMap = collections.stream().collect(ImmutableMap.toImmutableMap(collectionId -> collectionId, collectionId -> new ImmutableTile.Builder().from(tile).collectionIds(ImmutableList.of(collectionId)).isDatasetTile(false).build()));
Map<String, FeatureQuery> queryMap = collections.stream().filter(collectionId -> {
Optional<FeaturesCoreConfiguration> featuresConfiguration = apiData.getCollections().get(collectionId).getExtension(FeaturesCoreConfiguration.class);
return featuresConfiguration.isPresent() && featuresConfiguration.get().getQueryables().isPresent() && !featuresConfiguration.get().getQueryables().get().getSpatial().isEmpty();
}).collect(ImmutableMap.toImmutableMap(collectionId -> collectionId, collectionId -> {
String featureTypeId = apiData.getCollections().get(collectionId).getExtension(FeaturesCoreConfiguration.class).map(cfg -> cfg.getFeatureType().orElse(collectionId)).orElse(collectionId);
TilesConfiguration layerConfiguration = apiData.getExtension(TilesConfiguration.class, collectionId).orElseThrow();
FeatureQuery query = ((TileFormatWithQuerySupportExtension) outputFormat).getQuery(singleLayerTileMap.get(collectionId), allowedParameters, queryParameters, layerConfiguration, uriCustomizer);
return ImmutableFeatureQuery.builder().from(query).type(featureTypeId).build();
}));
FeaturesCoreConfiguration coreConfiguration = apiData.getExtension(FeaturesCoreConfiguration.class).orElseThrow();
return new ImmutableQueryInputTileMultiLayer.Builder().from(genericInput).tile(tile).singleLayerTileMap(singleLayerTileMap).queryMap(queryMap).processingParameters(processingParameters).defaultCrs(apiData.getExtension(FeaturesCoreConfiguration.class).map(FeaturesCoreConfiguration::getDefaultEpsgCrs).orElseThrow()).build();
} else {
String collectionId = tile.getCollectionId();
FeatureTypeConfigurationOgcApi featureType = apiData.getCollectionData(collectionId).orElseThrow();
TilesConfiguration layerConfiguration = apiData.getExtension(TilesConfiguration.class, collectionId).orElseThrow();
FeatureQuery query = ((TileFromFeatureQuery) outputFormat).getQuery(tile, allowedParameters, queryParameters, layerConfiguration, uriCustomizer);
FeaturesCoreConfiguration coreConfiguration = featureType.getExtension(FeaturesCoreConfiguration.class).orElseThrow();
return new ImmutableQueryInputTileSingleLayer.Builder().from(genericInput).tile(tile).query(query).processingParameters(processingParameters).defaultCrs(featureType.getExtension(FeaturesCoreConfiguration.class).map(FeaturesCoreConfiguration::getDefaultEpsgCrs).orElseThrow()).build();
}
}
use of de.ii.ogcapi.tiles.domain.TileFormatExtension in project ldproxy by interactive-instruments.
the class AbstractEndpointTileSingleCollection method getTile.
protected Response getTile(OgcApi api, ApiRequestContext requestContext, UriInfo uriInfo, String definitionPath, String collectionId, String tileMatrixSetId, String tileMatrix, String tileRow, String tileCol, TileProvider tileProvider) throws CrsTransformationException, IOException, NotFoundException {
OgcApiDataV2 apiData = api.getData();
Map<String, String> queryParams = toFlatMap(uriInfo.getQueryParameters());
FeatureTypeConfigurationOgcApi featureType = apiData.getCollections().get(collectionId);
TilesConfiguration tilesConfiguration = featureType.getExtension(TilesConfiguration.class).orElseThrow();
checkPathParameter(extensionRegistry, apiData, definitionPath, "collectionId", collectionId);
checkPathParameter(extensionRegistry, apiData, definitionPath, "tileMatrixSetId", tileMatrixSetId);
checkPathParameter(extensionRegistry, apiData, definitionPath, "tileMatrix", tileMatrix);
checkPathParameter(extensionRegistry, apiData, definitionPath, "tileRow", tileRow);
checkPathParameter(extensionRegistry, apiData, definitionPath, "tileCol", tileCol);
final List<OgcApiQueryParameter> allowedParameters = getQueryParameters(extensionRegistry, apiData, definitionPath, collectionId);
int row;
int col;
int level;
try {
level = Integer.parseInt(tileMatrix);
row = Integer.parseInt(tileRow);
col = Integer.parseInt(tileCol);
} catch (NumberFormatException e) {
throw new ServerErrorException("Could not convert tile coordinates that have been validated to integers", 500);
}
MinMax zoomLevels = tilesConfiguration.getZoomLevelsDerived().get(tileMatrixSetId);
if (zoomLevels.getMax() < level || zoomLevels.getMin() > level)
throw new NotFoundException("The requested tile is outside the zoom levels for this tile set.");
TileMatrixSet tileMatrixSet = tileMatrixSetRepository.get(tileMatrixSetId).orElseThrow(() -> new NotFoundException("Unknown tile matrix set: " + tileMatrixSetId));
TileMatrixSetLimits tileLimits = limitsGenerator.getCollectionTileMatrixSetLimits(api, collectionId, tileMatrixSet, zoomLevels).stream().filter(limits -> limits.getTileMatrix().equals(tileMatrix)).findAny().orElse(null);
if (tileLimits != null) {
if (tileLimits.getMaxTileCol() < col || tileLimits.getMinTileCol() > col || tileLimits.getMaxTileRow() < row || tileLimits.getMinTileRow() > row)
// return 404, if outside the range
throw new NotFoundException("The requested tile is outside of the limits for this zoom level and tile set.");
}
String path = definitionPath.replace("{collectionId}", collectionId).replace("{tileMatrixSetId}", tileMatrixSetId).replace("{tileMatrix}", tileMatrix).replace("{tileRow}", tileRow).replace("{tileCol}", tileCol);
TileFormatExtension outputFormat = requestContext.getApi().getOutputFormat(TileFormatExtension.class, requestContext.getMediaType(), path, Optional.of(collectionId)).orElseThrow(() -> new NotAcceptableException(MessageFormat.format("The requested media type ''{0}'' is not supported for this resource.", requestContext.getMediaType())));
Optional<FeatureProvider2> featureProvider = providers.getFeatureProvider(apiData);
// check, if the cache can be used (no query parameters except f)
boolean useCache = tileProvider.tilesMayBeCached() && tilesConfiguration.getCache() != TilesConfiguration.TileCacheType.NONE && (queryParams.isEmpty() || (queryParams.size() == 1 && queryParams.containsKey("f")));
// don't store the tile in the cache if it is outside the range
MinMax cacheMinMax = tilesConfiguration.getZoomLevelsDerived().get(tileMatrixSetId);
useCache = useCache && (Objects.isNull(cacheMinMax) || (level <= cacheMinMax.getMax() && level >= cacheMinMax.getMin()));
Tile tile = new ImmutableTile.Builder().tileMatrixSet(tileMatrixSet).tileLevel(level).tileRow(row).tileCol(col).api(api).apiData(apiData).outputFormat(outputFormat).featureProvider(featureProvider).collectionIds(ImmutableList.of(collectionId)).temporary(!useCache).isDatasetTile(false).build();
QueryInput queryInput = null;
// if cache can be used and the tile is cached for the requested format, return the cache
if (useCache) {
// get the tile from the cache and return it
Optional<InputStream> tileStream = Optional.empty();
try {
tileStream = cache.getTile(tile);
} catch (Exception e) {
LOGGER.warn("Failed to retrieve tile {}/{}/{}/{} for collection '{}' from the cache. Reason: {}", tile.getTileMatrixSet().getId(), tile.getTileLevel(), tile.getTileRow(), tile.getTileCol(), collectionId, e.getMessage());
}
if (tileStream.isPresent()) {
queryInput = new Builder().from(getGenericQueryInput(apiData)).tile(tile).tileContent(tileStream.get()).build();
}
}
// not cached or cache access failed
if (Objects.isNull(queryInput))
queryInput = tileProvider.getQueryInput(apiData, requestContext.getUriCustomizer(), queryParams, allowedParameters, getGenericQueryInput(apiData), tile);
TilesQueriesHandler.Query query = null;
if (queryInput instanceof TilesQueriesHandler.QueryInputTileMbtilesTile)
query = TilesQueriesHandler.Query.MBTILES_TILE;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileTileServerTile)
query = TilesQueriesHandler.Query.TILESERVER_TILE;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileEmpty)
query = TilesQueriesHandler.Query.EMPTY_TILE;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileStream)
query = TilesQueriesHandler.Query.TILE_STREAM;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileMultiLayer)
query = TilesQueriesHandler.Query.MULTI_LAYER_TILE;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileSingleLayer)
query = TilesQueriesHandler.Query.SINGLE_LAYER_TILE;
return queryHandler.handle(query, queryInput, requestContext);
}
use of de.ii.ogcapi.tiles.domain.TileFormatExtension in project ldproxy by interactive-instruments.
the class TileCacheImpl method deleteTilesFiles.
private void deleteTilesFiles(OgcApiDataV2 apiData, Optional<String> collectionId, Map<String, MinMax> zoomLevels, Map<String, BoundingBox> boundingBoxes) throws IOException {
List<String> extensions = getTileFormats(apiData, collectionId).stream().map(TileFormatExtension::getExtension).collect(ImmutableList.toImmutableList());
Map<String, Map<String, TileMatrixSetLimits>> limits = zoomLevels.keySet().stream().map(tmsId -> {
Map<String, TileMatrixSetLimits> limitsMap = getLimits(apiData, getTileMatrixSetById(tmsId), zoomLevels.get(tmsId), collectionId, boundingBoxes.get(tmsId)).stream().map(l -> new SimpleImmutableEntry<>(l.getTileMatrix(), l)).collect(Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue));
return new SimpleImmutableEntry<>(tmsId, limitsMap);
}).collect(Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue));
Path basePath = getTilesStore().resolve(apiData.getId());
try (Stream<Path> walk = Files.find(basePath, 5, (path, basicFileAttributes) -> basicFileAttributes.isRegularFile() && shouldDeleteTileFile(basePath.relativize(path), collectionId, limits, extensions))) {
walk.map(Path::toFile).forEach(File::delete);
}
}
use of de.ii.ogcapi.tiles.domain.TileFormatExtension in project ldproxy by interactive-instruments.
the class AbstractEndpointTileMultiCollection method getTile.
protected Response getTile(OgcApi api, ApiRequestContext requestContext, UriInfo uriInfo, String definitionPath, String tileMatrixSetId, String tileMatrix, String tileRow, String tileCol, TileProvider tileProvider) throws CrsTransformationException, IOException, NotFoundException {
OgcApiDataV2 apiData = api.getData();
Map<String, String> queryParams = toFlatMap(uriInfo.getQueryParameters());
TilesConfiguration tilesConfiguration = apiData.getExtension(TilesConfiguration.class).orElseThrow();
checkPathParameter(extensionRegistry, apiData, definitionPath, "tileMatrixSetId", tileMatrixSetId);
checkPathParameter(extensionRegistry, apiData, definitionPath, "tileMatrix", tileMatrix);
checkPathParameter(extensionRegistry, apiData, definitionPath, "tileRow", tileRow);
checkPathParameter(extensionRegistry, apiData, definitionPath, "tileCol", tileCol);
final List<OgcApiQueryParameter> allowedParameters = getQueryParameters(extensionRegistry, apiData, definitionPath);
int row;
int col;
int level;
try {
level = Integer.parseInt(tileMatrix);
row = Integer.parseInt(tileRow);
col = Integer.parseInt(tileCol);
} catch (NumberFormatException e) {
throw new ServerErrorException("Could not convert tile coordinates that have been validated to integers", 500);
}
MinMax zoomLevels = tilesConfiguration.getZoomLevelsDerived().get(tileMatrixSetId);
if (zoomLevels.getMax() < level || zoomLevels.getMin() > level)
throw new NotFoundException("The requested tile is outside the zoom levels for this tile set.");
TileMatrixSet tileMatrixSet = tileMatrixSetRepository.get(tileMatrixSetId).orElseThrow(() -> new NotFoundException("Unknown tile matrix set: " + tileMatrixSetId));
TileMatrixSetLimits tileLimits = limitsGenerator.getTileMatrixSetLimits(api, tileMatrixSet, zoomLevels).stream().filter(limits -> limits.getTileMatrix().equals(tileMatrix)).findAny().orElse(null);
if (tileLimits != null) {
if (tileLimits.getMaxTileCol() < col || tileLimits.getMinTileCol() > col || tileLimits.getMaxTileRow() < row || tileLimits.getMinTileRow() > row)
// return 404, if outside the range
throw new NotFoundException("The requested tile is outside of the limits for this zoom level and tile set.");
}
String path = definitionPath.replace("{tileMatrixSetId}", tileMatrixSetId).replace("{tileMatrix}", tileMatrix).replace("{tileRow}", tileRow).replace("{tileCol}", tileCol);
TileFormatExtension outputFormat = requestContext.getApi().getOutputFormat(TileFormatExtension.class, requestContext.getMediaType(), path, Optional.empty()).orElseThrow(() -> new NotAcceptableException(MessageFormat.format("The requested media type ''{0}'' is not supported for this resource.", requestContext.getMediaType())));
Optional<FeatureProvider2> featureProvider = providers.getFeatureProvider(apiData);
List<String> collections = queryParams.containsKey("collections") ? Splitter.on(",").splitToList(queryParams.get("collections")) : apiData.getCollections().values().stream().filter(collection -> apiData.isCollectionEnabled(collection.getId())).filter(collection -> {
Optional<TilesConfiguration> layerConfiguration = collection.getExtension(TilesConfiguration.class);
if (layerConfiguration.isEmpty() || !layerConfiguration.get().isEnabled() || !layerConfiguration.get().isMultiCollectionEnabled())
return false;
MinMax levels = layerConfiguration.get().getZoomLevelsDerived().get(tileMatrixSetId);
return !Objects.nonNull(levels) || (levels.getMax() >= level && levels.getMin() <= level);
}).map(FeatureTypeConfiguration::getId).collect(Collectors.toList());
// check, if the cache can be used (no query parameters except f)
boolean useCache = tileProvider.tilesMayBeCached() && tilesConfiguration.getCache() != TilesConfiguration.TileCacheType.NONE && (queryParams.isEmpty() || (queryParams.size() == 1 && queryParams.containsKey("f")));
// don't store the tile in the cache if it is outside the range
MinMax cacheMinMax = tilesConfiguration.getZoomLevelsDerived().get(tileMatrixSetId);
useCache = useCache && (Objects.isNull(cacheMinMax) || (level <= cacheMinMax.getMax() && level >= cacheMinMax.getMin()));
Tile tile = new ImmutableTile.Builder().tileMatrixSet(tileMatrixSet).tileLevel(level).tileRow(row).tileCol(col).api(api).apiData(apiData).outputFormat(outputFormat).featureProvider(featureProvider).collectionIds(collections).temporary(!useCache).isDatasetTile(true).build();
QueryInput queryInput = null;
// if cache can be used and the tile is cached for the requested format, return the cache
if (useCache) {
// get the tile from the cache and return it
Optional<InputStream> tileStream = Optional.empty();
try {
tileStream = cache.getTile(tile);
} catch (Exception e) {
LOGGER.warn("Failed to retrieve multi-collection tile {}/{}/{}/{} from the cache. Reason: {}", tile.getTileMatrixSet().getId(), tile.getTileLevel(), tile.getTileRow(), tile.getTileCol(), e.getMessage());
}
if (tileStream.isPresent()) {
queryInput = new Builder().from(getGenericQueryInput(apiData)).tile(tile).tileContent(tileStream.get()).build();
}
}
// not cached or cache access failed
if (Objects.isNull(queryInput))
queryInput = tileProvider.getQueryInput(apiData, requestContext.getUriCustomizer(), queryParams, allowedParameters, getGenericQueryInput(apiData), tile);
TilesQueriesHandler.Query query = null;
if (queryInput instanceof TilesQueriesHandler.QueryInputTileMbtilesTile)
query = TilesQueriesHandler.Query.MBTILES_TILE;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileTileServerTile)
query = TilesQueriesHandler.Query.TILESERVER_TILE;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileEmpty)
query = TilesQueriesHandler.Query.EMPTY_TILE;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileStream)
query = TilesQueriesHandler.Query.TILE_STREAM;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileMultiLayer)
query = TilesQueriesHandler.Query.MULTI_LAYER_TILE;
else if (queryInput instanceof TilesQueriesHandler.QueryInputTileSingleLayer)
query = TilesQueriesHandler.Query.SINGLE_LAYER_TILE;
return queryHandler.handle(query, queryInput, requestContext);
}
Aggregations