use of de.ii.xtraplatform.features.domain.FeatureProvider2 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.xtraplatform.features.domain.FeatureProvider2 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.xtraplatform.features.domain.FeatureProvider2 in project ldproxy by interactive-instruments.
the class EndpointRoutesPost method computeRoute.
/**
* creates a new route
*
* @return a route according to the RouteExchangeModel
*/
@POST
@SuppressWarnings("UnstableApiUsage")
public Response computeRoute(@Auth Optional<User> optionalUser, @Context OgcApi api, @Context ApiRequestContext requestContext, @Context UriInfo uriInfo, @Context HttpServletRequest request, byte[] requestBody) {
OgcApiDataV2 apiData = api.getData();
checkAuthorization(apiData, optionalUser);
FeatureProvider2 featureProvider = providers.getFeatureProviderOrThrow(api.getData());
ensureFeatureProviderSupportsRouting(featureProvider);
String featureTypeId = api.getData().getExtension(RoutingConfiguration.class).map(RoutingConfiguration::getFeatureType).orElseThrow(() -> new IllegalStateException("No feature type has been configured for routing."));
EpsgCrs defaultCrs = apiData.getExtension(RoutingConfiguration.class).map(RoutingConfiguration::getDefaultEpsgCrs).orElse(OgcCrs.CRS84);
Map<String, Integer> coordinatePrecision = apiData.getExtension(RoutingConfiguration.class).map(RoutingConfiguration::getCoordinatePrecision).orElse(ImmutableMap.of());
String speedLimitUnit = apiData.getExtension(RoutingConfiguration.class).map(RoutingConfiguration::getSpeedLimitUnit).orElse("kmph");
Double elevationProfileSimplificationTolerance = apiData.getExtension(RoutingConfiguration.class).map(RoutingConfiguration::getElevationProfileSimplificationTolerance).orElse(null);
List<OgcApiQueryParameter> allowedParameters = getQueryParameters(extensionRegistry, api.getData(), "/routes", HttpMethods.POST);
FeatureQuery query = ogcApiFeaturesQuery.requestToBareFeatureQuery(api.getData(), featureTypeId, defaultCrs, coordinatePrecision, 1, Integer.MAX_VALUE, Integer.MAX_VALUE, toFlatMap(uriInfo.getQueryParameters()), allowedParameters);
RouteDefinition definition;
try {
// parse input
definition = mapper.readValue(requestBody, RouteDefinition.class);
} catch (IOException e) {
throw new IllegalArgumentException(String.format("The content of the route definition is invalid: %s", e.getMessage()), e);
}
String routeId = Hashing.murmur3_128().newHasher().putObject(definition, RouteDefinition.FUNNEL).putString(Optional.ofNullable(request.getHeader("crs")).orElse(defaultCrs.toUriString()), StandardCharsets.UTF_8).hash().toString();
if (apiData.getExtension(RoutingConfiguration.class).map(RoutingConfiguration::isManageRoutesEnabled).orElse(false)) {
if (routeRepository.routeExists(apiData, routeId)) {
// If the same route is already stored, just return the stored route
QueryHandlerRoutes.QueryInputRoute queryInput = new ImmutableQueryInputRoute.Builder().routeId(routeId).build();
return queryHandler.handle(QueryHandlerRoutes.Query.GET_ROUTE, queryInput, requestContext);
}
}
QueryHandlerRoutes.QueryInputComputeRoute queryInput = new ImmutableQueryInputComputeRoute.Builder().from(getGenericQueryInput(api.getData())).definition(definition).routeId(routeId).featureProvider(featureProvider).featureTypeId(featureTypeId).query(query).crs(Optional.ofNullable(request.getHeader("crs"))).defaultCrs(defaultCrs).speedLimitUnit(speedLimitUnit).elevationProfileSimplificationTolerance(Optional.ofNullable(elevationProfileSimplificationTolerance)).build();
return queryHandler.handle(QueryHandlerRoutes.Query.COMPUTE_ROUTE, queryInput, requestContext);
}
use of de.ii.xtraplatform.features.domain.FeatureProvider2 in project ldproxy by interactive-instruments.
the class CapabilityFeaturesCore method computeInterval.
private Optional<TemporalExtent> computeInterval(OgcApiDataV2 apiData, String collectionId) {
FeatureTypeConfigurationOgcApi collectionData = apiData.getCollections().get(collectionId);
Optional<FeatureProvider2> featureProvider = providers.getFeatureProvider(apiData, collectionData);
if (featureProvider.map(FeatureProvider2::supportsExtents).orElse(false)) {
List<String> temporalQueryables = collectionData.getExtension(FeaturesCoreConfiguration.class).flatMap(FeaturesCoreConfiguration::getQueryables).map(FeaturesCollectionQueryables::getTemporal).orElse(ImmutableList.of());
if (!temporalQueryables.isEmpty()) {
Optional<Interval> interval;
if (temporalQueryables.size() >= 2) {
interval = featureProvider.get().extents().getTemporalExtent(collectionId, temporalQueryables.get(0), temporalQueryables.get(1));
} else {
interval = featureProvider.get().extents().getTemporalExtent(collectionId, temporalQueryables.get(0));
}
return interval.map(TemporalExtent::of);
}
}
return Optional.empty();
}
use of de.ii.xtraplatform.features.domain.FeatureProvider2 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();
}
Aggregations