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 @@ -7,6 +7,11 @@
@Data
@Builder
public class DownloadableFieldModel {
public enum Dimension {
range,
single
}

@JsonProperty("label")
private String label;

Expand All @@ -15,4 +20,8 @@ public class DownloadableFieldModel {

@JsonProperty("name")
private String name;
// Indicate if this a single time point or range?
@Builder.Default
@JsonProperty("view_dimension")
private Dimension viewDimension = Dimension.range;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import java.math.BigDecimal;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -114,8 +115,8 @@ else if(wfsUrlComponents.getQueryParams().get("CQL_FILTER") != null) {
if (range.size() == 2) {
// Due to no standard name, we try our best to guess if 2 dateTime field, range mean we found start/end date
String[] d = request.getDatetime().split("/");
String guess1 = target.get(0).getName();
String guess2 = target.get(1).getName();
String guess1 = range.get(0).getName();
String guess2 = range.get(1).getName();

if ((guess1.contains("start") || guess1.contains("min")) && (guess2.contains("end") || guess2.contains("max"))) {
String timeCql = String.format("CQL_FILTER=%s >= %s AND %s <= %s", guess1, d[0], guess2, d[1]);
Expand Down Expand Up @@ -174,6 +175,8 @@ protected List<String> createMapQueryUrl(String url, String uuid, FeatureRequest
param.putAll(wmsDefaultParam.getWms());
} else if (pathSegments.get(pathSegments.size() - 1).equalsIgnoreCase("ncwms")) {
param.putAll(wmsDefaultParam.getNcwms());
// Specific for ncWMS, check comment below related to CQL_FILTER
param.put("TIME", request.getDatetime());
}

// Now we add the missing argument from the request
Expand Down Expand Up @@ -215,12 +218,23 @@ protected List<String> createMapQueryUrl(String url, String uuid, FeatureRequest
builder.queryParam(key, value);
}
});
// Cannot set cql in param as it contains value like "/" which is not allow in UriComponent checks
// but server must use "/" in param and cannot encode it to %2F, so to avoid exception in the
// build() call, we append the cql after the construction.
String target = String.join("&", builder.build().toUriString(), createCQLFilter(uuid, request));
log.debug("Url to wms geoserver {}", target);
urls.add(target);
if (pathSegments.get(pathSegments.size() - 1).equalsIgnoreCase("wms")) {
// No, ncWMS (including GeoServer extension) does not support CQL_FILTER.
// It focuses on NetCDF gridded data with parameters like TIME, ELEVATION, COLORSCALERANGE,
// STYLES (palettes), NUMCOLORBANDS. CQL_FILTER is a GeoServer vendor parameter for vector
// filtering, not implemented in ncWMS. So we only add CQL if it is WMS

// Cannot set cql in param as it contains value like "/" which is not allow in UriComponent checks
// but server must use "/" in param and cannot encode it to %2F, so to avoid exception in the
// build() call, we append the cql after the construction.
String target = String.join("&", builder.build().toUriString(), createCQLFilter(uuid, request));
log.debug("Url to wms geoserver {}", target);
urls.add(target);
} else if (pathSegments.get(pathSegments.size() - 1).equalsIgnoreCase("ncwms")) {
String target = builder.build().toUriString();
log.debug("Url to ncWms geoserver {}", target);
urls.add(target);
}

return urls;
}
Expand Down Expand Up @@ -419,7 +433,14 @@ protected Optional<String> getMapServerUrl(String collectionId, FeatureRequest r
return Optional.empty();
}
}

/**
* Fetch the popup content when use click on the map, so it is kind of features associated with the map
* @param collectionId - The uuid
* @param request - The request object for the query
* @return - Usually an HTML or JSON from the server, not a good type of return type but this is from legacy system
* @throws JsonProcessingException - Not expected
* @throws URISyntaxException - Not expected
*/
public FeatureInfoResponse getMapFeatures(String collectionId, FeatureRequest request) throws JsonProcessingException, URISyntaxException {

Optional<String> mapServerUrl = getMapServerUrl(collectionId, request);
Expand Down Expand Up @@ -486,15 +507,20 @@ public DescribeLayerResponse describeLayer(String collectionId, FeatureRequest r
@Cacheable(value = CACHE_WMS_MAP_TILE)
public byte[] getMapTile(String collectionId, FeatureRequest request) throws URISyntaxException {
Optional<String> mapServerUrl = getMapServerUrl(collectionId, request);
log.debug("map tile request for uuid {} layername {}", collectionId, request.getLayerName());
if (mapServerUrl.isPresent()) {
List<String> urls = createMapQueryUrl(mapServerUrl.get(), collectionId, request);
// Try one by one, we exit when any works
for (String url : urls) {
log.debug("map tile request for layer name {} url {} ", request.getLayerName(), url);
ResponseEntity<byte[]> response = restTemplateUtils.handleRedirect(url, restTemplate.exchange(url, HttpMethod.GET, pretendUserEntity, byte[].class), byte[].class, pretendUserEntity);
if (response.getStatusCode().is2xxSuccessful()) {
return response.getBody();
if (response.getHeaders().getContentType() != null && response.getHeaders().getContentType().getType().equals("image")) {
return response.getBody();
}
else {
// Something wrong from the server likely syntax error
throw new URISyntaxException(response.getBody() != null ? new String(response.getBody(), StandardCharsets.UTF_8) : "", url);
}
}
}
}
Expand All @@ -509,15 +535,47 @@ public byte[] getMapTile(String collectionId, FeatureRequest request) throws URI
public List<DownloadableFieldModel> getDownloadableFields(String collectionId, FeatureRequest request) {

DescribeLayerResponse response = this.describeLayer(collectionId, request);
List<DownloadableFieldModel> result;

if (response != null && response.getLayerDescription().getWfs() != null) {
// If we are able to find the wfs server and real layer name based on wms layer, then use it
FeatureRequest modified = FeatureRequest.builder().layerName(response.getLayerDescription().getQuery().getTypeName()).build();
return wfsServer.getDownloadableFields(collectionId, modified, response.getLayerDescription().getWfs());
result = wfsServer.getDownloadableFields(collectionId, modified, response.getLayerDescription().getWfs());
} else {
// We trust what is found inside the elastic search metadata
return wfsServer.getDownloadableFields(collectionId, request, null);
result = wfsServer.getDownloadableFields(collectionId, request, null);
}

Optional<String> wmsUrl = getMapServerUrl(collectionId, request);
if (wmsUrl.isPresent() && wmsUrl.get().contains("/ncwms")) {
// Special case for ncWMS where it is a gridded data and support TIME param, not CQL_FILTERS which only works
// for wms, we need to test if this TIME field is a single time=2012-01-01 .. or support range
// time=2012-01-01/2022-01-01, the only way to do it now is to issue a query and test it.
// This is likely the only case for imos custom ncwms server
FeatureRequest test = FeatureRequest.builder()
.layerName(request.getLayerName())
.bbox(List.of(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.ONE))
.datetime("2020-01-01/2020-01-03") // Date value not important, with "/" indicate range
.build();
try {
// Post this to ncwms, if this is not a range TIME, then it will
self.getMapTile(collectionId, test);
// Mark the time field dimension to RANGE
result.stream()
.filter(f -> f.getName().equalsIgnoreCase("time"))
.findFirst()
.ifPresent(a -> a.setViewDimension(DownloadableFieldModel.Dimension.range));
}
catch(Exception uriSyntaxException) {
// Not support range
result.stream()
.filter(f -> f.getName().equalsIgnoreCase("time"))
.findFirst()
.ifPresent(a -> a.setViewDimension(DownloadableFieldModel.Dimension.single));
}
}

return result;
}
/**
* Fetch raw layers from WMS GetCapabilities - cached by URL, that is query all layer supported by this WMS server.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public ResponseEntity<?> getFeature(
case wms_downloadable_fields -> {
return (request.getLayerName() == null || request.getLayerName().isEmpty()) ?
ResponseEntity.badRequest().build() :
featuresService.getWmsDownloadableFields(collectionId, request);
featuresService.getWmsFieldsProperties(collectionId, request);
}
case wms_map_tile -> {
return featuresService.getWmsMapTile(collectionId, request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public ResponseEntity<FeatureInfoResponse> getWmsMapFeature(String collectionId,
public ResponseEntity<byte[]> getWmsMapTile(String collectionId, FeatureRequest request) {
try {
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
.body(wmsServer.getMapTile(collectionId, request));
} catch (Throwable e) {
throw new RuntimeException(e);
Expand All @@ -103,15 +104,14 @@ public ResponseEntity<?> getWfsDownloadableFields(String collectionId, FeatureRe
ResponseEntity.notFound().build() :
ResponseEntity.ok(result);
}

/**
* This is used to get the downloadable fields from wfs given a wms layer
* This is used to get the downloadable fields / viewable fields given a wms layer
*
* @param collectionId - The uuid of dataset
* @param request - Request to get field given a WMS layer name
* @return - The downloadable field name, or UNAUTHORIZED if it is not in white list
*/
public ResponseEntity<?> getWmsDownloadableFields(String collectionId, FeatureRequest request) {
public ResponseEntity<?> getWmsFieldsProperties(String collectionId, FeatureRequest request) {

if (request.getLayerName() == null || request.getLayerName().isEmpty()) {
log.info("Layer name cannot be null or empty");
Expand Down
1 change: 1 addition & 0 deletions server/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ wms-default-param:
STYLES: ""
QUERYABLE: "true"
CRS: "EPSG:3857"
NUMCOLORBANDS: "253"
WIDTH: 256
HEIGHT: 256
allow-id: # Full list
Expand Down
Loading
Loading