package com.bbn.openmap.dataAccess.mapTile;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import com.bbn.openmap.dataAccess.mapTile.MapTileCoordinateTransform;
import com.bbn.openmap.dataAccess.mapTile.OSMMapTileCoordinateTransform;
import com.bbn.openmap.dataAccess.mapTile.ServerMapTileFactory;
import com.bbn.openmap.dataAccess.mapTile.StandardMapTileFactory.TilePathBuilder;
import com.bbn.openmap.dataAccess.mapTile.TMSMapTileCoordinateTransform;
import com.bbn.openmap.dataAccess.mapTile.ZoomLevelInfo;
import com.bbn.openmap.dataAccess.shape.EsriGraphic;
import com.bbn.openmap.dataAccess.shape.EsriGraphicList;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.proj.coords.LatLonPoint;
import com.bbn.openmap.util.PropUtils;
/**
* Utility class that fetches tiles from a http source. Runs as an application.
* Prints out a usage statement that describes options.
*
* @author dietrick
*/
public class TileGrabber {
public TileGrabber() {
}
/**
* @author ddietrick
*/
public static class Builder {
protected String source;
protected String target = "";
protected int fromZoom = 0;
protected int toZoom = 0;
protected int minx = 0;
protected int miny = 0;
protected int maxx = -1;
protected int maxy = -1;
protected double minlon = -180.0;
protected double minlat = -85.0;
protected double maxlon = 180.0;
protected double maxlat = 85.0;
protected boolean fill = false;
protected boolean verbose = false;
protected boolean extraVerbose = false;
protected boolean tileBoundsSet = false;
MapTileCoordinateTransform transform = new OSMMapTileCoordinateTransform();
public Builder(String source) throws FileNotFoundException {
if (source == null) {
throw new FileNotFoundException("Source file invalid");
}
this.source = source;
}
public Builder targetFile(String targetFile) {
this.target = targetFile;
return this;
}
public Builder fromZoom(int zoomLevel) throws NumberFormatException {
if (checkZoomLevel(zoomLevel)) {
this.fromZoom = zoomLevel;
}
if (this.fromZoom > this.toZoom) {
this.toZoom = this.fromZoom;
}
return this;
}
public Builder toZoom(int zoomLevel) throws NumberFormatException {
if (checkZoomLevel(zoomLevel)) {
this.toZoom = zoomLevel;
if (this.fromZoom > this.toZoom) {
this.fromZoom = this.toZoom;
}
}
return this;
}
protected boolean checkZoomLevel(int zoomLevel) {
if (zoomLevel < 0 || zoomLevel > 20) {
throw new NumberFormatException("Zoom level needs to be > 0 and < 20");
}
return true;
}
public String toString() {
StringBuilder sb = new StringBuilder("WholeWorldTileHandler[");
sb.append("source:").append(source).append(',');
sb.append("target:").append(target).append(',');
sb.append("fromZoom:").append(fromZoom).append(',');
sb.append("toZoom:").append(toZoom).append(',');
if (minx != 0) {
sb.append(",").append("minx:").append(minx);
}
if (miny != 0) {
sb.append(",").append("miny:").append(miny);
}
if (maxx >= 0) {
sb.append(",").append("maxx:").append(maxx);
}
if (maxy >= 0) {
sb.append(",").append("maxy:").append(maxy);
}
sb.append(']');
return sb.toString();
}
protected int[] getTileBoundsForProjection(LatLonPoint ul, LatLonPoint lr, int z) {
if (fromZoom == toZoom && tileBoundsSet) {
ZoomLevelInfo zoomInfo = new ZoomLevelInfo();
zoomInfo.setZoomLevel(z);
int maxDim = zoomInfo.getEdgeTileCount();
if (maxx == -1) {
maxx = maxDim;
}
if (maxy == -1) {
maxy = maxDim;
}
return new int[] { miny, minx, maxy, maxx };
}
return transform.getTileBoundsForProjection(ul, lr, z);
}
/**
* source:
* "http://192.168.99.100:32771/mapbox-studio-osm-bright/{z}/{x}/{y}.png"
* target: "/Users/dietrick/Downloads/st_osm_tiles"
*
* @throws FileNotFoundException
* @throws IOException
*/
public void go() throws FileNotFoundException, IOException {
LatLonPoint ul = new LatLonPoint.Double(maxlat, minlon);
LatLonPoint lr = new LatLonPoint.Double(minlat, maxlon);
ServerMapTileFactory tileServer = new ServerMapTileFactory();
TilePathBuilder serverPathBuilder = new TilePathBuilder(source);
TilePathBuilder localPathBuilder = new TilePathBuilder(target);
ZoomLevelInfo zoomInfo = new ZoomLevelInfo();
for (int z = fromZoom; z <= toZoom; z++) {
int[] uv = getTileBoundsForProjection(ul, lr, z);
int startX = uv[1];
int endX = uv[3];
int startY = uv[0];
int endY = uv[2];
if (verbose) {
System.out.println("fetching tiles for zoom level " + z + " between " + startY + ", " + startX
+ " and " + endY + ", " + endX);
}
zoomInfo.setZoomLevel(z);
int maxDim = zoomInfo.getEdgeTileCount();
for (int x = startX; x <= endX && x <= maxDim - 1; x++) {
for (int y = startY; y <= endY && y <= maxDim - 1; y++) {
String serverImagePath = serverPathBuilder.buildTilePath(x, y, z, ".png");
String localFilePath = localPathBuilder.buildTilePath(x, y, z, ".png");
if (fill) {
File localFile = new File(localFilePath);
if (localFile.exists()) {
if (extraVerbose) {
System.out.println("--- skipping " + localFilePath + ", aready got it");
}
continue;
}
}
if (extraVerbose) {
System.out.println("<<< fetching " + serverImagePath);
}
tileServer.getImageBytes(serverImagePath, localFilePath);
}
}
}
}
public void go(String shapefile) throws FileNotFoundException, IOException {
try {
if (verbose) {
System.out.println("fetching tiles covering " + shapefile);
}
URL shp = PropUtils.getResourceOrFileOrURL(shapefile);
EsriGraphicList egl = EsriGraphicList.getEsriGraphicList(shp, null, null);
fetchTilesForEsriGraphicList(egl);
} catch (MalformedURLException murle) {
throw new FileNotFoundException(murle.getMessage());
}
}
protected void fetchTilesForEsriGraphicList(EsriGraphicList egl) throws FileNotFoundException, IOException {
for (OMGraphic omg : egl) {
if (omg instanceof EsriGraphicList) {
fetchTilesForEsriGraphicList((EsriGraphicList) omg);
} else if (omg instanceof EsriGraphic) {
fetchTilesForEsriGraphic((EsriGraphic) omg);
}
}
}
protected void fetchTilesForEsriGraphic(EsriGraphic eg) throws FileNotFoundException, IOException {
// miny, minx, maxy maxx
double[] coords = eg.getExtents();
minlat = coords[0];
minlon = coords[1];
maxlat = coords[2];
maxlon = coords[3];
go();
}
/**
* Set the starting x number of the subjar file to create. Depends on
* the subjar zoom to figure out what that means.
*
* @param parseInt
*/
public void minx(int parseInt) {
minx = parseInt;
tileBoundsSet = true;
}
/**
* Set the starting y number of the subjar file to create. Depends on
* the subjar zoom to figure out what that means.
*
* @param parseInt
*/
public void miny(int parseInt) {
miny = parseInt;
tileBoundsSet = true;
}
/**
* Set the ending y number of the subjar file to create. Depends on the
* subjar zoom to figure out what that means.
*
* @param parseInt
*/
public void maxx(int parseInt) {
maxx = parseInt;
tileBoundsSet = true;
}
/**
* Set the ending y number of the subjar file to create. Depends on the
* subjar zoom to figure out what that means.
*
* @param parseInt
*/
public void maxy(int parseInt) {
maxy = parseInt;
tileBoundsSet = true;
}
/**
* set lower longitude of fetching
*
* @param parsed
*/
public void minlon(double parsed) {
minlon = parsed;
}
/**
* set lower latitude of fetching
*
* @param parsed
*/
public void minlat(double parsed) {
minlat = parsed;
}
/**
* set upper longitude of fetching
*
* @param parsed
*/
public void maxlon(double parsed) {
maxlon = parsed;
}
/**
* set upper latitude of fetching
*
* @param parsed
*/
public void maxlat(double parsed) {
maxlat = parsed;
}
/**
* Check whether the build process will only fetch tiles that don't
* exist.
*/
public boolean isFill() {
return fill;
}
/**
* Set whether the build process will only fetch tiles that don't exist.
*
* @param fill
*/
public void setFill(boolean fill) {
this.fill = fill;
}
}
/**
* Takes arguments for source tile directory, target directory, and option
* sub-jar zoom level, and creates jars in the right place with expected
* tiles. Prints usage statement.
*
* @param args
*/
public static void main(String[] args) {
com.bbn.openmap.util.ArgParser ap = new com.bbn.openmap.util.ArgParser("TileGrabber");
ap.add("source", "Path to fetch tiles from, should be http://path/{z}/{x}/{y}.png form", 1);
ap.add("target", "Path to the output directory for fetched tiles", 1);
ap.add("fromZoom", "Starting zoom level", 1);
ap.add("toZoom", "Ending zoom level", 1);
ap.add("minx", "min x tile (only used if fromZoom and toZoom match)", 1);
ap.add("miny", "min y tile (only used if fromZoom and toZoom match)", 1);
ap.add("maxx", "max x tile (only used if fromZoom and toZoom match)", 1);
ap.add("maxy", "max y tile (only used if fromZoom and toZoom match)", 1);
ap.add("minlon", "min longitude", 1);
ap.add("minlat", "min latitude", 1);
ap.add("maxlon", "max longitude", 1);
ap.add("maxlat", "max latitude", 1);
ap.add("shapefile", "fetch coverage over shapes in shapefile", 1);
ap.add("verbose", "Describe what's going on.");
ap.add("extraVerbose", "Really describe what's going on.");
ap.add("fill", "Only fetch tiles that don't exist.");
ap.add("TMS", "Specify that the tile numbering scheme matches TMS (OSM is default)");
if (!ap.parse(args)) {
ap.printUsage();
System.exit(0);
}
String[] arg = ap.getArgValues("source");
if (arg != null) {
try {
Builder wwthBuilder = new Builder(arg[0]);
arg = ap.getArgValues("target");
if (arg != null) {
wwthBuilder.targetFile(arg[0]);
}
arg = ap.getArgValues("fromZoom");
if (arg != null) {
wwthBuilder.fromZoom(Integer.parseInt(arg[0]));
}
arg = ap.getArgValues("toZoom");
if (arg != null) {
wwthBuilder.toZoom(Integer.parseInt(arg[0]));
}
arg = ap.getArgValues("minx");
if (arg != null) {
wwthBuilder.minx(Integer.parseInt(arg[0]));
}
arg = ap.getArgValues("miny");
if (arg != null) {
wwthBuilder.miny(Integer.parseInt(arg[0]));
}
arg = ap.getArgValues("maxx");
if (arg != null) {
wwthBuilder.maxx(Integer.parseInt(arg[0]));
}
arg = ap.getArgValues("maxy");
if (arg != null) {
wwthBuilder.maxy(Integer.parseInt(arg[0]));
}
arg = ap.getArgValues("minlon");
if (arg != null) {
wwthBuilder.minlon(Double.parseDouble(arg[0]));
}
arg = ap.getArgValues("minlat");
if (arg != null) {
wwthBuilder.minlat(Double.parseDouble(arg[0]));
}
arg = ap.getArgValues("maxlon");
if (arg != null) {
wwthBuilder.maxlon(Double.parseDouble(arg[0]));
}
arg = ap.getArgValues("maxlat");
if (arg != null) {
wwthBuilder.maxlat(Double.parseDouble(arg[0]));
}
arg = ap.getArgValues("verbose");
if (arg != null) {
wwthBuilder.verbose = true;
}
arg = ap.getArgValues("extraVerbose");
if (arg != null) {
wwthBuilder.verbose = true;
wwthBuilder.extraVerbose = true;
}
arg = ap.getArgValues("fill");
if (arg != null) {
wwthBuilder.setFill(true);
}
arg = ap.getArgValues("TMS");
if (arg != null) {
wwthBuilder.transform = new TMSMapTileCoordinateTransform();
}
arg = ap.getArgValues("shapefile");
if (arg != null) {
wwthBuilder.go(arg[0]);
} else {
System.out.println(wwthBuilder.toString());
wwthBuilder.go();
}
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
System.out.println(nfe.getMessage());
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
System.out.println(fnfe.getMessage());
} catch (IOException ioe) {
ioe.printStackTrace();
System.out.println(ioe.getMessage());
}
} else {
ap.bail("Need a source directory.", true);
}
System.exit(0);
}
}