Skip to content

Commit

Permalink
[GEOT 6895] - Restrict the responsibilities of WMTSTileService - #2 (g…
Browse files Browse the repository at this point in the history
…eotools#3566)

* [GEOT-6895] - Restrict the responsibilities of WMTSTileService

* Fixed PMD Diamond issue

* Fixed javadoc issues

* Fixed comments

* Url encoding tileMatrix
  • Loading branch information
roarbra authored Jul 5, 2021
1 parent 84240fd commit abc461c
Show file tree
Hide file tree
Showing 11 changed files with 658 additions and 245 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2021, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.ows.wmts;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;

/**
* Some useful functions that was used within gt-wmts.
*
* <p>Might have their substitution elsewhere
*
* @author Roar Brænden
*/
public class WMTSHelper {

/** Replaces first occurence of {dimName} with dimValue within the baseUrl */
public static String replaceToken(String baseUrl, String dimName, String dimValue) {
String token = "{" + dimName.toLowerCase() + "}";
int index = baseUrl.toLowerCase().indexOf(token);
if (index != -1) {
return baseUrl.substring(0, index)
+ (dimValue == null ? "" : dimValue)
+ baseUrl.substring(index + dimName.length() + 2);
} else {
return baseUrl;
}
}

/**
* Append the parameters to the baseUrl.
*
* <p>Add a ? mark if baseUrl doesn't have it.
*
* <p>Does not add existing parameters
*
* <p>If a value starts with {, it will avoid url-encoding
*/
public static String appendQueryString(String baseUrl, Map<String, String> params) {
StringBuilder arguments = new StringBuilder();
String separator = (!baseUrl.contains("?") ? "?" : "&");

String lowerBase = baseUrl.toLowerCase();
try {
for (String key : params.keySet()) {
if (!lowerBase.contains(key.toLowerCase() + "=")) {
Object val = params.get(key);
if (val != null) {
String valString = val.toString();
arguments
.append(separator)
.append(key)
.append("=")
.append(
valString.startsWith("{")
? valString
: URLEncoder.encode(valString, "UTF-8"));
separator = "&";
}
}
}
return baseUrl + arguments.toString();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Doesn't support UTF-8", e);
}
}

/**
* Fixes a problem when spaces within url's are replaced with +. In parts of the url we should
* instead use %20. A fix for GEOT-4317
*/
static String usePercentEncodingForSpace(String name) {
try {
return URLEncoder.encode(name, "UTF-8").replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Doesn't support UTF-8", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.geotools.data.ows.AbstractGetCapabilitiesRequest;
import org.geotools.data.ows.GetCapabilitiesRequest;
import org.geotools.data.ows.Response;
Expand All @@ -35,17 +39,20 @@
import org.geotools.ows.wmts.request.AbstractGetTileRequest;
import org.geotools.ows.wmts.response.GetTileResponse;
import org.geotools.ows.wmts.response.WMTSGetCapabilitiesResponse;
import org.geotools.util.logging.Logging;

/**
* WMTS version 1.0.0 specification.
*
* <p>Used to create GetCapabilities and GetTile requests.
*
* @author ian
* @author Emanuele Tajariol (etj at geo-solutions dot it)
*/
public class WMTSSpecification extends Specification {

public static final String WMTS_VERSION = "1.0.0";

private WMTSServiceType type;

/** */
public WMTSSpecification() {}

Expand All @@ -72,6 +79,12 @@ public GetTileRequest createGetTileRequest(

public static class GetTileRequest extends AbstractGetTileRequest {

private static Logger LOGGER = Logging.getLogger(GetTileRequest.class);

public static final String DIMENSION_TIME = "time";

public static final String DIMENSION_ELEVATION = "elevation";

/** */
public GetTileRequest(
URL onlineResource, Properties properties, WMTSCapabilities capabilities) {
Expand Down Expand Up @@ -116,6 +129,84 @@ public WMTSServiceType getType() {
public void setType(WMTSServiceType type) {
this.type = type;
}

@Override
protected String createTemplateUrl(String tileMatrixSetName) {

String baseUrl = getFinalURL().toExternalForm();

String layerString = WMTSHelper.usePercentEncodingForSpace(layer.getName());
String styleString =
WMTSHelper.usePercentEncodingForSpace(styleName == null ? "" : styleName);

String format = getFormat();

if (StringUtils.isEmpty(format)) {
if (!layer.getFormats().isEmpty()) {
format = layer.getFormats().get(0);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(
"Format is not set, available formats: "
+ layer.getFormats()
+ " -- Selecting "
+ format);
}
}
}

if (StringUtils.isEmpty(format)) {
format = "image/png";
if (LOGGER.isLoggable(Level.FINE))
LOGGER.fine("Format not set, trying with " + format);
}
setFormat(format);

switch (type) {
case KVP:
return WMTSHelper.appendQueryString(
baseUrl,
getKVPparams(layerString, styleString, tileMatrixSetName, format));
case REST:
return getRESTurl(baseUrl, layerString, styleString, tileMatrixSetName);
default:
throw new IllegalArgumentException("Unexpected WMTS Service type " + type);
}
}

private String getRESTurl(
String baseUrl, String layerString, String styleString, String tileMatrixSetName) {
baseUrl = WMTSHelper.replaceToken(baseUrl, "layer", layerString);
baseUrl = WMTSHelper.replaceToken(baseUrl, "style", styleString);
baseUrl = WMTSHelper.replaceToken(baseUrl, "tilematrixset", tileMatrixSetName);
return baseUrl;
}

/**
* Returns the properties for KVP WMTS, as well as placeholder's for the specific parameters
* of GetTile
*
* @param layerString
* @param styleString
* @param tileMatrixSetName
* @param format
* @return
*/
public static HashMap<String, String> getKVPparams(
String layerString, String styleString, String tileMatrixSetName, String format) {
HashMap<String, String> params = new HashMap<>();
params.put("service", "WMTS");
params.put("version", WMTS_VERSION);
params.put("request", "GetTile");
params.put("layer", layerString);
params.put("style", styleString);
params.put("format", format);
params.put("tilematrixset", tileMatrixSetName);
params.put("TileMatrix", "{TileMatrix}");
params.put("TileCol", "{TileCol}");
params.put("TileRow", "{TileRow}");

return params;
}
}

public static class GetCapsRequest extends AbstractGetCapabilitiesRequest {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2021, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.ows.wmts.client;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.logging.Logger;
import org.geotools.util.logging.Logging;

/**
* Convenience class that creates a URL from a templateURL by exchanging {TileMatrix}, {TileCol} and
* {TileRow}
*
* @author Roar Brænden
*/
class TileURLBuilder {

private static Logger LOGGER = Logging.getLogger(TileURLBuilder.class);

private static final String TILEMATRIX = "{tilematrix}";
private static final String TILECOL = "{tilecol}";
private static final String TILEROW = "{tilerow}";

private static final int MATRIX = 0;
private static final int COL = 1;
private static final int ROW = 2;

private int[] indexes = new int[3];

private ArrayList<Part> parts;
private int start = 0;
private String templateURL;

private int urlLength = 50;

TileURLBuilder(String templateURL) {
final String lowerTemplate = templateURL.toLowerCase();
indexes[MATRIX] = lowerTemplate.indexOf(TILEMATRIX);
indexes[COL] = lowerTemplate.indexOf(TILECOL);
indexes[ROW] = lowerTemplate.indexOf(TILEROW);

parts = new ArrayList<>(7);
this.templateURL = templateURL;
if (indexes[MATRIX] < indexes[COL] && indexes[MATRIX] < indexes[ROW]) {
addMatrixParts();
if (indexes[COL] < indexes[ROW]) {
addColParts();
addRowParts();
} else {
addRowParts();
addColParts();
}
} else if (indexes[COL] < indexes[ROW]) {
addColParts();

if (indexes[MATRIX] < indexes[ROW]) {
addMatrixParts();
addRowParts();
} else {
addRowParts();
addMatrixParts();
}
} else {
addRowParts();
if (indexes[MATRIX] < indexes[COL]) {
addMatrixParts();
addColParts();
} else {
addColParts();
addMatrixParts();
}
}

addStringPart(templateURL.length(), 0);
}

/**
* Create a Url with the given set of MatrixSet, TileCol and TileRow
*
* @param tileCol
* @param tileRow
* @param tileMatrix
* @return
*/
String createURL(String tileMatrix, int tileCol, int tileRow) {
final StringBuilder builder = new StringBuilder(urlLength);
try {
final String matrixEncoded = URLEncoder.encode(tileMatrix, "UTF-8");
parts.forEach((part) -> part.append(builder, matrixEncoded, tileCol, tileRow));
final String url = builder.toString();
urlLength = Math.max(urlLength, url.length());
return url;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Didn't support UTF-8", e);
}
}

private void addColParts() {
if (indexes[COL] == -1) {
LOGGER.info("WMTSTileService templateURL doesn't contain {TileCol}");
return;
}
addStringPart(indexes[COL], TILECOL.length());
parts.add((builder, matrix, col, row) -> builder.append(col));
}

private void addRowParts() {
if (indexes[ROW] == -1) {
LOGGER.info("WMTSTileService templateURL doesn't contain {TileRow}");
return;
}
addStringPart(indexes[ROW], TILEROW.length());
parts.add((builder, matrix, col, row) -> builder.append(row));
}

private void addMatrixParts() {
if (indexes[MATRIX] == -1) {
LOGGER.info("WMTSTileService templateURL doesn't contain {TileMatrix}");
return;
}
addStringPart(indexes[MATRIX], TILEMATRIX.length());
parts.add((builder, matrix, col, row) -> builder.append(matrix));
}

private void addStringPart(int end, int length) {
if (end < start) {
throw new IllegalArgumentException("end can't be smaller than start.");
}
if (start < end) {
final String part = templateURL.substring(start, end);
parts.add((builder, matrix, col, row) -> builder.append(part));
start = end + length;
}
}

static interface Part {
void append(StringBuilder builder, String tileMatrix, int tileCol, int tileRow);
}
}
Loading

0 comments on commit abc461c

Please sign in to comment.