Skip to content

Commit

Permalink
skip GetFeatureInfo query link when not supported formats
Browse files Browse the repository at this point in the history
  • Loading branch information
dromagnoli committed Sep 27, 2024
1 parent db367f3 commit 2ce617f
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1641,21 +1641,6 @@ private void createMinAndMaxWidthHeight() {
* Generate inputs and links that the client will use to generate WMTS GetFeatureInfo requests
*/
private void generateWMTSQueryClientLinks(MapMLLayerMetadata mapMLLayerMetadata) {
// query i value (x)
Input input = new Input();
input.setName("i");
input.setType(InputType.LOCATION);
input.setUnits(UnitType.TILE);
input.setAxis(AxisType.I);
extentList.add(input);

// query j value (y)
input = new Input();
input.setName("j");
input.setType(InputType.LOCATION);
input.setUnits(UnitType.TILE);
input.setAxis(AxisType.J);
extentList.add(input);

// query link
Link queryLink = new Link();
Expand All @@ -1680,8 +1665,29 @@ private void generateWMTSQueryClientLinks(MapMLLayerMetadata mapMLLayerMetadata)
new MapMLRequestMangler(
mapContent, mapMLLayerMetadata, baseUrlPattern, path, params, proj);
String urlTemplate = mangler.getUrlTemplate();
queryLink.setTref(urlTemplate);
extentList.add(queryLink);
// It may be that the mangler decided to not generate any query URL due
// to unsupported info formats from the remote layer. So we are not
// generating the query link.
if (urlTemplate != null) {
// query i value (x)
Input input = new Input();
input.setName("i");
input.setType(InputType.LOCATION);
input.setUnits(UnitType.TILE);
input.setAxis(AxisType.I);
extentList.add(input);

// query j value (y)
input = new Input();
input.setName("j");
input.setType(InputType.LOCATION);
input.setUnits(UnitType.TILE);
input.setAxis(AxisType.J);
extentList.add(input);

queryLink.setTref(urlTemplate);
extentList.add(queryLink);
}
}

/** Generate inputs and links the client will use to create WMS GetFeatureInfo requests */
Expand All @@ -1690,21 +1696,6 @@ private void generateWMSQueryClientLinks(MapMLLayerMetadata mapMLLayerMetadata)
if (mapMLLayerMetadata.isUseTiles()) {
units = UnitType.TILE;
}
// query i value (x)
Input input = new Input();
input.setName("i");
input.setType(InputType.LOCATION);
input.setUnits(units);
input.setAxis(AxisType.I);
extentList.add(input);

// query j value (y)
input = new Input();
input.setName("j");
input.setType(InputType.LOCATION);
input.setUnits(units);
input.setAxis(AxisType.J);
extentList.add(input);

// query link
Link queryLink = new Link();
Expand Down Expand Up @@ -1744,8 +1735,29 @@ private void generateWMSQueryClientLinks(MapMLLayerMetadata mapMLLayerMetadata)
new MapMLRequestMangler(
mapContent, mapMLLayerMetadata, baseUrlPattern, path, params, proj);
String urlTemplate = mangler.getUrlTemplate();
queryLink.setTref(urlTemplate);
extentList.add(queryLink);
// It may be that the mangler decided to not generate any query URL due
// to unsupported info formats from the remote layer. So we are not
// generating the query link.
if (urlTemplate != null) {
// query i value (x)
Input input = new Input();
input.setName("i");
input.setType(InputType.LOCATION);
input.setUnits(units);
input.setAxis(AxisType.I);
extentList.add(input);

// query j value (y)
input = new Input();
input.setName("j");
input.setType(InputType.LOCATION);
input.setUnits(units);
input.setAxis(AxisType.J);
extentList.add(input);

queryLink.setTref(urlTemplate);
extentList.add(queryLink);
}
}

private void setCqlFilterParam(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -38,6 +40,7 @@
import org.geoserver.wms.WMSMapContent;
import org.geotools.api.filter.Filter;
import org.geotools.api.referencing.FactoryException;
import org.geotools.data.ows.OperationType;
import org.geotools.ows.wms.Layer;
import org.geotools.ows.wms.WMSCapabilities;
import org.geotools.ows.wmts.model.TileMatrix;
Expand Down Expand Up @@ -75,6 +78,9 @@ public class MapMLRequestMangler {
private static final double ORIGIN_DELTA = 0.1;
private static final double SCALE_DELTA = 1E-5;

private static final List<String> GET_FEATURE_INFO_FORMATS =
Arrays.asList("text/mapml", "text/html", "text/plain");

static class CRSMapper {

Set<String> inputCRSs;
Expand Down Expand Up @@ -162,7 +168,7 @@ public String getUrlTemplate() {
baseUrlPattern, path, params, URLMangler.URLType.SERVICE),
"UTF-8");
} else {
urlTemplate = tryCascading(path, params, layerInfo);
urlTemplate = generateURL(path, params, layerInfo);
}
} catch (UnsupportedEncodingException uee) {
}
Expand Down Expand Up @@ -202,16 +208,20 @@ private boolean canCascade(LayerInfo layerInfo) {
return false;
}
/**
* Try cascading to the remote Server unless any condition is preventing that (i.e. CRS not
* supported on the remote server)
* Try cascading to the remote Server and generate the cascaded URL. If cascading cannot be
* performed (i.e. CRS not supported on the remote server) a local URL will be generated. If the
* URL should not be generated at all (i.e. requesting a GetFeatureInfo to a remote that is not
* supporting text/plain, text/html, mapml) null will be returned and the document builder won't
* add the URL (i.e. the query link).
*/
private String tryCascading(String path, HashMap<String, String> params, LayerInfo layerInfo)
private String generateURL(String path, HashMap<String, String> params, LayerInfo layerInfo)
throws UnsupportedEncodingException {
String baseUrl = baseUrlPattern;
String version = "1.3.0";
String reason = null;
boolean doCascade = false;
URLMangler.URLType urlType = URLMangler.URLType.SERVICE;
List<String> infoFormats = new ArrayList<>();
if (layerInfo != null) {
ResourceInfo resourceInfo = layerInfo.getResource();
String layerName = resourceInfo.getNativeName();
Expand Down Expand Up @@ -239,20 +249,31 @@ private String tryCascading(String path, HashMap<String, String> params, LayerIn
WMSCapabilities capabilities =
wmsStoreInfo.getWebMapServer(null).getCapabilities();
version = capabilities.getVersion();

if (!WMS_1_3_0.equals(version)) {
version = "1.1.1";
}
List<Layer> layerList = capabilities.getLayerList();
boolean isSupportedCrs = false;
for (Layer layer : layerList) {
if (layerName.equals(layer.getName())) {
isSupportedCrs = isSRSInLayerOrParents(layer, requestedCRS);
break;
if (checkLayers(
capabilities.getRequest().getGetFeatureInfo(),
params,
infoFormats)) {
if (!WMS_1_3_0.equals(version)) {
version = "1.1.1";
}
List<Layer> layerList = capabilities.getLayerList();
boolean isSupportedCrs = false;
for (Layer layer : layerList) {
if (layerName.equals(layer.getName())) {
isSupportedCrs = isSRSInLayerOrParents(layer, requestedCRS);
break;
}
}
isSupportedCrs &= (outputCRS != null);
doCascade = isSupportedCrs;
} else {
if ("GetFeatureInfo".equalsIgnoreCase(params.get("request"))
&& infoFormats.isEmpty()) {
LOGGER.fine(
"URL won't be generated due to Requesting a not supported GetFeatureInfo format");
return null;
}
}
isSupportedCrs &= (outputCRS != null);
doCascade = isSupportedCrs;
} catch (IOException e) {
reason =
"Unable to extract the WMS remote capabilities. Cascading won't be performed";
Expand Down Expand Up @@ -293,7 +314,8 @@ private String tryCascading(String path, HashMap<String, String> params, LayerIn
baseUrl = baseUrlAndPath[0];
path = baseUrlAndPath[1];
urlType = URLMangler.URLType.EXTERNAL;
refineRequestParams(params, layerName, version, requestedCRS, tileMatrixSet);
refineRequestParams(
params, layerName, version, requestedCRS, tileMatrixSet, infoFormats);
} else {
LOGGER.fine("Cascading won't be performed, due to: " + reason);
}
Expand All @@ -305,6 +327,20 @@ private String tryCascading(String path, HashMap<String, String> params, LayerIn
return urlTemplate;
}

private boolean checkLayers(
OperationType getFeatureInfo,
HashMap<String, String> params,
List<String> featureInfoFormats) {
if ("GetFeatureInfo".equalsIgnoreCase(params.get("request"))) {
featureInfoFormats.addAll(getFeatureInfo.getFormats());
featureInfoFormats.retainAll(GET_FEATURE_INFO_FORMATS);
if (featureInfoFormats.isEmpty()) {
return false;
}
}
return true;
}

private void cleanupCRS(HashMap<String, String> params, String version, String requestedCRS) {
boolean cleanupCrs = params.containsKey(CRS_PARAM) || params.containsKey(SRS_PARAM);
if (cleanupCrs) {
Expand All @@ -320,7 +356,8 @@ private void refineRequestParams(
String layerName,
String version,
String requestedCRS,
String tileMatrixSetName) {
String tileMatrixSetName,
List<String> infoFormats) {
String requestType = params.get(REQUEST);
String service = params.get(SERVICE);
if (params.containsKey(LAYER)) {
Expand All @@ -333,11 +370,7 @@ private void refineRequestParams(
if (params.containsKey("query_layers")) {
params.put("query_layers", layerName);
}
if (params.containsKey("info_format")) {
params.put("info_format", "text/html");
} else if (params.containsKey("infoformat")) {
params.put("infoformat", "text/html");
}
refineInfoFormat(params, infoFormats);
cleanupCRS(params, version, requestedCRS);
}
// Extra settings for WMTS
Expand All @@ -354,6 +387,34 @@ private void refineRequestParams(
}
}

private void refineInfoFormat(HashMap<String, String> params, List<String> infoFormats) {
// When entering this method, infoFormats cannot be empty.

String paramName = "info_format";
String infoFormat = params.get(paramName);
if (infoFormat == null) {
paramName = "infoformat";
infoFormat = params.get(paramName);
}
if (infoFormat != null) {
// replace the infoFormat with a supported one
infoFormat = updateInfoFormat(infoFormat, infoFormats);
params.put(paramName, infoFormat);
}
}

private String updateInfoFormat(String infoFormat, List<String> infoFormats) {
if (!infoFormats.contains(infoFormat)) {
// Fall back on text/html
if (infoFormats.contains("text/html") || infoFormats.isEmpty()) {
infoFormat = "text/html";
} else {
infoFormat = infoFormats.get(0);
}
}
return infoFormat;
}

@SuppressWarnings("PMD.UseCollectionIsEmpty")
private boolean hasVendorParams() {
GetMapRequest req = mapContent.getRequest();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* (c) 2024 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.mapml;

import static org.junit.Assert.assertTrue;

import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.WMSLayerInfo;
import org.geoserver.catalog.WMSStoreInfo;
import org.geoserver.data.test.SystemTestData;
import org.junit.BeforeClass;
import org.junit.Test;
import org.w3c.dom.Document;

public class MapMLWMSGetFeatureInfoProxyTest extends MapMLBaseProxyTest {

@BeforeClass
public static void beforeClass() {
initMockService(
"/mockgeoserver",
"/wms",
"REQUEST=GetCapabilities&VERSION=1.3.0&SERVICE=WMS",
"wmscapsgmlfeatureinfo.xml");
}

@Override
protected void onSetUp(SystemTestData testData) throws Exception {
super.onSetUp(testData);
Catalog catalog = getCatalog();

WMSStoreInfo wmsStore = catalog.getFactory().createWebMapServer();
wmsStore.setName("wmsStore");
wmsStore.setWorkspace(catalog.getDefaultWorkspace());
wmsStore.setCapabilitiesURL(
"http://localhost:" + mockService.port() + getCapabilitiesURL());
wmsStore.setEnabled(true);
catalog.add(wmsStore);

// Create WMSLayerInfo using the Catalog factory
WMSLayerInfo wmsLayer = catalog.getFactory().createWMSLayer();
wmsLayer.setName("cascadedLayer");
wmsLayer.setNativeName("topp:states");
wmsLayer.setStore(wmsStore);
wmsLayer.setAdvertised(true);
wmsLayer.setEnabled(true);

// Add the layer to the catalog
LayerInfo layer = catalog.getFactory().createLayer();
layer.setResource(wmsLayer);
layer.setDefaultStyle(catalog.getStyleByName("default"));
catalog.add(wmsLayer);
catalog.add(layer);
}

@Test
public void testMapMLLocalQueryLink() throws Exception {
Catalog cat = getCatalog();
// Verify the layer was added
LayerInfo layerInfo = cat.getLayerByName("cascadedLayer");
ResourceInfo layerMeta = layerInfo.getResource();
layerMeta.getMetadata().put("mapml.useRemote", false);
cat.save(layerMeta);
String path = BASE_REQUEST;
Document doc = getMapML(path);

String url = xpath.evaluate("//html:map-link[@rel='query']/@tref", doc);
assertTrue(url.startsWith("http://localhost:8080/geoserver" + CONTEXT));
assertTrue(url.contains("layers=cascadedLayer"));
assertTrue(url.contains("request=GetFeatureInfo"));

layerMeta.getMetadata().put("mapml.useRemote", true);
cat.save(layerMeta);

// The sample getCapabilities is only returning GML as supported
// Formats for the GetFeatureInfo so the query link will not be generated
// when cascading
doc = getMapML(path);
url = xpath.evaluate("//html:map-link[@rel='query']/@tref", doc);
assertTrue(url.isEmpty());
}
}
Loading

0 comments on commit 2ce617f

Please sign in to comment.