Skip to content
Merged
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 @@ -36,8 +36,7 @@
import static au.org.aodn.ogcapi.server.core.configuration.CacheConfig.CACHE_WMS_MAP_TILE;
import static au.org.aodn.ogcapi.server.core.configuration.CacheConfig.GET_CAPABILITIES_WMS_LAYERS;
import static au.org.aodn.ogcapi.server.core.service.wms.WmsDefaultParam.WMS_LINK_MARKER;
import static au.org.aodn.ogcapi.server.core.util.GeoserverUtils.extractLayernameOrTypenameFromUrl;
import static au.org.aodn.ogcapi.server.core.util.GeoserverUtils.roughlyMatch;
import static au.org.aodn.ogcapi.server.core.util.GeoserverUtils.*;

@Slf4j
public class WmsServer {
Expand Down Expand Up @@ -652,36 +651,58 @@
}

/**
* Filter WMS layers based on matching with WFS links
* Matching logic:
* 1. Primary: link.title matches layer.name OR layer.title (fuzzy match)
* 2. Fallback: extract layername from link URI, then layername matches layer.name OR layer.title (fuzzy match)
* Get all WMS links from a collection.
*
* @param collectionId - The uuid
* @param layers - List of layers to filter
* @return Filtered list of WMS layers that have matching metadata WMS links
* @return - List of WMS LinkModel objects from the collection
*/
public List<LayerInfo> filterLayersByWmsLinks(String collectionId, List<LayerInfo> layers) {
protected List<LinkModel> getWmsLinks(String collectionId) {
ElasticSearchBase.SearchResult<StacCollectionModel> result = search.searchCollections(collectionId);

if (result.getCollections().isEmpty()) {
log.info("Return empty layers if as no collection found for collectionId: {}", collectionId);
log.info("No collection found for collectionId: {}", collectionId);
return Collections.emptyList();
}

StacCollectionModel model = result.getCollections().get(0);

// Filter WMS links where ai:group == "Data Access > wms"
List<LinkModel> wmsLinks = model.getLinks()
// Filter WMS links where ai:group contains WMS_LINK_MARKER
return model.getLinks()
.stream()
.filter(link -> link.getAiGroup() != null)
.filter(link -> link.getAiGroup().contains(WMS_LINK_MARKER))
.toList();
}

// Filter WMS layers based on matching with WFS links
/**
* Filter layers based on matching with WMS links or given layername
* Primary matching logic:
* Direct match by requested layer name (fuzzy match)
* Fallback matching logic:
* 1. Primary: link.title matches layer.name OR layer.title (fuzzy match)
* 2. Fallback: extract layername from link URI, then layername matches layer.name OR layer.title (fuzzy match)
*
* @param collectionId - Collection uuid
* @param layers - List of layers to filter
* @return Filtered list of WMS layers that have matching metadata WMS links
*/
protected List<LayerInfo> filterLayers(String collectionId, FeatureRequest request, List<LayerInfo> layers) {
// Direct match by requested layer name
if (request != null && request.getLayerName() != null && !request.getLayerName().isEmpty()) {
log.debug("=== Starting to match {} layers by layername {} ===", layers.size(), request.getLayerName());
String requestLayerName = request.getLayerName();
return layers.stream()
.filter(layer ->
roughlyMatch(requestLayerName, layer.getName()) ||
roughlyMatch(requestLayerName, layer.getTitle()))
.toList();
}

// Fallback to matching by WMS links
List<LinkModel> wmsLinks = getWmsLinks(collectionId);
List<LayerInfo> filteredLayers = new ArrayList<>();

log.debug("=== Starting to match {} layers ===", layers.size());
log.debug("=== Starting to match {} layers by WMS links ===", layers.size());
for (LayerInfo layer : layers) {
boolean matched = false;

Expand Down Expand Up @@ -723,6 +744,38 @@
return filteredLayers;
}

/**
* Fetch NCWMS Metadata from call GetMetadata in WMS server NCWMS service
*
* @param mapServerUrl - WMS server url
* @param layername - NCWMS layername
* @return NCWMS metadata that could be attached to layers
*/
protected Optional<NcWmsLayerInfo> fetchNCWMSMetadata(String mapServerUrl, String layername) {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(mapServerUrl);
builder.scheme("https"); // Force https

wmsDefaultParam.getNcmetadata().forEach((key, value) -> {
if (value != null) {
builder.queryParam(key, value);
}
});
builder.queryParam("layerName", layername);

ResponseEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, pretendUserEntity, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
try {
return Optional.of(objectMapper.readValue(response.getBody(), NcWmsLayerInfo.class));
} catch (JsonProcessingException e) {
// Save to ignore
}
} else {
return Optional.empty();
}

return Optional.empty();
}

/**
* Get filtered layers from WMS GetCapabilities for a specific collection
* First fetches all layers (cached by URL), then filters by WMS links
Expand All @@ -732,50 +785,57 @@
* we can use xxx as the workspace name and rewrite the URL to https://www.cmar.csiro.au/geoserver/xxx/wms
*
* @param collectionId - The uuid
* @param request - The request param
* @return - List of LayerInfo objects filtered by WFS link matching
* @param request - The request param (can be null)
* @return - List of LayerInfo objects filtered by WMS link matching
*/
public List<LayerInfo> getCapabilitiesLayers(String collectionId, FeatureRequest request) {
Optional<String> mapServerUrl = getMapServerUrl(collectionId, null);
Optional<String> mapServerUrl = getMapServerUrl(collectionId, request);

if (mapServerUrl.isPresent()) {
// Fetch all layers from GetCapabilities (this call is cached by URL)
// Special rewrite to speed up query
String url = rewriteUrlWithWorkSpace(mapServerUrl.get(), request);
List<LayerInfo> allLayers = self.fetchCapabilitiesLayersByUrl(url);

if (!allLayers.isEmpty()) {
// Filter layers based on WMS link matching
List<LayerInfo> filteredLayers = filterLayersByWmsLinks(collectionId, allLayers);

// Special case for NCWMS layer where we need to call GetMetadata to find the related points for gridded data
if (mapServerUrl.get().contains("/ncwms")) {
filteredLayers.forEach(layer -> {
// For each ncwms layer, we attach the metadata
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(mapServerUrl.get());
builder.scheme("https"); // Force https

wmsDefaultParam.getNcmetadata().forEach((key, value) -> {
if (value != null) {
builder.queryParam(key, value);
}
});
builder.queryParam("layerName", layer.getName());

ResponseEntity<String> response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, pretendUserEntity, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
try {
layer.setNcWmsLayerInfo(objectMapper.readValue(response.getBody(), NcWmsLayerInfo.class));
} catch (JsonProcessingException e) {
// Save to ignore
List<LayerInfo> filteredLayers = filterLayers(collectionId, request, allLayers);

// Special case for NCWMS layer where we need to call GetMetadata to find the related points for gridded data
if (mapServerUrl.get().contains("/ncwms")) {
if (filteredLayers.isEmpty()) {
// Most of the time the NCWMS layer does not match any WMS capability layers,
// we trust the NCWMS layernames from the metadata and try fetching NCWMS metadata for each
List<LinkModel> wmsLinks = getWmsLinks(collectionId);
for (LinkModel wmsLink : wmsLinks) {
Optional<String> layerName = extractLayernameOrTypenameFromLink(wmsLink);

if (layerName.isPresent()) {
Optional<NcWmsLayerInfo> ncWmsLayerInfo = fetchNCWMSMetadata(mapServerUrl.get(), layerName.get());

// Build LayerInfo with ncWmsLayerInfo
if (ncWmsLayerInfo.isPresent()) {
filteredLayers = List.of(
LayerInfo.builder()
.name(layerName.get())
.title(layerName.get())
.queryable("0")
.ncWmsLayerInfo(ncWmsLayerInfo.get())
.build()
);
}
}
});
}
}
log.debug("Returning layers {}", filteredLayers);

return filteredLayers;
filteredLayers.forEach(layer -> {
// For each ncwms layer, we attach the metadata
Optional<NcWmsLayerInfo> ncWmsLayerInfo = fetchNCWMSMetadata(mapServerUrl.get(), layer.getName());
ncWmsLayerInfo.ifPresent(layer::setNcWmsLayerInfo);
});
}

log.debug("Returning layers {}", filteredLayers);

return filteredLayers;
}

return Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package au.org.aodn.ogcapi.server.core.util;

import au.org.aodn.ogcapi.server.core.model.LinkModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;
Expand Down Expand Up @@ -82,5 +83,29 @@ public static Optional<String> extractLayernameOrTypenameFromUrl(String url) {
return Optional.empty();
}

/**
* Extract layer name or typename from a LinkModel.
* First tries link.title, then falls back to extracting from link.href URL query parameters.
*
* @param link - The LinkModel object
* @return layer name/typename if found, empty otherwise
*/
public static Optional<String> extractLayernameOrTypenameFromLink(LinkModel link) {
if (link == null) {
return Optional.empty();
}

// Try to get layer name from link title first
if (link.getTitle() != null && !link.getTitle().isEmpty()) {
return Optional.of(link.getTitle());
}

// Fallback: extract layer name from URL query parameters
if (link.getHref() != null) {
return extractLayernameOrTypenameFromUrl(link.getHref());
}

return Optional.empty();
}

}
Loading