/**
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Arne Kepp / OpenGeo, Copyright 2009
*/
package org.geowebcache.service.tms;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.geowebcache.grid.GridSet;
import org.geowebcache.grid.GridSetBroker;
import org.geowebcache.grid.GridSubset;
import org.geowebcache.io.XMLBuilder;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.layer.TileLayerDispatcher;
import org.geowebcache.layer.meta.LayerMetaInformation;
import org.geowebcache.mime.MimeType;
import org.geowebcache.util.URLMangler;
/**
* Basic implementation of the TMS documents. Not all of GWCs more advanced
* features can easily be accomodated by this service.
*
* The commented out sections are optional parts of the OSGeo standard
*/
public class TMSDocumentFactory {
protected TileLayerDispatcher tld;
protected GridSetBroker gsb;
private String defaultBaseUrl;
private String defaultContextPath;
protected URLMangler urlMangler;
Charset encoding;
public final static String TILEMAPSERVICE_LEADINGPATH = "tms/1.0.0";
public final static String SERVICE_PATH = "service/" + TILEMAPSERVICE_LEADINGPATH;
protected TMSDocumentFactory(TileLayerDispatcher tld, GridSetBroker gsb, URLMangler urlMangler,
String serviceName, Charset encoding) {
this.tld = tld;
this.gsb = gsb;
this.urlMangler = urlMangler;
this.encoding = encoding;
}
protected TMSDocumentFactory(TileLayerDispatcher tld, GridSetBroker gsb, String baseUrl,
String contextPath, URLMangler urlMangler) {
this(tld, gsb, baseUrl, contextPath, urlMangler, StandardCharsets.UTF_8);
}
protected TMSDocumentFactory(TileLayerDispatcher tld, GridSetBroker gsb, String baseUrl,
String contextPath, URLMangler urlMangler, Charset encoding) {
this.tld = tld;
this.gsb = gsb;
this.defaultBaseUrl = baseUrl;
this.defaultContextPath = contextPath;
this.urlMangler = urlMangler;
this.encoding = encoding;
}
protected TMSDocumentFactory(TileLayerDispatcher tld, GridSetBroker gsb, URLMangler urlMangler) {
this (tld, gsb, null, null, urlMangler);
}
protected String getTileMapServiceDoc() {
return getTileMapServiceDoc(defaultBaseUrl, defaultContextPath);
}
protected String getTileMapServiceDoc(String baseUrl, String contextPath) {
StringBuilder str = new StringBuilder();
XMLBuilder xml = new XMLBuilder(str);
try {
xml.header("1.0", encoding);
xml.indentElement("TileMapService")
.attribute("version", "1.0.0")
.attribute("services", urlMangler.buildURL(baseUrl, contextPath, ""));
// TODO can have these set through Spring
xml.simpleElement("Title", "Tile Map Service", true);
xml.simpleElement("Abstract", "A Tile Map Service served by GeoWebCache", true);
//TODO Optional stuff, note that there is some meta data stuff on the
// TileLayer object that we simply don't use yet
// <KeywordList>example tile service</KeywordList>
// <ContactInformation>
// <ContactPersonPrimary>
// <ContactPerson>Paul Ramsey</ContactPerson>
// <ContactOrganization>Refractions Research</ContactOrganization>
// </ContactPersonPrimary>
// <ContactPosition>Manager</ContactPosition>
// <ContactAddress>
// <AddressType>postal</AddressType>
// <Address>300 - 1207 Douglas Street</Address>
// <City>Victoria</City>
// <StateOrProvince>British Columbia</StateOrProvince>
// <PostCode>V8W2E7</PostCode>
// <Country>Canada</Country>
// </ContactAddress>
// <ContactVoiceTelephone>12503833022</ContactVoiceTelephone>
// <ContactFacsimileTelephone>12503832140</ContactFacsimileTelephone>
// <ContactElectronicMailAddress>pramsey@refractions.net</ContactElectronicMailAddress>
// </ContactInformation>
xml.indentElement("TileMaps");
Iterable<TileLayer> iter = tld.getLayerList();
for (TileLayer layer : iter) {
if(!layer.isEnabled() || !layer.isAdvertised()){
continue;
}
tileMapsForLayer(xml, layer, baseUrl, contextPath);
}
xml.endElement();
xml.endElement();
return str.toString();
} catch (IOException ex) {
// Should not happen
throw new IllegalStateException(ex);
}
}
protected void tileMapsForLayer(XMLBuilder xml, TileLayer layer,
String baseUrl, String contextPath) throws IOException {
for(String gridSetId : layer.getGridSubsets()){
GridSubset gridSub = layer.getGridSubset(gridSetId);
for(MimeType mimeType : layer.getMimeTypes()) {
// GridSubset gridSub = iter.next();
xml.indentElement("TileMap")
.attribute("title", tileMapTitle(layer))
.attribute("srs", gridSub.getSRS().toString())
.attribute("profile", profileForGridSet(gridSub.getGridSet()))
.attribute("href", tileMapUrl(layer, gridSub, mimeType, baseUrl, contextPath))
.endElement();
}
}
}
protected String getTileMapDoc(TileLayer layer, GridSubset gridSub, GridSetBroker gsb, MimeType mimeType) {
return getTileMapDoc(layer, gridSub, gsb, mimeType, defaultBaseUrl, defaultContextPath);
}
protected String getTileMapDoc(TileLayer layer, GridSubset gridSub, MimeType mimeType, String baseUrl,
String contextPath) {
return getTileMapDoc(layer, gridSub, gsb, mimeType, baseUrl, contextPath);
}
protected String getTileMapDoc(TileLayer layer, GridSubset gridSub, GridSetBroker gsb, MimeType mimeType,
String baseUrl, String contextPath) {
StringBuilder str = new StringBuilder();
XMLBuilder xml = new XMLBuilder(str);
try {
xml.header("1.0", encoding);
xml.indentElement("TileMap")
.attribute("version", "1.0.0")
.attribute("tilemapservice", urlMangler.buildURL(baseUrl, contextPath, SERVICE_PATH));
xml.simpleElement("Title", tileMapTitle(layer), true);
xml.simpleElement("Abstract", tileMapDescription(layer), true);
// <KeywordList></KeywordList>
// <Metadata type="TC211" mime-type="text/xml" href="http://www.org" />
// <Attribution>
// <Title>National Geospatial Intelligence Agency</Title>
// <Logo width="10" height="10" href="http://nga.mil/logo.gif" mime-type="image/gif" />
// </Attribution>
// <WebMapContext href="http://wms.org" />
// <Face>0</Face>
// Check with tschaub whether we actually have to provide this as OSGEO:40041
// No.
xml.simpleElement("SRS", gridSub.getSRS().toString(), true);
double[] coords = gridSub.getCoverageBestFitBounds().getCoords();
xml.boundingBox(null, coords[0], coords[1], coords[2], coords[3]);
xml.indentElement("Origin")
.attribute("x", Double.toString(coords[0]))
.attribute("y", Double.toString(coords[1]))
.endElement();
// Can we have multiple formats? NO
xml.indentElement("TileFormat")
.attribute("width", Integer.toString(gridSub.getTileWidth()))
.attribute("height", Integer.toString(gridSub.getTileHeight()))
.attribute("mime-type", mimeType.getMimeType())
.attribute("extension", mimeType.getFileExtension())
.endElement();
xml.indentElement("TileSets")
.attribute("profile", profileForGridSet(gridSub.getGridSet()));
double[] resolutions = gridSub.getResolutions();
int resIdx = 0;
for(int zoom = gridSub.getZoomStart(); zoom <= gridSub.getZoomStop(); zoom++) {
xml.indentElement("TileSet");
xml.attribute("href", tileMapUrl(layer, gridSub, mimeType, zoom, baseUrl, contextPath));
xml.attribute("units-per-pixel", Double.toString(resolutions[resIdx]));
xml.attribute("order", Integer.toString(resIdx));
xml.endElement();;
resIdx++;
}
xml.endElement();
xml.endElement();
return str.toString();
} catch (IOException ex) {
// Should not happen
throw new IllegalStateException(ex);
}
}
protected String profileForGridSet(GridSet gridSet) {
if(gridSet == gsb.WORLD_EPSG4326) {
return "global-geodetic";
} else if(gridSet == gsb.WORLD_EPSG3857) {
return "global-mercator";
} else {
return "local";
}
}
protected String tileMapUrl(TileLayer tl, GridSubset gridSub, MimeType mimeType,
String baseUrl, String contextPath) {
return urlMangler.buildURL(baseUrl, contextPath, SERVICE_PATH + "/" + tileMapName(tl,gridSub,mimeType));
}
protected String tileMapUrl(TileLayer tl, GridSubset gridSub, MimeType mimeType, int z,
String baseUrl, String contextPath) {
return tileMapUrl(tl, gridSub, mimeType, baseUrl, contextPath) + "/" + z;
}
protected String tileMapName(TileLayer tl, GridSubset gridSub, MimeType mimeType) {
try {
String name = URLEncoder.encode(tl.getName(), "UTF-8");
String gridSubset = URLEncoder.encode(gridSub.getName(), "UTF-8");
return name + "@" + gridSubset + "@" + mimeType.getFileExtension();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
protected String tileMapTitle(TileLayer tl) {
LayerMetaInformation metaInfo = tl.getMetaInformation();
if(metaInfo != null && metaInfo.getTitle() != null) {
return metaInfo.getTitle();
}
return tl.getName();
}
protected String tileMapDescription(TileLayer tl) {
LayerMetaInformation metaInfo = tl.getMetaInformation();
if(metaInfo != null && metaInfo.getDescription() != null) {
return metaInfo.getDescription();
}
return "";
}
}