Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import de.ii.xtraplatform.cql.domain.Cql;
import de.ii.xtraplatform.crs.domain.BoundingBox;
import de.ii.xtraplatform.crs.domain.CrsInfo;
import de.ii.xtraplatform.crs.domain.CrsTransformationException;
import de.ii.xtraplatform.crs.domain.CrsTransformerFactory;
import de.ii.xtraplatform.crs.domain.EpsgCrs;
import de.ii.xtraplatform.crs.domain.OgcCrs;
Expand Down Expand Up @@ -316,10 +315,15 @@ public FeatureSchema getSortablesSchema(

@Override
public Optional<BoundingBox> getSpatialExtent(String typeName) {
if (getData().getTypes().containsKey(typeName)) {
if (!getData().getTypes().containsKey(typeName)) {
return Optional.empty();
}

Optional<BoundingBox> configured = getConfiguredSpatialExtent(typeName);
if (configured.isPresent()) {
return configured;
}

try {
Stream<Optional<BoundingBox>> extentGraph =
aggregateStatsReader.getSpatialExtent(
Expand All @@ -342,23 +346,16 @@ public Optional<BoundingBox> getSpatialExtent(String typeName) {
@Override
public Optional<BoundingBox> getSpatialExtent(String typeName, EpsgCrs crs) {
return getSpatialExtent(typeName)
.flatMap(
boundingBox ->
crsTransformerFactory
.getTransformer(getNativeCrs(), crs, false)
.flatMap(
crsTransformer -> {
try {
return Optional.of(crsTransformer.transformBoundingBox(boundingBox));
} catch (CrsTransformationException e) {
return Optional.empty();
}
}));
.flatMap(boundingBox -> transformSpatialExtent(boundingBox, crs));
}

@Override
public Optional<Interval> getTemporalExtent(String typeName) {
return Optional.empty();
if (!getData().getTypes().containsKey(typeName)) {
return Optional.empty();
}

return getConfiguredTemporalExtent(typeName);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import de.ii.xtraplatform.cql.domain.Cql;
import de.ii.xtraplatform.crs.domain.BoundingBox;
import de.ii.xtraplatform.crs.domain.CrsInfo;
import de.ii.xtraplatform.crs.domain.CrsTransformationException;
import de.ii.xtraplatform.crs.domain.CrsTransformerFactory;
import de.ii.xtraplatform.crs.domain.EpsgCrs;
import de.ii.xtraplatform.crs.domain.OgcCrs;
Expand Down Expand Up @@ -312,7 +311,7 @@ public boolean is3dSupported() {
@Override
@SuppressWarnings("PMD.AvoidCatchingGenericException")
public long getFeatureCount(String typeName) {
if (getData().getTypes().containsKey(typeName)) {
if (!getData().getTypes().containsKey(typeName)) {
return -1;
}

Expand Down Expand Up @@ -358,10 +357,15 @@ public FeatureSchema getSortablesSchema(
@Override
@SuppressWarnings("PMD.AvoidCatchingGenericException")
public Optional<BoundingBox> getSpatialExtent(String typeName) {
if (getData().getTypes().containsKey(typeName)) {
if (!getData().getTypes().containsKey(typeName)) {
return Optional.empty();
}

Optional<BoundingBox> configured = getConfiguredSpatialExtent(typeName);
if (configured.isPresent()) {
return configured;
}

try {
Stream<Optional<BoundingBox>> extentGraph =
aggregateStatsReader.getSpatialExtent(
Expand All @@ -383,23 +387,15 @@ public Optional<BoundingBox> getSpatialExtent(String typeName) {
@Override
public Optional<BoundingBox> getSpatialExtent(String typeName, EpsgCrs crs) {
return getSpatialExtent(typeName)
.flatMap(
boundingBox ->
crsTransformerFactory
.getTransformer(getNativeCrs(), crs, false)
.flatMap(
crsTransformer -> {
try {
return Optional.of(crsTransformer.transformBoundingBox(boundingBox));
} catch (CrsTransformationException e) {
return Optional.empty();
}
}));
.flatMap(boundingBox -> transformSpatialExtent(boundingBox, crs));
}

@Override
public Optional<Interval> getTemporalExtent(String typeName) {
return Optional.empty();
if (!getData().getTypes().containsKey(typeName)) {
return Optional.empty();
}
return getConfiguredTemporalExtent(typeName);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,11 @@ public Optional<BoundingBox> getSpatialExtent(String typeName) {
return Optional.empty();
}

Optional<BoundingBox> configured = getConfiguredSpatialExtent(typeName);
if (configured.isPresent()) {
return configured;
}

String[] cacheKey = {typeName, "stats", "spatial"};
String cacheValidator = getData().getStableHash();

Expand Down Expand Up @@ -1107,18 +1112,7 @@ public Optional<BoundingBox> getSpatialExtent(String typeName) {
@Override
public Optional<BoundingBox> getSpatialExtent(String typeName, EpsgCrs crs) {
return getSpatialExtent(typeName)
.flatMap(
boundingBox ->
crsTransformerFactory
.getTransformer(getNativeCrs(), crs, false)
.flatMap(
crsTransformer -> {
try {
return Optional.of(crsTransformer.transformBoundingBox(boundingBox));
} catch (Exception e) {
return Optional.empty();
}
}));
.flatMap(boundingBox -> transformSpatialExtent(boundingBox, crs));
}

@Override
Expand All @@ -1127,6 +1121,11 @@ public Optional<Interval> getTemporalExtent(String typeName) {
return Optional.empty();
}

Optional<Interval> configured = getConfiguredTemporalExtent(typeName);
if (configured.isPresent()) {
return configured;
}

String[] cacheKey = {typeName, "stats", "temporal"};
String cacheValidator = getData().getStableHash();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import de.ii.xtraplatform.base.domain.resiliency.Volatile2;
import de.ii.xtraplatform.base.domain.resiliency.VolatileRegistry;
import de.ii.xtraplatform.codelists.domain.Codelist;
import de.ii.xtraplatform.crs.domain.BoundingBox;
import de.ii.xtraplatform.crs.domain.CrsInfo;
import de.ii.xtraplatform.crs.domain.CrsTransformationException;
import de.ii.xtraplatform.crs.domain.CrsTransformerFactory;
import de.ii.xtraplatform.crs.domain.EpsgCrs;
import de.ii.xtraplatform.crs.domain.OgcCrs;
Expand All @@ -44,6 +46,9 @@
import de.ii.xtraplatform.streams.domain.Reactive.Stream;
import de.ii.xtraplatform.values.domain.Values;
import java.io.IOException;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
Expand All @@ -62,6 +67,7 @@
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.extra.Interval;

public abstract class AbstractFeatureProvider<
T, U, V extends FeatureProviderConnector.QueryOptions, W extends SchemaBase<W>>
Expand Down Expand Up @@ -591,6 +597,72 @@ protected Query preprocessQuery(Query query) {
return query;
}

protected Optional<BoundingBox> getConfiguredSpatialExtent(String typeName) {
Optional<SpatialExtent> fromType =
Optional.ofNullable(getData().getTypes().get(typeName))
.flatMap(FeatureSchema::getExtent)
.flatMap(FeatureTypeExtent::getSpatial);
Optional<SpatialExtent> fromProvider =
getData().getExtent().flatMap(FeatureTypeExtent::getSpatial);

return fromType
.or(() -> fromProvider)
.filter(extent -> !Boolean.TRUE.equals(extent.getComputed()))
.flatMap(extent -> extent.toBoundingBox(getData().getNativeCrs().orElse(OgcCrs.CRS84)));
}

protected Optional<Interval> getConfiguredTemporalExtent(String typeName) {
Optional<TemporalExtent> fromType =
Optional.ofNullable(getData().getTypes().get(typeName))
.flatMap(FeatureSchema::getExtent)
.flatMap(FeatureTypeExtent::getTemporal);
Optional<TemporalExtent> fromProvider =
getData().getExtent().flatMap(FeatureTypeExtent::getTemporal);

return fromType
.or(() -> fromProvider)
.filter(extent -> !Boolean.TRUE.equals(extent.getComputed()))
.flatMap(
extent -> {
if (extent.getStart() == null && extent.getEnd() == null) {
return Optional.empty();
}
OffsetDateTime start =
extent.getStart() != null
? parseConfiguredTemporalBound(extent.getStart(), false)
: OffsetDateTime.parse("0001-01-01T00:00:00Z");
OffsetDateTime end =
extent.getEnd() != null
? parseConfiguredTemporalBound(extent.getEnd(), true)
: OffsetDateTime.parse("9999-12-31T23:59:59Z");
return Optional.of(Interval.of(start.toInstant(), end.toInstant()));
});
}

protected Optional<BoundingBox> transformSpatialExtent(
BoundingBox boundingBox, EpsgCrs targetCrs) {
return crsTransformerFactory
.getTransformer(getData().getNativeCrs().orElse(OgcCrs.CRS84), targetCrs, false)
.flatMap(
crsTransformer -> {
try {
return Optional.of(crsTransformer.transformBoundingBox(boundingBox));
} catch (CrsTransformationException e) {
return Optional.empty();
}
});
}

private static OffsetDateTime parseConfiguredTemporalBound(String value, boolean endOfDay) {
if (value.contains("T")) {
return OffsetDateTime.parse(value);
}

return endOfDay
? LocalDate.parse(value).atTime(23, 59, 59).atOffset(ZoneOffset.UTC)
: LocalDate.parse(value).atStartOfDay().atOffset(ZoneOffset.UTC);
}

@Override
public FeatureChanges changes() {
return changeHandler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,14 @@ default List<CustomFunction> getCql2Functions() {
@Override
Optional<Boolean> getAuto();

/**
* @langEn Optional spatial and temporal extent for all types in this provider. If set, disables
* automatic calculation.
* @langDe Optionaler räumlicher und zeitlicher Extent für alle Types dieses Providers. Wenn
* gesetzt, wird keine automatische Berechnung durchgeführt.
*/
Optional<FeatureTypeExtent> getExtent();

// custom builder to automatically use keys of types as name of FeatureTypeV2
abstract class Builder<T extends Builder<T>> implements EntityDataBuilder<FeatureProviderDataV2> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,12 @@ default Type getType() {
*/
Optional<String> getDescription();

/**
* @langEn Optional spatial and temporal extent for this feature type.
* @langDe Optionaler räumlicher und zeitlicher Extent für diesen Feature-Type.
*/
Optional<FeatureTypeExtent> getExtent();

/**
* @langEn The unit of measurement of the value, only relevant for numeric properties.
* @langDe Die Maßeinheit des Wertes, nur relevant bei numerischen Eigenschaften.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,12 @@ public interface FeatureTypeConfiguration {
* @default ""
*/
Optional<String> getDescription();

/**
* @langEn Optional spatial and temporal extent for this type. If set, disables automatic
* calculation for this type.
* @langDe Optionaler räumlicher und zeitlicher Extent für diesen Type. Wenn gesetzt, wird keine
* automatische Berechnung für diesen Type durchgeführt.
*/
Optional<FeatureTypeExtent> getExtent();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2026 interactive instruments GmbH
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package de.ii.xtraplatform.features.domain;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.util.Optional;
import org.immutables.value.Value;

/** Extent object for spatial and temporal extents. */
@Value.Immutable
@JsonDeserialize(builder = ImmutableFeatureTypeExtent.Builder.class)
public interface FeatureTypeExtent {
Optional<SpatialExtent> getSpatial();

Optional<TemporalExtent> getTemporal();
}
Loading
Loading