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
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@
<artifactId>gt-referencing</artifactId>
<version>${org.geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-wps</artifactId>
<version>${org.geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-xml</artifactId>
<version>${org.geotools.version}</version>
</dependency>
<!--
https://mvnrepository.com/artifact/org.geotools/gt-epsg-hsql
it is use to lookup EPSG values
Expand Down
8 changes: 8 additions & 0 deletions server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</artifactId>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-wps</artifactId>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-xml</artifactId>
</dependency>
<dependency>
<groupId>org.locationtech.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,87 +1,7 @@
package au.org.aodn.ogcapi.server.core.service.geoserver;

import au.org.aodn.ogcapi.server.core.model.ogc.wfs.WfsField;
import au.org.aodn.ogcapi.server.core.model.ogc.wfs.WfsFields;
import au.org.aodn.ogcapi.server.core.service.geoserver.wfs.WfsServer;
import au.org.aodn.ogcapi.server.core.util.DatetimeUtils;
import au.org.aodn.ogcapi.server.core.util.GeometryUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Slf4j
public abstract class Server {
protected WfsServer wfsServer;

public Server(WfsServer wfsServer) {
this.wfsServer = wfsServer;
}
/**
* Build CQL filter for temporal and spatial constraints
*/
protected String buildCqlFilter(String wfsServerUrl, String uuid, String layerName, String sd, String ed, Object multiPolygon) {

WfsFields wfsFieldModel = wfsServer.getDownloadableFields(
uuid,
WfsServer.WfsFeatureRequest.builder()
.layerName(layerName)
.server(wfsServerUrl)
.build()
);
log.debug("WFSFieldModel by wfs typename: {}", wfsFieldModel);

// Validate start and end dates
final String startDate = DatetimeUtils.validateAndFormatDate(sd, true);
final String endDate = DatetimeUtils.validateAndFormatDate(ed, false);

StringBuilder cqlFilter = new StringBuilder();

if (wfsFieldModel == null || wfsFieldModel.getFields() == null) {
return cqlFilter.toString();
}

List<WfsField> fields = wfsFieldModel.getFields();

// Possible to have multiple days, better to consider all
List<WfsField> temporalField = fields.stream()
.filter(field -> "dateTime".equals(field.getType()) || "date".equals(field.getType()))
.toList();

// Add temporal filter only if both dates are specified
if (!temporalField.isEmpty() && startDate != null && !startDate.isEmpty() && endDate != null && !endDate.isEmpty()) {
List<String> cqls = new ArrayList<>();
temporalField.forEach(temp ->
cqls.add(String.format("(%s DURING %sT00:00:00Z/%sT23:59:59Z)", temp.getName(), startDate, endDate))
);
cqlFilter.append("(").append(String.join(" OR ", cqls)).append(")");
}

// Find geometry field
Optional<WfsField> geometryField = fields.stream()
.filter(field -> "geometrypropertytype".equalsIgnoreCase(field.getType()))
.findFirst();

// Add spatial filter
if (geometryField.isPresent() && multiPolygon != null) {
String fieldName = geometryField.get().getName();

String wkt = GeometryUtils.convertToWkt(multiPolygon);

if ((wkt != null) && !cqlFilter.isEmpty()) {
cqlFilter.append(" AND ");
}

if (wkt != null) {
cqlFilter.append("INTERSECTS(")
.append(fieldName)
.append(",")
.append(wkt)
.append(")");
}
}

return cqlFilter.toString();
}
/**
* Common interface for GeoServer component, we need to refactor and move common function here later
*/
public interface Server {
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package au.org.aodn.ogcapi.server.core.service.geoserver.wfs;

import au.org.aodn.ogcapi.server.core.model.ogc.FeatureRequest;
import au.org.aodn.ogcapi.server.core.service.geoserver.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -18,7 +17,8 @@

@Slf4j
@Service
public class DownloadWfsDataService extends Server {
public class DownloadWfsDataService {
private final WfsServer wfsServer;
private final RestTemplate restTemplate;
private final HttpEntity<?> pretendUserEntity;
private final int chunkSize;
Expand All @@ -29,7 +29,7 @@ public DownloadWfsDataService(
@Qualifier("pretendUserEntity") HttpEntity<?> pretendUserEntity,
@Value("${app.sse.chunkSize:16384}") int chunkSize
) {
super(wfsServer);
this.wfsServer = wfsServer;
this.restTemplate = restTemplate;
this.pretendUserEntity = pretendUserEntity;
this.chunkSize = chunkSize;
Expand All @@ -55,7 +55,7 @@ public String prepareWfsRequestUrl(
String wfsServerUrl = featureServerUrl.get();

// Build CQL filter
String cqlFilter = buildCqlFilter(wfsServerUrl, uuid, layerName, startDate, endDate, multiPolygon);
String cqlFilter = wfsServer.buildCqlFilter(wfsServerUrl, uuid, layerName, startDate, endDate, multiPolygon);

// Build final WFS request URL
String wfsRequestUrl = wfsServer.createWfsRequestUrl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import au.org.aodn.ogcapi.server.core.model.ogc.wfs.*;
import au.org.aodn.ogcapi.server.core.service.ElasticSearchBase;
import au.org.aodn.ogcapi.server.core.service.Search;
import au.org.aodn.ogcapi.server.core.service.geoserver.Server;
import au.org.aodn.ogcapi.server.core.util.DatetimeUtils;
import au.org.aodn.ogcapi.server.core.util.GeometryUtils;
import au.org.aodn.ogcapi.server.core.util.RestTemplateUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
Expand Down Expand Up @@ -40,7 +43,7 @@
import static au.org.aodn.ogcapi.server.core.util.GeoserverUtils.*;

@Slf4j
public class WfsServer {
public class WfsServer implements Server {
// Cannot use singleton bean as it impacted other dependency
protected final XmlMapper xmlMapper;
protected RestTemplateUtils restTemplateUtils;
Expand Down Expand Up @@ -79,7 +82,72 @@ public WfsServer(Search search,
this.pretendUserEntity = entity;
this.wfsDefaultParam = wfsDefaultParam;
}
/**
* Build CQL filter for temporal and spatial constraints
*/
protected String buildCqlFilter(String serverUrl, String uuid, String layerName, String sd, String ed, Object multiPolygon) {

WfsFields wfsFieldModel = self.getDownloadableFields(
uuid,
WfsServer.WfsFeatureRequest.builder()
.layerName(layerName)
.server(serverUrl)
.build()
);
log.debug("WFSFieldModel by wfs typename: {}", wfsFieldModel);

// Validate start and end dates
final String startDate = DatetimeUtils.validateAndFormatDate(sd, true);
final String endDate = DatetimeUtils.validateAndFormatDate(ed, false);

StringBuilder cqlFilter = new StringBuilder();

if (wfsFieldModel == null || wfsFieldModel.getFields() == null) {
return cqlFilter.toString();
}

List<WfsField> fields = wfsFieldModel.getFields();

// Possible to have multiple days, better to consider all
List<WfsField> temporalField = fields.stream()
.filter(field -> "dateTime".equals(field.getType()) || "date".equals(field.getType()))
.toList();

// Add temporal filter only if both dates are specified
if (!temporalField.isEmpty() && startDate != null && !startDate.isEmpty() && endDate != null && !endDate.isEmpty()) {
List<String> cqls = new ArrayList<>();
temporalField.forEach(temp ->
cqls.add(String.format("(%s DURING %sT00:00:00Z/%sT23:59:59Z)", temp.getName(), startDate, endDate))
);
cqlFilter.append("(").append(String.join(" OR ", cqls)).append(")");
}

// Find geometry field
Optional<WfsField> geometryField = fields.stream()
.filter(field -> "geometrypropertytype".equalsIgnoreCase(field.getType()))
.findFirst();

// Add spatial filter
if (geometryField.isPresent() && multiPolygon != null) {
String fieldName = geometryField.get().getName();

String wkt = GeometryUtils.convertToWkt(multiPolygon);

if ((wkt != null) && !cqlFilter.isEmpty()) {
cqlFilter.append(" AND ");
}

if (wkt != null) {
cqlFilter.append("INTERSECTS(")
.append(fieldName)
.append(",")
.append(wkt)
.append(")");
}
}

return cqlFilter.toString();
}
/**
* Build WFS GetFeature URL
*/
Expand Down
Loading
Loading