use of de.ii.ogcapi.foundation.domain.FeatureTypeConfigurationOgcApi in project ldproxy by interactive-instruments.
the class AbstractEndpointTileSetSingleCollection method getTileSet.
protected Response getTileSet(OgcApiDataV2 apiData, ApiRequestContext requestContext, String definitionPath, String collectionId, String tileMatrixSetId) {
checkPathParameter(extensionRegistry, apiData, definitionPath, "collectionId", collectionId);
checkPathParameter(extensionRegistry, apiData, definitionPath, "tileMatrixSetId", tileMatrixSetId);
FeatureTypeConfigurationOgcApi featureType = apiData.getCollections().get(collectionId);
TilesConfiguration tilesConfiguration = featureType.getExtension(TilesConfiguration.class).get();
TilesQueriesHandler.QueryInputTileSet queryInput = new Builder().from(getGenericQueryInput(apiData)).collectionId(collectionId).tileMatrixSetId(tileMatrixSetId).center(tilesConfiguration.getCenterDerived()).zoomLevels(tilesConfiguration.getZoomLevelsDerived().get(tileMatrixSetId)).path(definitionPath).build();
return queryHandler.handle(TilesQueriesHandler.Query.TILE_SET, queryInput, requestContext);
}
use of de.ii.ogcapi.foundation.domain.FeatureTypeConfigurationOgcApi 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.FeatureTypeConfigurationOgcApi 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.foundation.domain.FeatureTypeConfigurationOgcApi in project ldproxy by interactive-instruments.
the class StyleFormatHtml method getStyleEntity.
@Override
public Object getStyleEntity(StylesheetContent stylesheetContent, OgcApi api, Optional<String> collectionId, String styleId, ApiRequestContext requestContext) {
OgcApiDataV2 apiData = api.getData();
URICustomizer uriCustomizer = new URICustomizer(servicesUri).ensureLastPathSegments(apiData.getSubPath().toArray(String[]::new));
String serviceUrl = uriCustomizer.toString();
if (collectionId.isPresent())
uriCustomizer.ensureLastPathSegments("collections", collectionId.get());
uriCustomizer.ensureLastPathSegments("styles", styleId).addParameter("f", "mbs");
String styleUrl = uriCustomizer.toString();
boolean popup = apiData.getExtension(StylesConfiguration.class).map(StylesConfiguration::getWebmapWithPopup).orElse(true);
boolean layerControl = apiData.getExtension(StylesConfiguration.class).map(StylesConfiguration::getWebmapWithLayerControl).orElse(false);
boolean allLayers = apiData.getExtension(StylesConfiguration.class).map(StylesConfiguration::getLayerControlAllLayers).orElse(false);
ArrayListMultimap<String, String> layerMap = ArrayListMultimap.create();
if (layerControl) {
MbStyleStylesheet mbStyle = StyleFormatMbStyle.parse(stylesheetContent, serviceUrl, true, false).get();
if (allLayers) {
Map<String, FeatureTypeConfigurationOgcApi> collectionData = apiData.getCollections();
mbStyle.getLayers().stream().forEach(styleLayer -> {
if (styleLayer.getSourceLayer().isPresent()) {
layerMap.put(collectionData.containsKey(styleLayer.getSourceLayer().get()) ? collectionData.get(styleLayer.getSourceLayer().get()).getLabel() : styleLayer.getSourceLayer().get(), styleLayer.getId());
} else {
layerMap.put(styleLayer.getId(), styleLayer.getId());
}
});
} else {
Set<String> vectorSources = mbStyle.getSources().entrySet().stream().filter(source -> source.getValue() instanceof MbStyleVectorSource).map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet());
Map<String, FeatureTypeConfigurationOgcApi> collectionData = apiData.getCollections();
mbStyle.getLayers().stream().filter(styleLayer -> styleLayer.getSource().isPresent() && vectorSources.contains(styleLayer.getSource().get()) && styleLayer.getSourceLayer().isPresent() && collectionData.containsKey(styleLayer.getSourceLayer().get())).forEach(styleLayer -> layerMap.put(collectionData.containsKey(styleLayer.getSourceLayer().get()) ? collectionData.get(styleLayer.getSourceLayer().get()).getLabel() : styleLayer.getSourceLayer().get(), styleLayer.getId()));
}
}
return new StyleView(styleUrl, apiData, api.getSpatialExtent(), styleId, popup, layerControl, layerMap.asMap(), requestContext.getStaticUrlPrefix());
}
use of de.ii.ogcapi.foundation.domain.FeatureTypeConfigurationOgcApi in project ldproxy by interactive-instruments.
the class FeaturesFormatHtml method onStartup.
@Override
public ValidationResult onStartup(OgcApi api, MODE apiValidation) {
// no additional operational checks for now, only validation; we can stop, if no validation is requested
if (apiValidation == MODE.NONE)
return ValidationResult.of();
ImmutableValidationResult.Builder builder = ImmutableValidationResult.builder().mode(apiValidation);
Map<String, FeatureSchema> featureSchemas = providers.getFeatureSchemas(api.getData());
// get HTML configurations to process
Map<String, FeaturesHtmlConfiguration> htmlConfigurationMap = api.getData().getCollections().entrySet().stream().map(entry -> {
final FeatureTypeConfigurationOgcApi collectionData = entry.getValue();
final FeaturesHtmlConfiguration config = collectionData.getExtension(FeaturesHtmlConfiguration.class).orElse(null);
if (Objects.isNull(config))
return null;
return new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), config);
}).filter(Objects::nonNull).collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
Map<String, Collection<String>> keyMap = htmlConfigurationMap.entrySet().stream().map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), entry.getValue().getTransformations().keySet())).collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
for (Map.Entry<String, Collection<String>> stringCollectionEntry : featuresCoreValidator.getInvalidPropertyKeys(keyMap, featureSchemas).entrySet()) {
for (String property : stringCollectionEntry.getValue()) {
builder.addStrictErrors(MessageFormat.format("A transformation for property ''{0}'' in collection ''{1}'' is invalid, because the property was not found in the provider schema.", property, stringCollectionEntry.getKey()));
}
}
Set<String> codelists = entityRegistry.getEntitiesForType(Codelist.class).stream().map(Codelist::getId).collect(Collectors.toUnmodifiableSet());
for (Map.Entry<String, FeaturesHtmlConfiguration> entry : htmlConfigurationMap.entrySet()) {
String collectionId = entry.getKey();
for (Map.Entry<String, List<PropertyTransformation>> entry2 : entry.getValue().getTransformations().entrySet()) {
String property = entry2.getKey();
for (PropertyTransformation transformation : entry2.getValue()) {
builder = transformation.validate(builder, collectionId, property, codelists);
}
}
}
return builder.build();
}
Aggregations