use of de.ii.ogcapi.foundation.domain.OgcApi 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.foundation.domain.OgcApi in project ldproxy by interactive-instruments.
the class TilesQueriesHandlerImpl method getMultiLayerTileResponse.
private Response getMultiLayerTileResponse(QueryInputTileMultiLayer queryInput, ApiRequestContext requestContext) {
OgcApi api = requestContext.getApi();
OgcApiDataV2 apiData = api.getData();
Tile multiLayerTile = queryInput.getTile();
List<String> collectionIds = multiLayerTile.getCollectionIds();
Map<String, FeatureQuery> queryMap = queryInput.getQueryMap();
Map<String, Tile> singleLayerTileMap = queryInput.getSingleLayerTileMap();
FeatureProvider2 featureProvider = multiLayerTile.getFeatureProvider().get();
TileMatrixSet tileMatrixSet = multiLayerTile.getTileMatrixSet();
int tileLevel = multiLayerTile.getTileLevel();
int tileRow = multiLayerTile.getTileRow();
int tileCol = multiLayerTile.getTileCol();
if (!(multiLayerTile.getOutputFormat() instanceof TileFormatWithQuerySupportExtension))
throw new RuntimeException(String.format("Unexpected tile format without query support. Found: %s", multiLayerTile.getOutputFormat().getClass().getSimpleName()));
TileFormatWithQuerySupportExtension outputFormat = (TileFormatWithQuerySupportExtension) multiLayerTile.getOutputFormat();
// process parameters and generate query
Optional<CrsTransformer> crsTransformer = Optional.empty();
EpsgCrs targetCrs = tileMatrixSet.getCrs();
if (featureProvider.supportsCrs()) {
EpsgCrs sourceCrs = featureProvider.crs().getNativeCrs();
crsTransformer = crsTransformerFactory.getTransformer(sourceCrs, targetCrs);
}
List<Link> links = new DefaultLinksGenerator().generateLinks(requestContext.getUriCustomizer(), requestContext.getMediaType(), requestContext.getAlternateMediaTypes(), i18n, requestContext.getLanguage());
Map<String, ByteArrayOutputStream> byteArrayMap = new HashMap<>();
for (String collectionId : collectionIds) {
// TODO limitation of the current model: all layers have to come from the same feature provider and use the same CRS
Tile tile = singleLayerTileMap.get(collectionId);
if (!multiLayerTile.getTemporary()) {
// use cached tile
try {
Optional<InputStream> tileContent = tileCache.getTile(tile);
if (tileContent.isPresent()) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ByteStreams.copy(tileContent.get(), buffer);
byteArrayMap.put(collectionId, buffer);
continue;
}
} catch (SQLException | IOException e) {
// could not read the cache, generate the tile
}
}
String featureTypeId = apiData.getCollections().get(collectionId).getExtension(FeaturesCoreConfiguration.class).map(cfg -> cfg.getFeatureType().orElse(collectionId)).orElse(collectionId);
FeatureQuery query = queryMap.get(collectionId);
ImmutableFeatureTransformationContextTiles transformationContext;
try {
transformationContext = new ImmutableFeatureTransformationContextTiles.Builder().api(api).apiData(apiData).featureSchema(featureProvider.getData().getTypes().get(featureTypeId)).tile(tile).tileCache(tileCache).collectionId(collectionId).ogcApiRequest(requestContext).crsTransformer(crsTransformer).codelists(entityRegistry.getEntitiesForType(Codelist.class).stream().collect(Collectors.toMap(PersistentEntity::getId, c -> c))).defaultCrs(queryInput.getDefaultCrs()).links(links).isFeatureCollection(true).fields(query.getFields()).limit(query.getLimit()).offset(0).i18n(i18n).outputStream(new OutputStreamToByteConsumer()).build();
} catch (Exception e) {
throw new RuntimeException("Error building the tile transformation context.", e);
}
Optional<FeatureTokenEncoder<?>> encoder = outputFormat.getFeatureEncoder(transformationContext);
if (outputFormat.supportsFeatureQuery() && encoder.isPresent()) {
FeatureStream featureStream = featureProvider.queries().getFeatureStream(query);
ResultReduced<byte[]> result = generateTile(featureStream, encoder.get(), transformationContext, outputFormat);
if (result.isSuccess()) {
byte[] bytes = result.reduced();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(bytes.length);
buffer.write(bytes, 0, bytes.length);
byteArrayMap.put(collectionId, buffer);
}
} else {
throw new NotAcceptableException(MessageFormat.format("The requested media type {0} cannot be generated, because it does not support streaming.", requestContext.getMediaType().type()));
}
}
TileFormatWithQuerySupportExtension.MultiLayerTileContent result;
try {
result = outputFormat.combineSingleLayerTilesToMultiLayerTile(tileMatrixSet, singleLayerTileMap, byteArrayMap);
} catch (IOException e) {
throw new RuntimeException("Error accessing the tile cache.", e);
}
// try to write/update tile in cache, if all collections have been processed
if (result.isComplete) {
try {
tileCache.storeTile(multiLayerTile, result.byteArray);
} catch (Throwable e) {
String msg = "Failure to write the multi-layer file of tile {}/{}/{}/{} in dataset '{}', format '{}' to the cache";
LogContext.errorAsInfo(LOGGER, e, msg, tileMatrixSet.getId(), tileLevel, tileRow, tileCol, api.getId(), outputFormat.getExtension());
}
}
Date lastModified = null;
EntityTag etag = getEtag(result.byteArray);
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_%d_%d_%d.%s", tileMatrixSet.getId(), tileLevel, tileRow, tileCol, outputFormat.getMediaType().fileExtension())).entity(result.byteArray).build();
}
use of de.ii.ogcapi.foundation.domain.OgcApi in project ldproxy by interactive-instruments.
the class TileSetsFormatHtml method getTileSetsEntity.
@Override
public Object getTileSetsEntity(TileSets tiles, Optional<String> collectionId, OgcApi api, ApiRequestContext requestContext) {
String rootTitle = i18n.get("root", requestContext.getLanguage());
String collectionsTitle = i18n.get("collectionsTitle", requestContext.getLanguage());
String tilesTitle = i18n.get("tilesTitle", requestContext.getLanguage());
URICustomizer resourceUri = requestContext.getUriCustomizer().copy().clearParameters();
final List<NavigationDTO> breadCrumbs = collectionId.isPresent() ? new ImmutableList.Builder<NavigationDTO>().add(new NavigationDTO(rootTitle, resourceUri.copy().removeLastPathSegments(api.getData().getSubPath().size() + 3).toString())).add(new NavigationDTO(api.getData().getLabel(), resourceUri.copy().removeLastPathSegments(3).toString())).add(new NavigationDTO(collectionsTitle, resourceUri.copy().removeLastPathSegments(2).toString())).add(new NavigationDTO(api.getData().getCollections().get(collectionId.get()).getLabel(), resourceUri.copy().removeLastPathSegments(1).toString())).add(new NavigationDTO(tilesTitle)).build() : new ImmutableList.Builder<NavigationDTO>().add(new NavigationDTO(rootTitle, resourceUri.copy().removeLastPathSegments(api.getData().getSubPath().size() + 1).toString())).add(new NavigationDTO(api.getData().getLabel(), resourceUri.copy().removeLastPathSegments(1).toString())).add(new NavigationDTO(tilesTitle)).build();
Optional<HtmlConfiguration> htmlConfig = collectionId.isPresent() ? api.getData().getExtension(HtmlConfiguration.class, collectionId.get()) : api.getData().getExtension(HtmlConfiguration.class);
Map<String, TileMatrixSet> tileMatrixSets = tileMatrixSetRepository.getAll();
Optional<TilesConfiguration> tilesConfig = collectionId.isEmpty() ? api.getData().getExtension(TilesConfiguration.class) : api.getData().getExtension(TilesConfiguration.class, collectionId.get());
MapClient.Type mapClientType = tilesConfig.map(TilesConfiguration::getMapClientType).orElse(MapClient.Type.MAP_LIBRE);
String serviceUrl = new URICustomizer(servicesUri).ensureLastPathSegments(api.getData().getSubPath().toArray(String[]::new)).toString();
String styleUrl = htmlConfig.map(cfg -> cfg.getStyle(tilesConfig.map(TilesConfiguration::getStyle), collectionId, serviceUrl)).orElse(null);
boolean removeZoomLevelConstraints = tilesConfig.map(TilesConfiguration::getRemoveZoomLevelConstraints).orElse(false);
return new TileSetsView(api.getData(), tiles, collectionId, api.getSpatialExtent(collectionId), api.getTemporalExtent(collectionId), tileMatrixSets, breadCrumbs, requestContext.getStaticUrlPrefix(), mapClientType, styleUrl, removeZoomLevelConstraints, htmlConfig.orElseThrow(), isNoIndexEnabledForApi(api.getData()), requestContext.getUriCustomizer(), i18n, requestContext.getLanguage());
}
use of de.ii.ogcapi.foundation.domain.OgcApi 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();
}
use of de.ii.ogcapi.foundation.domain.OgcApi in project ldproxy by interactive-instruments.
the class QueriesHandlerStylesImpl method getStyleMetadataResponse.
private Response getStyleMetadataResponse(QueryInputStyle queryInput, ApiRequestContext requestContext) {
OgcApi api = requestContext.getApi();
OgcApiDataV2 apiData = api.getData();
Optional<String> collectionId = queryInput.getCollectionId();
StyleMetadata metadata = styleRepository.getStyleMetadata(apiData, collectionId, queryInput.getStyleId(), requestContext);
StyleMetadataFormatExtension format = styleRepository.getStyleMetadataFormatStream(apiData, collectionId).filter(f -> requestContext.getMediaType().matches(f.getMediaType().type())).findAny().orElseThrow(() -> new NotAcceptableException(MessageFormat.format("The requested media type ''{0}'' is not supported, the following media types are available: {1}", requestContext.getMediaType(), String.join(", ", styleRepository.getStyleMetadataFormatStream(apiData, collectionId).map(f -> f.getMediaType().type().toString()).collect(Collectors.toUnmodifiableList())))));
Date lastModified = styleRepository.getStyleLastModified(apiData, collectionId, queryInput.getStyleId());
EntityTag etag = !format.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(metadata, StyleMetadata.FUNNEL, format) : null;
Response.ResponseBuilder response = evaluatePreconditions(requestContext, lastModified, etag);
if (Objects.nonNull(response))
return response.build();
return prepareSuccessResponse(requestContext, queryInput.getIncludeLinkHeader() ? metadata.getLinks() : null, lastModified, etag, queryInput.getCacheControl().orElse(null), queryInput.getExpires().orElse(null), null, true, String.format("%s.metadata.%s", queryInput.getStyleId(), format.getMediaType().fileExtension())).entity(format.getStyleMetadataEntity(metadata, apiData, collectionId, requestContext)).build();
}
Aggregations