From 4602397bbd48cca97700efedd50ad7bc31227fcc Mon Sep 17 00:00:00 2001 From: Andreas Zahnen Date: Tue, 3 Sep 2024 09:20:34 +0200 Subject: [PATCH 1/8] upgrade xtraplatform --- xtraplatform.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtraplatform.gradle b/xtraplatform.gradle index a088317af..1e72bf232 100644 --- a/xtraplatform.gradle +++ b/xtraplatform.gradle @@ -3,5 +3,5 @@ dependencies { layers group: 'de.interactive_instruments', name: 'xtraplatform-core', version: '6.2.0-SNAPSHOT' layers group: 'de.interactive_instruments', name: 'xtraplatform-native', version: "2.4.0-${platform}" - layers group: 'de.interactive_instruments', name: 'xtraplatform-spatial', version: '7.2.0-SNAPSHOT' + layers group: 'de.interactive_instruments', name: 'xtraplatform-spatial', version: '7.2.0-root-concat-SNAPSHOT' } From 730c42fc960f3e50d090b5e1b0e3ab5e76649a2c Mon Sep 17 00:00:00 2001 From: Andreas Zahnen Date: Tue, 3 Sep 2024 09:21:38 +0200 Subject: [PATCH 2/8] support schemas for root concat --- .../core/domain/JsonSchemaDocument.java | 2 + .../core/domain/SchemaDeriverJsonSchema.java | 38 +++++++++++++++++++ .../core/domain/SchemaDeriverOpenApi.java | 33 ++++++++++++++++ .../core/domain/SchemaDeriverJsonSpec.groovy | 2 +- 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/JsonSchemaDocument.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/JsonSchemaDocument.java index cec2c1c03..5a9e10e8f 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/JsonSchemaDocument.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/JsonSchemaDocument.java @@ -80,6 +80,8 @@ public abstract static class Builder { public abstract Builder patternProperties(Map entries); + public abstract Builder putPatternProperties(String key, JsonSchema value); + public abstract Builder additionalProperties(JsonSchema value); public abstract JsonSchemaDocument build(); diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSchema.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSchema.java index cc3b3c123..4f5aba81c 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSchema.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSchema.java @@ -17,10 +17,13 @@ import de.ii.xtraplatform.features.domain.SchemaDeriver; import de.ii.xtraplatform.geometries.domain.SimpleFeatureGeometry; import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -87,6 +90,41 @@ protected JsonSchema buildRootSchema( return builder.build(); } + @Override + protected JsonSchema mergeRootSchemas(List rootSchemas) { + JsonSchemaDocument.Builder builder = + version == VERSION.V7 + ? ImmutableJsonSchemaDocumentV7.builder() + : ImmutableJsonSchemaDocument.builder().schema(version.url()); + + builder.id(schemaUri).title(label).description(description.orElse("")); + + Map definitions = new LinkedHashMap<>(); + Map properties = new LinkedHashMap<>(); + Map patternProperties = new LinkedHashMap<>(); + Set required = new LinkedHashSet<>(); + + rootSchemas.stream() + .filter(Objects::nonNull) + .filter(schema -> schema instanceof JsonSchemaDocument) + .map(schema -> (JsonSchemaDocument) schema) + .forEach( + schema -> { + definitions.putAll(schema.getDefinitions()); + properties.putAll(schema.getProperties()); + patternProperties.putAll(schema.getPatternProperties()); + schema.getAdditionalProperties().ifPresent(builder::additionalProperties); + required.addAll(schema.getRequired()); + }); + + builder.definitions(definitions); + builder.properties(properties); + builder.patternProperties(patternProperties); + builder.required(required); + + return builder.build(); + } + protected void adjustRootSchema( FeatureSchema schema, Map properties, diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApi.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApi.java index 467a6608c..ffbaa1a9b 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApi.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApi.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -73,6 +74,38 @@ protected abstract Schema buildRootSchema( Map> definitions, List requiredProperties); + @Override + protected Schema mergeRootSchemas(List> rootSchemas) { + Schema rootSchema = rootSchemas.get(0); + + Map properties = new LinkedHashMap<>(); + Map patternProperties = new LinkedHashMap<>(); + Set required = new LinkedHashSet<>(); + + rootSchemas.stream() + .filter(Objects::nonNull) + .filter(schema -> schema instanceof ObjectSchema) + .map(schema -> (ObjectSchema) schema) + .forEach( + schema -> { + if (Objects.nonNull(schema.getProperties())) { + properties.putAll(schema.getProperties()); + } + if (Objects.nonNull(schema.getPatternProperties())) { + patternProperties.putAll(schema.getPatternProperties()); + } + if (Objects.nonNull(schema.getRequired())) { + required.addAll(schema.getRequired()); + } + }); + + rootSchema.properties(properties); + rootSchema.patternProperties(patternProperties); + rootSchema.required(new ArrayList<>(required)); + + return rootSchema; + } + @Override protected Schema buildObjectSchema( FeatureSchema schema, Map> properties, List requiredProperties) { diff --git a/ogcapi-stable/ogcapi-features-core/src/test/groovy/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSpec.groovy b/ogcapi-stable/ogcapi-features-core/src/test/groovy/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSpec.groovy index aeb39c47a..5cba93fed 100644 --- a/ogcapi-stable/ogcapi-features-core/src/test/groovy/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSpec.groovy +++ b/ogcapi-stable/ogcapi-features-core/src/test/groovy/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSpec.groovy @@ -52,7 +52,7 @@ class SchemaDeriverJsonSpec extends Specification { FeatureRefResolver featureRefResolver = new FeatureRefResolver(Set.of("JSON")) List queryables = ["geometry", "datetime", "featureRef" /*, "objects.date" */] Predicate excludeConnectors = path -> path.matches(".+?\\[[^=\\]]+].+"); - OnlyQueryables queryablesSelector = new OnlyQueryables(queryables, List.of(), ".", excludeConnectors); + OnlyQueryables queryablesSelector = new OnlyQueryables(queryables, List.of(), ".", excludeConnectors, false); WithTransformationsApplied schemaFlattener = new WithTransformationsApplied(ImmutableMap.of("*", new ImmutablePropertyTransformation.Builder().flatten(".").build())) SchemaDeriverJsonSchema schemaDeriver = new SchemaDeriverCollectionProperties(version, Optional.empty(), "test-label", Optional.empty(), ImmutableMap.of(), queryables) From cc95a06f57c3c5e0185325997b3aaa2938fa09b5 Mon Sep 17 00:00:00 2001 From: Andreas Zahnen Date: Tue, 3 Sep 2024 09:21:56 +0200 Subject: [PATCH 3/8] support queryables for root concat --- .../app/QueryParametersQueryables.java | 2 +- .../domain/QueryablesConfiguration.java | 28 +++++++++++++--- .../features/core/app/QueryParameterBbox.java | 2 +- .../core/app/QueryParameterDatetime.java | 2 +- .../core/domain/FeaturesCoreProviders.java | 32 +++++++++++++++++++ 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/ogcapi-stable/ogcapi-collections-queryables/src/main/java/de/ii/ogcapi/collections/queryables/app/QueryParametersQueryables.java b/ogcapi-stable/ogcapi-collections-queryables/src/main/java/de/ii/ogcapi/collections/queryables/app/QueryParametersQueryables.java index 18efc66ea..6005588dd 100644 --- a/ogcapi-stable/ogcapi-collections-queryables/src/main/java/de/ii/ogcapi/collections/queryables/app/QueryParametersQueryables.java +++ b/ogcapi-stable/ogcapi-collections-queryables/src/main/java/de/ii/ogcapi/collections/queryables/app/QueryParametersQueryables.java @@ -104,7 +104,7 @@ public List getRuntimeParameters( } return configuration - .map(c -> c.getQueryables(apiData, collectionData, featureSchema, providers)) + .map(c -> c.getQueryables(apiData, collectionData, featureSchema, providers, true)) .orElse(ImmutableMap.of()) .entrySet() .stream() diff --git a/ogcapi-stable/ogcapi-collections-queryables/src/main/java/de/ii/ogcapi/collections/queryables/domain/QueryablesConfiguration.java b/ogcapi-stable/ogcapi-collections-queryables/src/main/java/de/ii/ogcapi/collections/queryables/domain/QueryablesConfiguration.java index 13ed6b65c..18ac17cea 100644 --- a/ogcapi-stable/ogcapi-collections-queryables/src/main/java/de/ii/ogcapi/collections/queryables/domain/QueryablesConfiguration.java +++ b/ogcapi-stable/ogcapi-collections-queryables/src/main/java/de/ii/ogcapi/collections/queryables/domain/QueryablesConfiguration.java @@ -152,7 +152,7 @@ default Map getQueryables( FeaturesCoreProviders providers) { return providers .getFeatureSchema(apiData, collectionData) - .map(schema -> getQueryables(apiData, collectionData, schema, providers)) + .map(schema -> getQueryables(apiData, collectionData, schema, providers, true)) .orElse(ImmutableMap.of()); } @@ -160,8 +160,9 @@ default Map getQueryables( OgcApiDataV2 apiData, FeatureTypeConfigurationOgcApi collectionData, FeatureSchema schema, - FeaturesCoreProviders providers) { - return getQueryablesSchema(apiData, collectionData, schema, providers) + FeaturesCoreProviders providers, + boolean cleanupKeys) { + return getQueryablesSchema(apiData, collectionData, schema, providers, cleanupKeys) .getAllNestedProperties() .stream() .filter(FeatureSchema::queryable) @@ -173,16 +174,33 @@ default Map getQueryables( ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue, (first, second) -> second)); } - default FeatureSchema getQueryablesSchema( + default Map getQueryables( OgcApiDataV2 apiData, FeatureTypeConfigurationOgcApi collectionData, FeatureSchema schema, FeaturesCoreProviders providers) { + return getQueryables(apiData, collectionData, schema, providers, false); + } + + default FeatureSchema getQueryablesSchema( + OgcApiDataV2 apiData, + FeatureTypeConfigurationOgcApi collectionData, + FeatureSchema schema, + FeaturesCoreProviders providers, + boolean cleanupKeys) { FeatureQueries featureQueries = providers.getFeatureProviderOrThrow(apiData, collectionData, FeatureProvider::queries); return featureQueries.getQueryablesSchema( - schema, getIncluded(), getExcluded(), getPathSeparator().toString()); + schema, getIncluded(), getExcluded(), getPathSeparator().toString(), cleanupKeys); + } + + default FeatureSchema getQueryablesSchema( + OgcApiDataV2 apiData, + FeatureTypeConfigurationOgcApi collectionData, + FeatureSchema schema, + FeaturesCoreProviders providers) { + return getQueryablesSchema(apiData, collectionData, schema, providers, false); } abstract class Builder extends ExtensionConfiguration.Builder {} diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/QueryParameterBbox.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/QueryParameterBbox.java index 968718b3f..839f2649a 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/QueryParameterBbox.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/QueryParameterBbox.java @@ -189,7 +189,7 @@ public Cql2Expression parse( Optional primaryGeometry = providers - .getFeatureSchema(api.getData(), collectionData) + .getQueryablesSchema(api.getData(), collectionData) .orElseThrow( () -> new IllegalStateException( diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/QueryParameterDatetime.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/QueryParameterDatetime.java index 592c544e5..1be94a2de 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/QueryParameterDatetime.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/QueryParameterDatetime.java @@ -111,7 +111,7 @@ public Cql2Expression parse( FeatureSchema featureSchema = providers - .getFeatureSchema(api.getData(), collectionData) + .getQueryablesSchema(api.getData(), collectionData) .orElseThrow( () -> new IllegalStateException( diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeaturesCoreProviders.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeaturesCoreProviders.java index 21aeb7014..fd8f7e232 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeaturesCoreProviders.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeaturesCoreProviders.java @@ -14,6 +14,7 @@ import de.ii.xtraplatform.features.domain.FeatureProvider; import de.ii.xtraplatform.features.domain.FeatureSchema; import java.util.AbstractMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -40,6 +41,18 @@ public interface FeaturesCoreProviders { Optional getFeatureProvider( OgcApiDataV2 apiData, Function> capability); + default Optional getFeatureProvider( + FeatureProvider featureProvider, + Function> capability) { + OptionalVolatileCapability volatileCapability = capability.apply(featureProvider); + + if (volatileCapability.isAvailable()) { + return Optional.of(volatileCapability.get()); + } + + return Optional.empty(); + } + FeatureProvider getFeatureProviderOrThrow(OgcApiDataV2 apiData); boolean hasFeatureProvider(OgcApiDataV2 apiData, FeatureTypeConfigurationOgcApi featureType); @@ -87,6 +100,25 @@ default Map getFeatureSchemas(OgcApiDataV2 apiData) { .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); } + default Optional getQueryablesSchema( + OgcApiDataV2 apiData, FeatureTypeConfigurationOgcApi featureType) { + String featureTypeId = + featureType + .getExtension(FeaturesCoreConfiguration.class) + .flatMap(FeaturesCoreConfiguration::getFeatureType) + .orElse(featureType.getId()); + Optional featureProvider = getFeatureProvider(apiData, featureType); + return featureProvider + .flatMap(provider -> provider.info().getSchema(featureTypeId)) + .flatMap( + schema -> + getFeatureProvider(featureProvider.get(), FeatureProvider::queries) + .map( + queries -> + queries.getQueryablesSchema( + schema, List.of("*"), List.of(), ".", true))); + } + T getFeatureProviderOrThrow( OgcApiDataV2 apiData, FeatureTypeConfigurationOgcApi featureType, From 535a532286228e87c44a64e8a2ee31c951c6f94a Mon Sep 17 00:00:00 2001 From: Andreas Zahnen Date: Tue, 3 Sep 2024 09:22:04 +0200 Subject: [PATCH 4/8] cleanup --- .../features/html/app/FeaturesFormatHtml.java | 51 ------------------- .../features/html/app/FilterEditor.java | 4 -- .../domain/FeaturesHtmlConfiguration.java | 1 + .../features/html/domain/FeaturesView.java | 20 ++------ 4 files changed, 5 insertions(+), 71 deletions(-) diff --git a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FeaturesFormatHtml.java b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FeaturesFormatHtml.java index e7de92fc5..37ee4feb0 100644 --- a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FeaturesFormatHtml.java +++ b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FeaturesFormatHtml.java @@ -35,8 +35,6 @@ import de.ii.ogcapi.foundation.domain.Link; import de.ii.ogcapi.foundation.domain.OgcApi; import de.ii.ogcapi.foundation.domain.OgcApiDataV2; -import de.ii.ogcapi.foundation.domain.OgcApiQueryParameter; -import de.ii.ogcapi.foundation.domain.RuntimeQueryParametersExtension; import de.ii.ogcapi.foundation.domain.URICustomizer; import de.ii.ogcapi.html.domain.HtmlConfiguration; import de.ii.ogcapi.html.domain.MapClient; @@ -67,13 +65,11 @@ import java.nio.charset.StandardCharsets; import java.text.MessageFormat; import java.util.AbstractMap; -import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -319,49 +315,16 @@ public Optional> getFeatureEncoder( "The HTML output has a maximum page size (parameter 'limit') of %d. Found: %d", htmlMaxLimit, transformationContext.getLimit())); - Optional featuresCoreConfiguration = - collectionData.getExtension(FeaturesCoreConfiguration.class); - - List queryables = - extensionRegistry.getExtensionsForType(RuntimeQueryParametersExtension.class).stream() - .map( - extension -> - extension.getRuntimeParameters( - apiData, - Optional.of(collectionData.getId()), - "/collections/{collectionId}/items")) - .flatMap(Collection::stream) - .map(OgcApiQueryParameter::getName) - .collect(Collectors.toUnmodifiableList()); - - Map filterableFields = - transformationContext - .getFeatureSchema() - .map(schema -> schema.accept(SCHEMA_FLATTENER)) - .map( - schema -> - schema.getProperties().stream() - .filter(property -> queryables.contains(property.getName())) - .map( - property -> - new SimpleImmutableEntry<>( - property.getName(), - property.getLabel().orElse(property.getName()))) - .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue))) - .orElse(ImmutableMap.of()); - featureTypeDataset = createFeatureCollectionView( api, apiData.getCollections().get(collectionName), uriCustomizer.copy(), - filterableFields, staticUrlPrefix, language, isNoIndexEnabledForApi(apiData), getMapPosition(apiData, collectionName), hideMap, - getGeometryProperties(apiData, collectionName), getPropertyTooltips(apiData, collectionName, true), apiData.getLabel(), transformationContext.getLinks(), @@ -382,7 +345,6 @@ public Optional> getFeatureEncoder( apiData.getSubPath(), getMapPosition(apiData, collectionName), hideMap, - getGeometryProperties(apiData, collectionName), getPropertyTooltips(apiData, collectionName, true), user); } @@ -415,13 +377,11 @@ private ModifiableFeatureCollectionView createFeatureCollectionView( OgcApi api, FeatureTypeConfigurationOgcApi featureType, URICustomizer uriCustomizer, - Map filterableFields, String staticUrlPrefix, Optional language, boolean noIndex, POSITION mapPosition, boolean hideMap, - List geometryProperties, boolean propertyTooltips, String apiLabel, List links, @@ -499,8 +459,6 @@ private ModifiableFeatureCollectionView createFeatureCollectionView( .setStyle(style) .setRemoveZoomLevelConstraints(removeZoomLevelConstraints) .setHideMap(hideMap) - .setQueryables(filterableFields) - .setGeometryProperties(geometryProperties) .setPropertyTooltips(propertyTooltips) .setUriCustomizer(uriCustomizer) .setBreadCrumbs( @@ -547,7 +505,6 @@ private ModifiableFeatureCollectionView createFeatureDetailsView( List subPathToLandingPage, POSITION mapPosition, boolean hideMap, - List geometryProperties, boolean propertyTooltips, Optional user) { OgcApiDataV2 apiData = api.getData(); @@ -634,7 +591,6 @@ private ModifiableFeatureCollectionView createFeatureDetailsView( .setStyle(style) .setRemoveZoomLevelConstraints(removeZoomLevelConstraints) .setHideMap(hideMap) - .setGeometryProperties(geometryProperties) .setPropertyTooltips(propertyTooltips) .setRawTemporalExtent(api.getTemporalExtent(featureType.getId())) .setRawFormats(formats) @@ -798,11 +754,4 @@ private POSITION getMapPosition(OgcApiDataV2 apiData, String collectionId) { .map(FeaturesHtmlConfiguration::getMapPosition) .orElse(POSITION.AUTO); } - - private List getGeometryProperties(OgcApiDataV2 apiData, String collectionId) { - return apiData - .getExtension(FeaturesHtmlConfiguration.class, collectionId) - .map(FeaturesHtmlConfiguration::getGeometryProperties) - .orElse(ImmutableList.of()); - } } diff --git a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FilterEditor.java b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FilterEditor.java index 43bd58171..1239e115d 100644 --- a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FilterEditor.java +++ b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FilterEditor.java @@ -7,17 +7,13 @@ */ package de.ii.ogcapi.features.html.app; -import java.util.Map.Entry; import java.util.Optional; -import java.util.Set; import org.immutables.value.Value; @Value.Immutable @Value.Style(builder = "new", deepImmutablesDetection = true) public interface FilterEditor { - Set> getFields(); - Optional getBackgroundUrl(); Optional getAttribution(); diff --git a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/domain/FeaturesHtmlConfiguration.java b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/domain/FeaturesHtmlConfiguration.java index cdb057a2d..809702f2f 100644 --- a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/domain/FeaturesHtmlConfiguration.java +++ b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/domain/FeaturesHtmlConfiguration.java @@ -230,6 +230,7 @@ enum POSITION { * verwendet. * @default [] */ + @Deprecated(forRemoval = true, since = "4.2") @Nullable List getGeometryProperties(); diff --git a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/domain/FeaturesView.java b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/domain/FeaturesView.java index 2ed54decc..099940499 100644 --- a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/domain/FeaturesView.java +++ b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/domain/FeaturesView.java @@ -79,10 +79,6 @@ protected FeaturesView(String templateName) { public abstract boolean removeZoomLevelConstraints(); - public abstract Map queryables(); - - public abstract List geometryProperties(); - public abstract boolean propertyTooltips(); @Nullable @@ -212,18 +208,10 @@ public MapClient mapClient() { @Value.Default @Nullable public FilterEditor filterEditor() { - if (Objects.nonNull(queryables())) { - return new Builder() - .backgroundUrl(Optional.ofNullable(htmlConfig().getBasemapUrl())) - .attribution(Optional.ofNullable(htmlConfig().getBasemapAttribution())) - .fields( - queryables().entrySet().stream() - .sorted(Map.Entry.comparingByValue()) - .collect(Collectors.toList())) - .build(); - } else { - return null; - } + return new Builder() + .backgroundUrl(Optional.ofNullable(htmlConfig().getBasemapUrl())) + .attribution(Optional.ofNullable(htmlConfig().getBasemapAttribution())) + .build(); } @Value.Derived From 1ed2b3490ffabc63540f16f6d151a55ad929782e Mon Sep 17 00:00:00 2001 From: Andreas Zahnen Date: Tue, 1 Oct 2024 16:40:13 +0200 Subject: [PATCH 5/8] fix schemas and queryables --- .../core/domain/JsonSchemaDocument.java | 7 ++++ .../SchemaDeriverCollectionProperties.java | 38 +++++++++++++++++++ .../core/domain/SchemaDeriverJsonSchema.java | 27 ++++++------- .../core/domain/SchemaDeriverOpenApi.java | 20 +++------- ...emaDeriverOpenApiCollectionProperties.java | 35 +++++++++++++++++ 5 files changed, 97 insertions(+), 30 deletions(-) diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/JsonSchemaDocument.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/JsonSchemaDocument.java index 5a9e10e8f..317228249 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/JsonSchemaDocument.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/JsonSchemaDocument.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.hash.Funnel; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Map; import java.util.Optional; import org.immutables.value.Value; @@ -26,6 +27,9 @@ public String getSchema() { @JsonProperty("$id") public abstract Optional getId(); + @JsonProperty("anyOf") + public abstract List getAnyOf(); + @JsonProperty("$defs") public abstract Map getDefinitions(); @@ -84,6 +88,8 @@ public abstract static class Builder { public abstract Builder additionalProperties(JsonSchema value); + public abstract Builder anyOf(Iterable elements); + public abstract JsonSchemaDocument build(); } @@ -105,5 +111,6 @@ public abstract static class Builder { from.getDefinitions().entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEachOrdered(entry -> JsonSchema.FUNNEL.funnel(entry.getValue(), into)); + from.getAnyOf().stream().forEachOrdered(val -> JsonSchema.FUNNEL.funnel(val, into)); }; } diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverCollectionProperties.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverCollectionProperties.java index 343a24d17..670f9511a 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverCollectionProperties.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverCollectionProperties.java @@ -10,10 +10,13 @@ import de.ii.ogcapi.features.core.domain.JsonSchemaDocument.VERSION; import de.ii.xtraplatform.codelists.domain.Codelist; import de.ii.xtraplatform.features.domain.FeatureSchema; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; public class SchemaDeriverCollectionProperties extends SchemaDeriverJsonSchema { @@ -49,6 +52,41 @@ protected void adjustRootSchema( builder.additionalProperties(ImmutableJsonSchemaFalse.builder().build()); } + @Override + protected JsonSchema mergeRootSchemas(List rootSchemas) { + JsonSchemaDocument.Builder builder = + version == VERSION.V7 + ? ImmutableJsonSchemaDocumentV7.builder() + : ImmutableJsonSchemaDocument.builder().schema(version.url()); + + builder.id(schemaUri).title(label).description(description.orElse("")); + + Map definitions = new LinkedHashMap<>(); + Map properties = new LinkedHashMap<>(); + Map patternProperties = new LinkedHashMap<>(); + Set required = new LinkedHashSet<>(); + + rootSchemas.stream() + .filter(Objects::nonNull) + .filter(schema -> schema instanceof JsonSchemaDocument) + .map(schema -> (JsonSchemaDocument) schema) + .forEach( + schema -> { + definitions.putAll(schema.getDefinitions()); + properties.putAll(schema.getProperties()); + patternProperties.putAll(schema.getPatternProperties()); + schema.getAdditionalProperties().ifPresent(builder::additionalProperties); + required.addAll(schema.getRequired()); + }); + + builder.definitions(definitions); + builder.properties(properties); + builder.patternProperties(patternProperties); + builder.required(required); + + return builder.build(); + } + @Override protected JsonSchema deriveValueSchema(FeatureSchema schema) { JsonSchema schema2 = super.deriveValueSchema(schema); diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSchema.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSchema.java index 4f5aba81c..7634574e7 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSchema.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverJsonSchema.java @@ -16,14 +16,13 @@ import de.ii.xtraplatform.features.domain.SchemaConstraints; import de.ii.xtraplatform.features.domain.SchemaDeriver; import de.ii.xtraplatform.geometries.domain.SimpleFeatureGeometry; +import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -31,9 +30,9 @@ public abstract class SchemaDeriverJsonSchema extends SchemaDeriver { protected final VERSION version; - private final Optional schemaUri; - private final String label; - private final Optional description; + protected final Optional schemaUri; + protected final String label; + protected final Optional description; private final boolean useCodelistKeys; public SchemaDeriverJsonSchema( @@ -100,9 +99,7 @@ protected JsonSchema mergeRootSchemas(List rootSchemas) { builder.id(schemaUri).title(label).description(description.orElse("")); Map definitions = new LinkedHashMap<>(); - Map properties = new LinkedHashMap<>(); - Map patternProperties = new LinkedHashMap<>(); - Set required = new LinkedHashSet<>(); + List schemas = new ArrayList<>(); rootSchemas.stream() .filter(Objects::nonNull) @@ -111,16 +108,16 @@ protected JsonSchema mergeRootSchemas(List rootSchemas) { .forEach( schema -> { definitions.putAll(schema.getDefinitions()); - properties.putAll(schema.getProperties()); - patternProperties.putAll(schema.getPatternProperties()); - schema.getAdditionalProperties().ifPresent(builder::additionalProperties); - required.addAll(schema.getRequired()); + schemas.add( + new ImmutableJsonSchemaObject.Builder() + .from(schema) + .title(Optional.empty()) + .description(Optional.empty()) + .build()); }); builder.definitions(definitions); - builder.properties(properties); - builder.patternProperties(patternProperties); - builder.required(required); + builder.anyOf(schemas); return builder.build(); } diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApi.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApi.java index ffbaa1a9b..a996ebca6 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApi.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApi.java @@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -78,9 +77,7 @@ protected abstract Schema buildRootSchema( protected Schema mergeRootSchemas(List> rootSchemas) { Schema rootSchema = rootSchemas.get(0); - Map properties = new LinkedHashMap<>(); - Map patternProperties = new LinkedHashMap<>(); - Set required = new LinkedHashSet<>(); + List schemas = new ArrayList<>(); rootSchemas.stream() .filter(Objects::nonNull) @@ -88,20 +85,13 @@ protected Schema mergeRootSchemas(List> rootSchemas) { .map(schema -> (ObjectSchema) schema) .forEach( schema -> { - if (Objects.nonNull(schema.getProperties())) { - properties.putAll(schema.getProperties()); - } - if (Objects.nonNull(schema.getPatternProperties())) { - patternProperties.putAll(schema.getPatternProperties()); - } - if (Objects.nonNull(schema.getRequired())) { - required.addAll(schema.getRequired()); + if (Objects.nonNull(schema.getProperties()) + && schema.getProperties().containsKey("properties")) { + schemas.add(schema.getProperties().get("properties")); } }); - rootSchema.properties(properties); - rootSchema.patternProperties(patternProperties); - rootSchema.required(new ArrayList<>(required)); + rootSchema.getProperties().put("properties", new ObjectSchema().anyOf(schemas)); return rootSchema; } diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApiCollectionProperties.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApiCollectionProperties.java index b86505b2a..064780207 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApiCollectionProperties.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/SchemaDeriverOpenApiCollectionProperties.java @@ -12,11 +12,14 @@ import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.Schema; +import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; public class SchemaDeriverOpenApiCollectionProperties extends SchemaDeriverOpenApi { @@ -54,6 +57,38 @@ protected Schema buildRootSchema( return rootSchema; } + @Override + protected Schema mergeRootSchemas(List> rootSchemas) { + Schema rootSchema = rootSchemas.get(0); + + Map properties = new LinkedHashMap<>(); + Map patternProperties = new LinkedHashMap<>(); + Set required = new LinkedHashSet<>(); + + rootSchemas.stream() + .filter(Objects::nonNull) + .filter(schema -> schema instanceof ObjectSchema) + .map(schema -> (ObjectSchema) schema) + .forEach( + schema -> { + if (Objects.nonNull(schema.getProperties())) { + properties.putAll(schema.getProperties()); + } + if (Objects.nonNull(schema.getPatternProperties())) { + patternProperties.putAll(schema.getPatternProperties()); + } + if (Objects.nonNull(schema.getRequired())) { + required.addAll(schema.getRequired()); + } + }); + + rootSchema.properties(properties); + rootSchema.patternProperties(patternProperties); + rootSchema.required(new ArrayList<>(required)); + + return rootSchema; + } + @Override protected Schema deriveValueSchema(FeatureSchema schema) { Schema schema2 = super.deriveValueSchema(schema); From e6501ec71a5c9a09dd41cdcab77bcbb4ac98298a Mon Sep 17 00:00:00 2001 From: Andreas Zahnen Date: Wed, 2 Oct 2024 09:15:06 +0200 Subject: [PATCH 6/8] disable feature formats that do not support root concat out of the box --- .../features/csv/app/FeaturesFormatCsv.java | 4 +- .../app/FeaturesFormatFlatgeobuf.java | 4 +- .../gltf/app/FeaturesFormatGltfBinary.java | 4 +- .../jsonfg/app/FeaturesFormatJsonFg.java | 6 +- .../FeaturesFormatJsonFgCompatibility.java | 6 +- .../domain/FeaturesFormatJsonFgBase.java | 11 +++- .../app/FeaturesFormatCityJsonBase.java | 9 ++- .../app/PolicyAttributeFeaturesFormat.java | 11 +++- .../core/domain/FeatureFormatExtension.java | 55 ++++++++++++++++++- .../core/domain/FeaturesCoreProviders.java | 13 +++++ .../core/OgcApiCoreSpecCollections.groovy | 2 +- .../geojson/app/FeaturesFormatGeoJson.java | 9 ++- .../features/gml/app/FeaturesFormatGml.java | 4 +- .../features/html/app/FeaturesFormatHtml.java | 14 ++--- 14 files changed, 117 insertions(+), 35 deletions(-) diff --git a/ogcapi-custom/ogcapi-features-csv/src/main/java/de/ii/ogcapi/features/csv/app/FeaturesFormatCsv.java b/ogcapi-custom/ogcapi-features-csv/src/main/java/de/ii/ogcapi/features/csv/app/FeaturesFormatCsv.java index eff962f65..557043b9f 100644 --- a/ogcapi-custom/ogcapi-features-csv/src/main/java/de/ii/ogcapi/features/csv/app/FeaturesFormatCsv.java +++ b/ogcapi-custom/ogcapi-features-csv/src/main/java/de/ii/ogcapi/features/csv/app/FeaturesFormatCsv.java @@ -61,13 +61,11 @@ public class FeaturesFormatCsv extends FeatureFormatExtension implements Conform .parameter("json") .build(); - private final FeaturesCoreProviders providers; private final FeatureSchemaCache schemaCache; @Inject public FeaturesFormatCsv(FeaturesCoreProviders providers, ExtensionRegistry extensionRegistry) { - super(extensionRegistry); - this.providers = providers; + super(extensionRegistry, providers); this.schemaCache = new SchemaCacheSfFlat(); } diff --git a/ogcapi-custom/ogcapi-features-flatgeobuf/src/main/java/de/ii/ogcapi/features/flatgeobuf/app/FeaturesFormatFlatgeobuf.java b/ogcapi-custom/ogcapi-features-flatgeobuf/src/main/java/de/ii/ogcapi/features/flatgeobuf/app/FeaturesFormatFlatgeobuf.java index 6bf953943..f2ca1af69 100644 --- a/ogcapi-custom/ogcapi-features-flatgeobuf/src/main/java/de/ii/ogcapi/features/flatgeobuf/app/FeaturesFormatFlatgeobuf.java +++ b/ogcapi-custom/ogcapi-features-flatgeobuf/src/main/java/de/ii/ogcapi/features/flatgeobuf/app/FeaturesFormatFlatgeobuf.java @@ -64,15 +64,13 @@ public class FeaturesFormatFlatgeobuf extends FeatureFormatExtension implements .parameter("fgb") .build(); - private final FeaturesCoreProviders providers; private final CrsInfo crsInfo; private final FeatureSchemaCache schemaCache; @Inject public FeaturesFormatFlatgeobuf( FeaturesCoreProviders providers, CrsInfo crsInfo, ExtensionRegistry extensionRegistry) { - super(extensionRegistry); - this.providers = providers; + super(extensionRegistry, providers); this.crsInfo = crsInfo; this.schemaCache = new SchemaCacheSfFlat(); } diff --git a/ogcapi-draft/ogcapi-features-gltf/src/main/java/de/ii/ogcapi/features/gltf/app/FeaturesFormatGltfBinary.java b/ogcapi-draft/ogcapi-features-gltf/src/main/java/de/ii/ogcapi/features/gltf/app/FeaturesFormatGltfBinary.java index ba7fb0c8d..347d9b5bd 100644 --- a/ogcapi-draft/ogcapi-features-gltf/src/main/java/de/ii/ogcapi/features/gltf/app/FeaturesFormatGltfBinary.java +++ b/ogcapi-draft/ogcapi-features-gltf/src/main/java/de/ii/ogcapi/features/gltf/app/FeaturesFormatGltfBinary.java @@ -84,7 +84,6 @@ public class FeaturesFormatGltfBinary extends FeatureFormatExtension { public static final Schema SCHEMA = new BinarySchema(); public static final String SCHEMA_REF = "#/components/schemas/glTF"; - private final FeaturesCoreProviders providers; private final Values codelistStore; private final FeaturesCoreValidation featuresCoreValidator; private final CrsTransformerFactory crsTransformerFactory; @@ -101,8 +100,7 @@ public FeaturesFormatGltfBinary( ServicesContext servicesContext, Metadata3dSchemaCache schemaCache, ExtensionRegistry extensionRegistry) { - super(extensionRegistry); - this.providers = providers; + super(extensionRegistry, providers); this.codelistStore = valueStore.forType(Codelist.class); this.featuresCoreValidator = featuresCoreValidator; this.crsTransformerFactory = crsTransformerFactory; diff --git a/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/FeaturesFormatJsonFg.java b/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/FeaturesFormatJsonFg.java index 2188d7fa8..02540246a 100644 --- a/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/FeaturesFormatJsonFg.java +++ b/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/FeaturesFormatJsonFg.java @@ -9,6 +9,7 @@ import com.github.azahnen.dagger.annotations.AutoBind; import de.ii.ogcapi.features.core.domain.FeatureTransformationContext; +import de.ii.ogcapi.features.core.domain.FeaturesCoreProviders; import de.ii.ogcapi.features.core.domain.SchemaGeneratorCollectionOpenApi; import de.ii.ogcapi.features.core.domain.SchemaGeneratorOpenApi; import de.ii.ogcapi.features.geojson.domain.GeoJsonWriter; @@ -49,8 +50,9 @@ public FeaturesFormatJsonFg( SchemaGeneratorOpenApi schemaGeneratorFeature, SchemaGeneratorCollectionOpenApi schemaGeneratorFeatureCollection, GeoJsonWriterRegistry geoJsonWriterRegistry, - ExtensionRegistry extensionRegistry) { - super(extensionRegistry); + ExtensionRegistry extensionRegistry, + FeaturesCoreProviders providers) { + super(extensionRegistry, providers); this.schemaGeneratorFeature = schemaGeneratorFeature; this.schemaGeneratorFeatureCollection = schemaGeneratorFeatureCollection; this.geoJsonWriterRegistry = geoJsonWriterRegistry; diff --git a/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/FeaturesFormatJsonFgCompatibility.java b/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/FeaturesFormatJsonFgCompatibility.java index f70d82e41..bb19d8f9a 100644 --- a/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/FeaturesFormatJsonFgCompatibility.java +++ b/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/FeaturesFormatJsonFgCompatibility.java @@ -10,6 +10,7 @@ import com.github.azahnen.dagger.annotations.AutoBind; import com.google.common.collect.ImmutableMap; import de.ii.ogcapi.features.core.domain.FeatureTransformationContext; +import de.ii.ogcapi.features.core.domain.FeaturesCoreProviders; import de.ii.ogcapi.features.core.domain.SchemaGeneratorCollectionOpenApi; import de.ii.ogcapi.features.core.domain.SchemaGeneratorOpenApi; import de.ii.ogcapi.features.geojson.domain.GeoJsonWriter; @@ -49,8 +50,9 @@ public FeaturesFormatJsonFgCompatibility( SchemaGeneratorOpenApi schemaGeneratorFeature, SchemaGeneratorCollectionOpenApi schemaGeneratorFeatureCollection, GeoJsonWriterRegistry geoJsonWriterRegistry, - ExtensionRegistry extensionRegistry) { - super(extensionRegistry); + ExtensionRegistry extensionRegistry, + FeaturesCoreProviders providers) { + super(extensionRegistry, providers); this.schemaGeneratorFeature = schemaGeneratorFeature; this.schemaGeneratorFeatureCollection = schemaGeneratorFeatureCollection; this.geoJsonWriterRegistry = geoJsonWriterRegistry; diff --git a/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/domain/FeaturesFormatJsonFgBase.java b/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/domain/FeaturesFormatJsonFgBase.java index bf66c2adf..8c996692c 100644 --- a/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/domain/FeaturesFormatJsonFgBase.java +++ b/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/domain/FeaturesFormatJsonFgBase.java @@ -13,6 +13,7 @@ import com.google.common.collect.ImmutableSortedSet; import de.ii.ogcapi.features.core.domain.FeatureFormatExtension; import de.ii.ogcapi.features.core.domain.FeatureTransformationContext; +import de.ii.ogcapi.features.core.domain.FeaturesCoreProviders; import de.ii.ogcapi.features.geojson.domain.FeatureEncoderGeoJson; import de.ii.ogcapi.features.geojson.domain.GeoJsonConfiguration; import de.ii.ogcapi.features.geojson.domain.GeoJsonWriter; @@ -43,8 +44,9 @@ @AutoMultiBind public abstract class FeaturesFormatJsonFgBase extends FeatureFormatExtension { - protected FeaturesFormatJsonFgBase(ExtensionRegistry extensionRegistry) { - super(extensionRegistry); + protected FeaturesFormatJsonFgBase( + ExtensionRegistry extensionRegistry, FeaturesCoreProviders providers) { + super(extensionRegistry, providers); } @Override @@ -185,6 +187,11 @@ public boolean isComplex() { return true; } + @Override + public boolean supportsRootConcat() { + return true; + } + private Optional getMetadata(Object content, String key) { if (content instanceof byte[]) { JsonNode jsonNode; diff --git a/ogcapi-stable/ogcapi-features-cityjson/src/main/java/de/ii/ogcapi/features/cityjson/app/FeaturesFormatCityJsonBase.java b/ogcapi-stable/ogcapi-features-cityjson/src/main/java/de/ii/ogcapi/features/cityjson/app/FeaturesFormatCityJsonBase.java index c253c8e4a..a27b7447b 100644 --- a/ogcapi-stable/ogcapi-features-cityjson/src/main/java/de/ii/ogcapi/features/cityjson/app/FeaturesFormatCityJsonBase.java +++ b/ogcapi-stable/ogcapi-features-cityjson/src/main/java/de/ii/ogcapi/features/cityjson/app/FeaturesFormatCityJsonBase.java @@ -89,7 +89,6 @@ public abstract class FeaturesFormatCityJsonBase extends FeatureFormatExtension { public static final String IGNORE = "ignore"; - protected final FeaturesCoreProviders providers; private final Values codelistStore; protected final FeaturesCoreValidation featuresCoreValidator; protected final SchemaGeneratorOpenApi schemaGeneratorFeature; @@ -108,8 +107,7 @@ public FeaturesFormatCityJsonBase( CrsTransformerFactory crsTransformerFactory, CrsInfo crsInfo, ExtensionRegistry extensionRegistry) { - super(extensionRegistry); - this.providers = providers; + super(extensionRegistry, providers); this.codelistStore = valueStore.forType(Codelist.class); this.featuresCoreValidator = featuresCoreValidator; this.schemaGeneratorFeature = schemaGeneratorFeature; @@ -208,6 +206,11 @@ public boolean canEncodeFeatures() { return true; } + @Override + public boolean supportsRootConcat() { + return true; + } + protected Optional> getFeatureEncoder( FeatureTransformationContext transformationContext, @SuppressWarnings("unused") Optional language, diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/PolicyAttributeFeaturesFormat.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/PolicyAttributeFeaturesFormat.java index 1cf46e1b6..e1e1c81f1 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/PolicyAttributeFeaturesFormat.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/app/PolicyAttributeFeaturesFormat.java @@ -11,6 +11,7 @@ import de.ii.ogcapi.features.core.domain.FeatureFormatExtension; import de.ii.ogcapi.features.core.domain.FeatureTransformationContext; import de.ii.ogcapi.features.core.domain.FeaturesCoreConfiguration; +import de.ii.ogcapi.features.core.domain.FeaturesCoreProviders; import de.ii.ogcapi.foundation.domain.ApiMediaType; import de.ii.ogcapi.foundation.domain.ApiMediaTypeContent; import de.ii.ogcapi.foundation.domain.ExtensionConfiguration; @@ -46,8 +47,9 @@ public class PolicyAttributeFeaturesFormat extends FeatureFormatExtension { .build(); @Inject - public PolicyAttributeFeaturesFormat(ExtensionRegistry extensionRegistry) { - super(extensionRegistry); + public PolicyAttributeFeaturesFormat( + ExtensionRegistry extensionRegistry, FeaturesCoreProviders providers) { + super(extensionRegistry, providers); } @Override @@ -75,6 +77,11 @@ public boolean canEncodeFeatures() { return true; } + @Override + public boolean supportsRootConcat() { + return true; + } + @Override public Optional getPropertyTransformations( FeatureTypeConfigurationOgcApi collectionData) { diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeatureFormatExtension.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeatureFormatExtension.java index bce0f289c..5d7106405 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeatureFormatExtension.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeatureFormatExtension.java @@ -19,18 +19,67 @@ import de.ii.xtraplatform.features.domain.FeatureSchema; import de.ii.xtraplatform.features.domain.FeatureTokenEncoder; import de.ii.xtraplatform.features.domain.transform.PropertyTransformations; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @AutoMultiBind public abstract class FeatureFormatExtension implements FormatExtension { + private static final Logger LOGGER = LoggerFactory.getLogger(FeatureFormatExtension.class); + protected final ExtensionRegistry extensionRegistry; + protected final FeaturesCoreProviders providers; + private final Set warned; - protected FeatureFormatExtension(ExtensionRegistry extensionRegistry) { + protected FeatureFormatExtension( + ExtensionRegistry extensionRegistry, FeaturesCoreProviders providers) { this.extensionRegistry = extensionRegistry; + this.providers = providers; + this.warned = new HashSet<>(); + } + + @Override + public boolean isEnabledForApi(OgcApiDataV2 apiData, String collectionId) { + boolean enabled = FormatExtension.super.isEnabledForApi(apiData, collectionId); + + if (!enabled) { + return false; + } + + return checkRootConcat(apiData); + } + + @Override + public boolean isEnabledForApi(OgcApiDataV2 apiData) { + boolean enabled = FormatExtension.super.isEnabledForApi(apiData); + + if (!enabled) { + return false; + } + + return checkRootConcat(apiData); + } + + private boolean checkRootConcat(OgcApiDataV2 apiData) { + if (supportsRootConcat()) { + return true; + } + + if (providers.hasAnyRootConcat(apiData)) { + if (warned.add(this.getClass() + apiData.getId())) { + LOGGER.warn( + "{} does not support root concatenation in the feature schema, the format will be disabled.", + getMediaType().label()); + } + return false; + } + return true; } public abstract ApiMediaType getCollectionMediaType(); @@ -138,6 +187,10 @@ public boolean isForHumans() { return false; } + public boolean supportsRootConcat() { + return false; + } + public Map getDefaultProfiles(OgcApiDataV2 apiData, String collectionId) { return apiData .getExtension(getBuildingBlockConfigurationType(), collectionId) diff --git a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeaturesCoreProviders.java b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeaturesCoreProviders.java index fd8f7e232..3de1da8aa 100644 --- a/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeaturesCoreProviders.java +++ b/ogcapi-stable/ogcapi-features-core/src/main/java/de/ii/ogcapi/features/core/domain/FeaturesCoreProviders.java @@ -119,6 +119,19 @@ default Optional getQueryablesSchema( schema, List.of("*"), List.of(), ".", true))); } + default boolean hasAnyRootConcat(OgcApiDataV2 apiData) { + return getFeatureSchemas(apiData).values().stream() + .anyMatch(schema -> !schema.getConcat().isEmpty()); + } + + default boolean hasRootConcat(OgcApiDataV2 apiData, String collectionId) { + return getFeatureSchemas(apiData).entrySet().stream() + .anyMatch( + schema -> + Objects.equals(schema.getKey(), collectionId) + && !schema.getValue().getConcat().isEmpty()); + } + T getFeatureProviderOrThrow( OgcApiDataV2 apiData, FeatureTypeConfigurationOgcApi featureType, diff --git a/ogcapi-stable/ogcapi-features-core/src/test/groovy/de/ii/ogcapi/features/core/OgcApiCoreSpecCollections.groovy b/ogcapi-stable/ogcapi-features-core/src/test/groovy/de/ii/ogcapi/features/core/OgcApiCoreSpecCollections.groovy index a087d091f..8397dd806 100644 --- a/ogcapi-stable/ogcapi-features-core/src/test/groovy/de/ii/ogcapi/features/core/OgcApiCoreSpecCollections.groovy +++ b/ogcapi-stable/ogcapi-features-core/src/test/groovy/de/ii/ogcapi/features/core/OgcApiCoreSpecCollections.groovy @@ -230,7 +230,7 @@ class OgcApiCoreSpecCollections extends Specification { } if (extensionType == FeatureFormatExtension.class) { - return ImmutableList.of((T) new FeatureFormatExtension(self) { + return ImmutableList.of((T) new FeatureFormatExtension(self, null) { @Override ApiMediaType getMediaType() { diff --git a/ogcapi-stable/ogcapi-features-geojson/src/main/java/de/ii/ogcapi/features/geojson/app/FeaturesFormatGeoJson.java b/ogcapi-stable/ogcapi-features-geojson/src/main/java/de/ii/ogcapi/features/geojson/app/FeaturesFormatGeoJson.java index 33a39bc6a..90df9d379 100644 --- a/ogcapi-stable/ogcapi-features-geojson/src/main/java/de/ii/ogcapi/features/geojson/app/FeaturesFormatGeoJson.java +++ b/ogcapi-stable/ogcapi-features-geojson/src/main/java/de/ii/ogcapi/features/geojson/app/FeaturesFormatGeoJson.java @@ -81,7 +81,6 @@ public class FeaturesFormatGeoJson extends FeatureFormatExtension .parameter("json") .build(); - private final FeaturesCoreProviders providers; private final Values codelistStore; private final FeaturesCoreValidation featuresCoreValidator; private final SchemaGeneratorOpenApi schemaGeneratorFeature; @@ -97,8 +96,7 @@ public FeaturesFormatGeoJson( SchemaGeneratorCollectionOpenApi schemaGeneratorFeatureCollection, GeoJsonWriterRegistry geoJsonWriterRegistry, ExtensionRegistry extensionRegistry) { - super(extensionRegistry); - this.providers = providers; + super(extensionRegistry, providers); this.codelistStore = valueStore.forType(Codelist.class); this.featuresCoreValidator = featuresCoreValidator; this.schemaGeneratorFeature = schemaGeneratorFeature; @@ -304,6 +302,11 @@ public boolean isComplex() { return true; } + @Override + public boolean supportsRootConcat() { + return true; + } + private Optional getMetadata(Object content, String key) { if (content instanceof byte[]) { JsonNode jsonNode; diff --git a/ogcapi-stable/ogcapi-features-gml/src/main/java/de/ii/ogcapi/features/gml/app/FeaturesFormatGml.java b/ogcapi-stable/ogcapi-features-gml/src/main/java/de/ii/ogcapi/features/gml/app/FeaturesFormatGml.java index 4dfc0e4d9..162142689 100644 --- a/ogcapi-stable/ogcapi-features-gml/src/main/java/de/ii/ogcapi/features/gml/app/FeaturesFormatGml.java +++ b/ogcapi-stable/ogcapi-features-gml/src/main/java/de/ii/ogcapi/features/gml/app/FeaturesFormatGml.java @@ -151,7 +151,6 @@ public class FeaturesFormatGml extends FeatureFormatExtension implements Conform private static final String GMLSF2_CC = "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/gmlsf2"; - private final FeaturesCoreProviders providers; private final Values codelistStore; private final FeaturesCoreValidation featuresCoreValidator; private final GmlWriterRegistry gmlWriterRegistry; @@ -163,8 +162,7 @@ public FeaturesFormatGml( FeaturesCoreValidation featuresCoreValidator, GmlWriterRegistry gmlWriterRegistry, ExtensionRegistry extensionRegistry) { - super(extensionRegistry); - this.providers = providers; + super(extensionRegistry, providers); this.codelistStore = valueStore.forType(Codelist.class); this.featuresCoreValidator = featuresCoreValidator; this.gmlWriterRegistry = gmlWriterRegistry; diff --git a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FeaturesFormatHtml.java b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FeaturesFormatHtml.java index 37ee4feb0..d338440fe 100644 --- a/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FeaturesFormatHtml.java +++ b/ogcapi-stable/ogcapi-features-html/src/main/java/de/ii/ogcapi/features/html/app/FeaturesFormatHtml.java @@ -92,7 +92,6 @@ public class FeaturesFormatHtml extends FeatureFormatExtension private final Values codelistStore; private final I18n i18n; - private final FeaturesCoreProviders providers; private final FeaturesCoreValidation featuresCoreValidator; private final URI servicesUri; private final MustacheRenderer mustacheRenderer; @@ -108,10 +107,9 @@ public FeaturesFormatHtml( FeaturesCoreValidation featuresCoreValidator, ServicesContext servicesContext, Http http) { - super(extensionRegistry); + super(extensionRegistry, providers); this.codelistStore = valueStore.forType(Codelist.class); this.i18n = i18n; - this.providers = providers; this.featuresCoreValidator = featuresCoreValidator; this.servicesUri = servicesContext.getUri(); this.mustacheRenderer = mustacheRenderer; @@ -219,10 +217,7 @@ public Class getBuildingBlockConfigurationType @Override public boolean isEnabledForApi(OgcApiDataV2 apiData) { - return apiData - .getExtension(getBuildingBlockConfigurationType()) - .map(ExtensionConfiguration::isEnabled) - .orElse(false) + return super.isEnabledForApi(apiData) && apiData .getExtension(HtmlConfiguration.class) .map(ExtensionConfiguration::isEnabled) @@ -373,6 +368,11 @@ public boolean isForHumans() { return true; } + @Override + public boolean supportsRootConcat() { + return true; + } + private ModifiableFeatureCollectionView createFeatureCollectionView( OgcApi api, FeatureTypeConfigurationOgcApi featureType, From 02906a99a1acd2a1213ec4fd2935b9ccd8e56c57 Mon Sep 17 00:00:00 2001 From: Clemens Portele Date: Mon, 7 Oct 2024 08:56:23 +0200 Subject: [PATCH 7/8] additional root concatenation changes (#1276) --- .../app/QueryParameterIntersects.java | 2 +- .../jsonfg/app/JsonFgWriterConformsTo.java | 6 ++-- .../geojson/app/FeaturesFormatGeoJson.java | 10 ++----- .../tiles/app/TilesQueriesHandlerImpl.java | 29 ++++--------------- 4 files changed, 13 insertions(+), 34 deletions(-) diff --git a/ogcapi-custom/ogcapi-features-custom-extensions/src/main/java/de/ii/ogcapi/features/custom/extensions/app/QueryParameterIntersects.java b/ogcapi-custom/ogcapi-features-custom-extensions/src/main/java/de/ii/ogcapi/features/custom/extensions/app/QueryParameterIntersects.java index 6af0102e6..d637641d6 100644 --- a/ogcapi-custom/ogcapi-features-custom-extensions/src/main/java/de/ii/ogcapi/features/custom/extensions/app/QueryParameterIntersects.java +++ b/ogcapi-custom/ogcapi-features-custom-extensions/src/main/java/de/ii/ogcapi/features/custom/extensions/app/QueryParameterIntersects.java @@ -144,7 +144,7 @@ public Cql2Expression parse( Optional primaryGeometry = providers - .getFeatureSchema(api.getData(), collectionData) + .getQueryablesSchema(api.getData(), collectionData) .orElseThrow( () -> new IllegalStateException( diff --git a/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/JsonFgWriterConformsTo.java b/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/JsonFgWriterConformsTo.java index 864d6dabc..1f7f946ce 100644 --- a/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/JsonFgWriterConformsTo.java +++ b/ogcapi-draft/ogcapi-features-jsonfg/src/main/java/de/ii/ogcapi/features/jsonfg/app/JsonFgWriterConformsTo.java @@ -17,6 +17,7 @@ import de.ii.xtraplatform.features.domain.FeatureSchema; import de.ii.xtraplatform.geometries.domain.SimpleFeatureGeometry; import java.io.IOException; +import java.util.Collection; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -121,9 +122,8 @@ private boolean has3d(FeatureTransformationContextGeoJson transformationContext) return transformationContext.getFeatureSchemas().values().stream() .filter(Optional::isPresent) .map(Optional::get) - .map(FeatureSchema::getPrimaryGeometry) - .filter(Optional::isPresent) - .map(Optional::get) + .map(FeatureSchema::getPrimaryGeometries) + .flatMap(Collection::stream) .filter( s -> s.getGeometryType() diff --git a/ogcapi-stable/ogcapi-features-geojson/src/main/java/de/ii/ogcapi/features/geojson/app/FeaturesFormatGeoJson.java b/ogcapi-stable/ogcapi-features-geojson/src/main/java/de/ii/ogcapi/features/geojson/app/FeaturesFormatGeoJson.java index 90df9d379..7577472ec 100644 --- a/ogcapi-stable/ogcapi-features-geojson/src/main/java/de/ii/ogcapi/features/geojson/app/FeaturesFormatGeoJson.java +++ b/ogcapi-stable/ogcapi-features-geojson/src/main/java/de/ii/ogcapi/features/geojson/app/FeaturesFormatGeoJson.java @@ -42,7 +42,6 @@ import de.ii.xtraplatform.entities.domain.ValidationResult.MODE; import de.ii.xtraplatform.features.domain.FeatureSchema; import de.ii.xtraplatform.features.domain.FeatureTokenEncoder; -import de.ii.xtraplatform.features.domain.SchemaBase; import de.ii.xtraplatform.features.domain.transform.PropertyTransformation; import de.ii.xtraplatform.values.domain.ValueStore; import de.ii.xtraplatform.values.domain.Values; @@ -145,14 +144,11 @@ public ValidationResult onStartup(OgcApi api, MODE apiValidation) { Map featureSchemas = providers.getFeatureSchemas(api.getData()); for (Map.Entry entry : featureSchemas.entrySet()) { - if (entry - .getValue() - .getPrimaryGeometry() - .filter(SchemaBase::isSimpleFeatureGeometry) - .isEmpty()) { + if (entry.getValue().getPrimaryGeometries().stream() + .anyMatch(s -> !s.isSimpleFeatureGeometry())) { builder.addStrictErrors( String.format( - "Feature type '%s' does not have a primary geometry that is a Simple Feature geometry. GeoJSON only supports Simple Feature geometry types.", + "Feature type '%s' has a primary geometry that is not a Simple Feature geometry. GeoJSON only supports Simple Feature geometry types.", entry.getKey())); } } diff --git a/ogcapi-stable/ogcapi-tiles/src/main/java/de/ii/ogcapi/tiles/app/TilesQueriesHandlerImpl.java b/ogcapi-stable/ogcapi-tiles/src/main/java/de/ii/ogcapi/tiles/app/TilesQueriesHandlerImpl.java index 15bdb2b2a..5992c18a6 100644 --- a/ogcapi-stable/ogcapi-tiles/src/main/java/de/ii/ogcapi/tiles/app/TilesQueriesHandlerImpl.java +++ b/ogcapi-stable/ogcapi-tiles/src/main/java/de/ii/ogcapi/tiles/app/TilesQueriesHandlerImpl.java @@ -78,7 +78,6 @@ import de.ii.xtraplatform.crs.domain.OgcCrs; import de.ii.xtraplatform.features.domain.FeatureSchema; import de.ii.xtraplatform.features.domain.FeatureTypeConfiguration; -import de.ii.xtraplatform.geometries.domain.SimpleFeatureGeometry; import de.ii.xtraplatform.tiles.domain.ImmutableTileGenerationParametersTransient; import de.ii.xtraplatform.tiles.domain.ImmutableTileQuery; import de.ii.xtraplatform.tiles.domain.ImmutableTilesBoundingBox; @@ -997,29 +996,13 @@ public TileSet buildTileSet( .description(collectionData.getDescription()) .dataType(dataType); - if (levels.isPresent()) { - builder2 - .minTileMatrix(String.valueOf(levels.get().getMin())) - .maxTileMatrix(String.valueOf(levels.get().getMax())); - } + levels.ifPresent( + minMax -> + builder2 + .minTileMatrix(String.valueOf(minMax.getMin())) + .maxTileMatrix(String.valueOf(minMax.getMax()))); - switch (vectorSchema - .getPrimaryGeometry() - .flatMap(FeatureSchema::getGeometryType) - .orElse(SimpleFeatureGeometry.ANY)) { - case POINT: - case MULTI_POINT: - builder2.geometryDimension(0); - break; - case LINE_STRING: - case MULTI_LINE_STRING: - builder2.geometryDimension(1); - break; - case POLYGON: - case MULTI_POLYGON: - builder2.geometryDimension(2); - break; - } + vectorSchema.getEffectiveGeometryDimension().ifPresent(builder2::geometryDimension); builder2.propertiesSchema( new ImmutableJsonSchemaObject.Builder() From 88b988653e53d037485995eaa6b5f8ec3b8090c0 Mon Sep 17 00:00:00 2001 From: Andreas Zahnen Date: Mon, 7 Oct 2024 09:12:42 +0200 Subject: [PATCH 8/8] upgrade xtraplatform --- xtraplatform.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtraplatform.gradle b/xtraplatform.gradle index 1e72bf232..a088317af 100644 --- a/xtraplatform.gradle +++ b/xtraplatform.gradle @@ -3,5 +3,5 @@ dependencies { layers group: 'de.interactive_instruments', name: 'xtraplatform-core', version: '6.2.0-SNAPSHOT' layers group: 'de.interactive_instruments', name: 'xtraplatform-native', version: "2.4.0-${platform}" - layers group: 'de.interactive_instruments', name: 'xtraplatform-spatial', version: '7.2.0-root-concat-SNAPSHOT' + layers group: 'de.interactive_instruments', name: 'xtraplatform-spatial', version: '7.2.0-SNAPSHOT' }