Skip to content
Closed
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
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
<artifactId>gt-cql</artifactId>
<version>${org.geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-wfs-ng</artifactId>
<version>${org.geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
Expand Down
4 changes: 4 additions & 0 deletions server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@
<groupId>org.geotools</groupId>
<artifactId>gt-cql</artifactId>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-wfs-ng</artifactId>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,70 +1,16 @@
package au.org.aodn.ogcapi.server.core.model.ogc.wfs;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.*;

import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JacksonXmlRootElement(localName = "FeatureType")
@Getter
@Setter
public class FeatureTypeInfo {

@JacksonXmlProperty(localName = "Name")
protected String name;

@JacksonXmlProperty(localName = "Title")
protected String title;

@JsonProperty("abstract")
@JacksonXmlProperty(localName = "Abstract")
private String abstract_;

@JacksonXmlProperty(localName = "Keywords", namespace = "http://www.opengis.net/ows/1.1")
private Keywords keywords;

@JacksonXmlProperty(localName = "DefaultCRS")
private String defaultCRS;

@JacksonXmlProperty(localName = "WGS84BoundingBox", namespace = "http://www.opengis.net/ows/1.1")
private WGS84BoundingBox wgs84BoundingBox;

@JacksonXmlProperty(localName = "MetadataURL")
private MetadataURL metadataURL;

@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Keywords {
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "Keyword", namespace = "http://www.opengis.net/ows/1.1")
private List<String> keyword;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public static class WGS84BoundingBox {
@JacksonXmlProperty(localName = "LowerCorner", namespace = "http://www.opengis.net/ows/1.1")
private String lowerCorner;

@JacksonXmlProperty(localName = "UpperCorner", namespace = "http://www.opengis.net/ows/1.1")
private String upperCorner;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public static class MetadataURL {
@JacksonXmlProperty(isAttribute = true, localName = "href", namespace = "http://www.w3.org/1999/xlink")
private String href;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import au.org.aodn.ogcapi.server.core.model.StacCollectionModel;
import au.org.aodn.ogcapi.server.core.model.ogc.FeatureRequest;
import au.org.aodn.ogcapi.server.core.model.ogc.wfs.WfsDescribeFeatureTypeResponse;
import au.org.aodn.ogcapi.server.core.model.ogc.wfs.WfsGetCapabilitiesResponse;
import au.org.aodn.ogcapi.server.core.model.ogc.wfs.FeatureTypeInfo;
import au.org.aodn.ogcapi.server.core.model.ogc.wfs.DownloadableFieldModel;
import au.org.aodn.ogcapi.server.core.service.ElasticSearchBase;
Expand All @@ -16,6 +15,9 @@
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.store.ContentFeatureSource;
import org.geotools.data.wfs.WFSDataStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
Expand All @@ -27,14 +29,11 @@
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;

import static au.org.aodn.ogcapi.server.core.configuration.CacheConfig.DOWNLOADABLE_FIELDS;
import static au.org.aodn.ogcapi.server.core.configuration.CacheConfig.GET_CAPABILITIES_WFS_FEATURE_TYPES;
import static au.org.aodn.ogcapi.server.core.configuration.CacheConfig.*;
import static au.org.aodn.ogcapi.server.core.service.wfs.WfsDefaultParam.WFS_LINK_MARKER;
import static au.org.aodn.ogcapi.server.core.util.GeoserverUtils.extractLayernameOrTypenameFromUrl;
import static au.org.aodn.ogcapi.server.core.util.GeoserverUtils.roughlyMatch;
Expand Down Expand Up @@ -193,6 +192,30 @@ public Optional<String> getFeatureServerUrlByTitleOrQueryParam(String collection
}
}

public WFSDataStore createDataStore(String wfsServerUrl) throws IOException {
// Parse the base URL to construct GetCapabilities request
UriComponents components = UriComponentsBuilder.fromUriString(wfsServerUrl).build();

// Build GetCapabilities URL
UriComponentsBuilder builder = UriComponentsBuilder
.newInstance()
.scheme("https") // hardcode to be https to avoid redirect
.port(components.getPort())
.host(components.getHost())
.path(components.getPath() != null ? components.getPath() : "/geoserver/ows")
.queryParam("service", "wfs")
.queryParam("request", "GetCapabilities");

String url = builder.build().toUriString();
Map<String, Object> params = new HashMap<>();
params.put("WFSDataStoreFactory:GET_CAPABILITIES_URL", url);
params.put("WFSDataStoreFactory:PROTOCOL", "1.1.0");
params.put("WFSDataStoreFactory:HTTP_HEADERS", pretendUserEntity.getHeaders().toSingleValueMap());

log.debug("WFS GetCapabilities URL: {}", url);

return (WFSDataStore)DataStoreFinder.getDataStore(params);
}
/**
* Fetch raw feature types from WFS GetCapabilities - cached by URL.
* This allows multiple collections sharing the same WFS server to use cached results.
Expand All @@ -203,49 +226,28 @@ public Optional<String> getFeatureServerUrlByTitleOrQueryParam(String collection
@Cacheable(value = GET_CAPABILITIES_WFS_FEATURE_TYPES)
public List<FeatureTypeInfo> fetchCapabilitiesFeatureTypesByUrl(String wfsServerUrl) {
try {
// Parse the base URL to construct GetCapabilities request
UriComponents components = UriComponentsBuilder.fromUriString(wfsServerUrl).build();

// Build GetCapabilities URL
UriComponentsBuilder builder = UriComponentsBuilder
.newInstance()
.scheme("https") // hardcode to be https to avoid redirect
.port(components.getPort())
.host(components.getHost())
.path(components.getPath() != null ? components.getPath() : "/geoserver/ows")
.queryParam("service", "wfs")
.queryParam("request", "GetCapabilities");

String url = builder.build().toUriString();
log.debug("WFS GetCapabilities URL: {}", url);

// Make the HTTPS call
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, pretendUserEntity, String.class);

if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
// Parse XML response
WfsGetCapabilitiesResponse capabilitiesResponse = xmlMapper.readValue(
response.getBody(),
WfsGetCapabilitiesResponse.class
WFSDataStore ds = self.createDataStore(wfsServerUrl);
String[] typeNames = ds.getTypeNames();
List<FeatureTypeInfo> schemas = new ArrayList<>();

for (String typeName : typeNames) {
ContentFeatureSource c = ds.getFeatureSource(typeName);

schemas.add(
FeatureTypeInfo.builder()
.name(c.getInfo().getName())
.title(c.getInfo().getTitle())
.build()
);
}

// Extract all feature types
if (capabilitiesResponse != null
&& capabilitiesResponse.getFeatureTypeList() != null
&& capabilitiesResponse.getFeatureTypeList().getFeatureTypes() != null) {

List<FeatureTypeInfo> featureTypes = capabilitiesResponse.getFeatureTypeList().getFeatureTypes();
log.info("Fetched {} WFS feature types", schemas.size());
return schemas;

log.info("Fetched and cached WFS get-capabilities feature types: {} ", featureTypes.size());
return featureTypes;
}
}
} catch (RestClientException | JsonProcessingException e) {
} catch (RestClientException | IOException e) {
log.error("Error fetching WFS GetCapabilities for URL: {}", wfsServerUrl, e);
throw new RuntimeException(e);
}

return Collections.emptyList();
}

/**
Expand Down
Loading