/**
* 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, The Open Planning Project, Copyright 2008
*/
package org.geowebcache.service.ve;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geowebcache.GeoWebCacheException;
import org.geowebcache.conveyor.ConveyorTile;
import org.geowebcache.grid.GridSetBroker;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.layer.TileLayerDispatcher;
import org.geowebcache.mime.MimeException;
import org.geowebcache.mime.MimeType;
import org.geowebcache.service.Service;
import org.geowebcache.service.ServiceException;
import org.geowebcache.storage.StorageBroker;
import org.geowebcache.util.ServletUtils;
/**
* Class to convert from Virtual Earth quad keys to the internal representation
* of a tile.
*/
public class VEConverter extends Service {
public static final String SERVICE_VE = "ve";
private static Log log = LogFactory.getLog(org.geowebcache.service.ve.VEConverter.class);
private StorageBroker sb;
private TileLayerDispatcher tld;
private GridSetBroker gsb;
/**
* Protected no-argument constructor to allow run-time instrumentation
*/
protected VEConverter() {
super(SERVICE_VE);
}
public VEConverter(StorageBroker sb, TileLayerDispatcher tld, GridSetBroker gsb) {
super(SERVICE_VE);
this.sb = sb;
this.tld = tld;
this.gsb = gsb;
}
public ConveyorTile getConveyor(HttpServletRequest request, HttpServletResponse response)
throws ServiceException {
Map<String,String[]> params = request.getParameterMap();
String layerId = super.getLayersParameter(request);
String encoding = request.getCharacterEncoding();
String strQuadKey = ServletUtils.stringFromMap(params, encoding, "quadkey");
String strFormat = ServletUtils.stringFromMap(params, encoding, "format");
String strCached = ServletUtils.stringFromMap(params, encoding, "cached");
String strMetaTiled = ServletUtils.stringFromMap(params, encoding, "metatiled");
long[] gridLoc = VEConverter.convert(strQuadKey);
MimeType mimeType = null;
if(strFormat != null) {
try {
mimeType = MimeType.createFromFormat(strFormat);
} catch (MimeException me) {
throw new ServiceException("Unable to determined requested format, " + strFormat);
}
}
ConveyorTile ret = new ConveyorTile(sb, layerId, gsb.WORLD_EPSG3857.getName(), gridLoc, mimeType, null, request, response);
if(strCached != null && ! Boolean.parseBoolean(strCached)) {
ret.setRequestHandler(ConveyorTile.RequestHandler.SERVICE);
if(strMetaTiled != null && ! Boolean.parseBoolean(strMetaTiled)) {
ret.setHint("not_cached,not_metatiled");
} else {
ret.setHint("not_cached");
}
}
return ret;
}
/**
* NB The following code is shared across Google Maps, Mobile Google Maps and Virtual Earth
*/
public void handleRequest(ConveyorTile tile)
throws GeoWebCacheException {
if (tile.getHint() != null) {
//boolean requestTiled = true;
if (tile.getHint().equals("not_cached,not_metatiled")) {
//requestTiled = false;
} else if (!tile.getHint().equals("not_cached")) {
throw new GeoWebCacheException("Hint " + tile.getHint() + " is not known.");
}
TileLayer tl = tld.getTileLayer(tile.getLayerId());
if (tl == null) {
throw new GeoWebCacheException("Unknown layer " + tile.getLayerId());
}
if(! tl.isCacheBypassAllowed().booleanValue()) {
throw new GeoWebCacheException("Layer " + tile.getLayerId()
+ " is not configured to allow bypassing the cache.");
}
tile.setTileLayer(tl);
tl.getNoncachedTile(tile);
Service.writeTileResponse(tile, false);
}
}
/**
* Convert a quadkey into the internal representation {x,y,z} of a grid
* location
*
* @param quadKey
* @return internal representation
*/
public static long[] convert(String strQuadKey) {
char[] quadArray = strQuadKey.toCharArray();
long zoomLevel = quadArray.length;
long extent = (long) Math.pow(2, zoomLevel);
long yPos = 0;
long xPos = 0;
// Now we traverse the quadArray from left to right, interpretation
// 0 1
// 2 3
// see http://msdn2.microsoft.com/en-us/library/bb259689.aspx
//
// What we'll end up with is the top left hand corner of the bbox
//
for (int i = 0; i < zoomLevel; i++) {
char curChar = quadArray[i];
// For each round half as much is at stake
extent = extent / 2;
if (curChar == '0') {
// X stays
yPos += extent;
} else if (curChar == '1') {
xPos += extent;
yPos += extent;
} else if (curChar == '2') {
// X stays
// Y stays
} else if (curChar == '3') {
xPos += extent;
// Y stays
} else {
log.error("Don't know how to interpret quadKey: " + strQuadKey);
return null;
}
}
long[] gridLoc = { xPos, yPos, zoomLevel };
return gridLoc;
}
}