/*
* <copyright>
* Copyright 2011 BBN Technologies
* </copyright>
*/
package com.bbn.openmap.dataAccess.mapTile;
import java.awt.Image;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipOutputStream;
import javax.swing.ImageIcon;
import com.bbn.openmap.image.BufferedImageHelper;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.ClasspathHacker;
import com.bbn.openmap.util.FileUtils;
import com.bbn.openmap.util.PropUtils;
/**
* The WholeWorldTileHandler is a special EmptyTileHandler that manages a
* special set of jar files containing tiles that cover the entire earth. If you
* put all of the tiles from zoom levels 0-12 into a jar file, you end up with a
* 5Gb jar, and java doesn't handle it. Having zoom levels 0-14 on disk takes up
* 30Gb without being compressed.
* <p>
*
* This class, run as an application, organizes tiles into several jar files,
* depending on their zoom level and location. All tiles for the world in zoom
* levels 0-10 are placed in a single jar file. For all other zoom levels, they
* are placed together geographically, based on the tile boundaries of some
* higher level (zoom level 3 is the default, higher numbers might be chosen if
* tiles are created for higher zoom levels than 15).
* <p>
*
* This class is consulted when the MapTileFactory can't find a map tile. Since
* this class is an EmptyTileHandler, it is given free reign to return a tile
* for the missing one. Instead of assuming that the tiles don't exist and a
* quick replacement needs to be created, this EmtpyTileHandler assumes that the
* proper jar file hasn't been loaded yet, which is why the tile wasn't found as
* a resource. It uses the ux, uv, and zoom level for the tile to figure out
* what jar it would have been in, and checks to see if that jar has been added
* to the classpath. If the jar has been added, then the tile doesn't exist. If
* the jar needs to be added, it is and then the tile retrieval starts.
* <p>
*
* The properties for the tile set are a little different when using this
* EmptyTileHandler:
*
* <pre>
* # specify this empty tile handler
* emptyTileHandler=com.bbn.openmap.dataAccess.mapTile.WholeWorldTileHandler
* # instead of the root directory to the jars, specify the root directory of the tiles
* # inside the jars. All of the jars within a set should share the root directory name
* # within them. Each set of jars should have a unique root directory name for that dataset.
* rootDir=main_tiles
* # The name of the jar containing world-level coverage. All of the sub-jars should be
* # named based on this jar name. If this class is run to build the tile jars,
* # it should have done this automatically.
* parentJarName=main_tiles.jar
* # This is the grid that defines how the subjars are divided. It matches the tile boundaries
* # of a certain zoom level. Level 3 is the default.
* subJarZoomLevel=9
* # This is the highest level of tiles held in the part jar name. Tells the layer when it should
* # start looking in subjars for tiles at higher zoom levels.
* worldWideZoomLevel=10
* # Tells the layer when to stop trying to fill tile orders.
* noCoverageZoom=15
* # optional, png is default.
* fileExt=.png
* # You can also specify how empty tiles are handled, using the ShpFileEmptyTileHandler.
* shpFile=/Users/dietrick/dev/openmap/share/data/shape/cntry02/cntry02.shp
* land.fillColor=DECD8B
* background.fillColor=EAFFF4
* mapTileTransform=com.bbn.openmap.dataAccess.mapTile.OSMMapTileCoordinateTransform
* land.lineColor=DECD8B
* background.lineColor=EAFFF4
*
* </pre>
*
* NOTE: If you create a set of tile jars to use with a Windows jre, and you're
* having problems when the jars are added to the classpath, it's because there
* are too many tiles in the jars. The number for files in this case can't
* exceed 262144. So you have to increase the zoom level used for the layout of
* the subjar files.
*
* @author ddietrick
*/
public class WholeWorldTileHandler extends ShpFileEmptyTileHandler {
public final static String SUBJAR_ZOOMLEVEL_PROPERTY = "subJarZoomLevel";
public final static String WORLDWIDE_ZOOMLEVEL_PROPERTY = "worldWideZoomLevel";
public final static String PARENT_JAR_NAME_PROPERTY = "parentJarName";
public final static int DEFAULT_SUBJAR_DEF_ZOOMLEVEL = 3;
public final static int DEFAULT_WORLDWIDE_ZOOMLEVEL = 10;
protected int subFrameDefZoomLevel = DEFAULT_SUBJAR_DEF_ZOOMLEVEL;
protected int worldWideZoomLevel = DEFAULT_WORLDWIDE_ZOOMLEVEL;
protected String parentJarName;
protected Set<String> loadedJars;
protected String rootDirForJars;
final static String SOURCE = "source";
final static String TARGET = "target";
final static String SUB_JAR_ZOOM = "subJarZoom";
final static String MAX_ZOOM_IN_SUBJARS = "maxZoomInSubJars";
final static String WORLD_WIDE_ZOOM_LEVEL = "worldWideZoomLevel";
final static String TILE_EXT = "tileExt";
final static String MINX = "minx";
final static String MINY = "miny";
final static String MAXX = "maxx";
final static String MAXY = "maxy";
final static String NO_WORLD_JAR = "noWorldJar";
final static String VERBOSE = "verbose";
final static String FILL = "fill";
/**
* Turns out that the Windows JRE won't add jar files with more than 262144
* tiles in them.
*/
public final static int WIN_MAX_FILES_IN_JAR = 262144;
public WholeWorldTileHandler() {
loadedJars = Collections.synchronizedSet(new HashSet<String>());
}
/*
* (non-Javadoc)
*
* @see com.bbn.openmap.dataAccess.mapTile.EmptyTileHandler#
* getOMGraphicForEmptyTile (java.lang.String, int, int, int,
* com.bbn.openmap.dataAccess.mapTile.MapTileCoordinateTransform,
* com.bbn.openmap.proj.Projection)
*/
public BufferedImage getImageForEmptyTile(String imagePath, int x, int y, int zoomLevel,
MapTileCoordinateTransform mtcTransform,
Projection proj) {
BufferedImage bi = null;
if (parentJarName != null) {
/**
* OK, the thing to remember here is that if this method has been
* called, the tile has not been found. So our job here is to
* determine if a jar file needs to be loaded, and if it does, load
* it and see if the missing tile is in it. If a jar doesn't need to
* be loaded, then we push the action up to the superclass - it's a
* non-created tile.
*/
Point2D pnt = new Point2D.Double(x, y);
Point2D tileUL = mtcTransform.tileUVToLatLon(pnt, zoomLevel);
StringBuilder jarFilePath = new StringBuilder("jar:file:").append(rootDirForJars).append("/");
if (zoomLevel <= worldWideZoomLevel) {
/*
* Need to load the primary jar that has worldwide coverage.
*/
jarFilePath.append(parentJarName);
} else {
/*
* Need to figure out what the subjar name is, given the lat/lon
* coordinate of the tile.
*/
Point2D subframeUVPnt = mtcTransform.latLonToTileUV(tileUL, subFrameDefZoomLevel);
jarFilePath.append(buildSubJarName(parentJarName, subframeUVPnt.getX(), subframeUVPnt.getY()));
}
jarFilePath.append("!/").append(imagePath);
byte[] imageBytes = getImageBytes(jarFilePath.toString());
if (imageBytes != null && imageBytes.length > 0) {
// image found
try {
ImageIcon ii = new ImageIcon(imageBytes);
Image origImage = ii.getImage();
int imageWidth = ii.getIconWidth();
int imageHeight = ii.getIconHeight();
if (origImage instanceof BufferedImage) {
return (BufferedImage) origImage;
} else {
return BufferedImageHelper.getBufferedImage(origImage, 0, 0, imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
}
} catch (InterruptedException ie) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("WholeWorldTileHandler interrupted fetching " + imagePath);
}
}
}
} else {
logger.fine("parent jar name not set, can't figure out how to load tile jars.");
}
// If no image is found, do the default action for an empty tile.
if (bi == null) {
bi = super.getImageForEmptyTile(imagePath, x, y, zoomLevel, mtcTransform, proj);
}
return bi;
}
public static String buildSubJarName(String parentJarNme, double x, double y) {
int dotIndex = parentJarNme.length();
if (parentJarNme.endsWith(".jar")) {
dotIndex = parentJarNme.lastIndexOf('.');
}
StringBuilder sBuilder = new StringBuilder(parentJarNme.substring(0, dotIndex));
sBuilder.append("_" + ((int) x) + "_" + ((int) y) + parentJarNme.substring(dotIndex));
return sBuilder.toString();
}
/**
* Tries to get the image bytes from imagePath URL. If image found, will
* write it locally to localFilePath for caching.
*
* @param imagePath the source URL image path.
* @return byte[] of image
*/
public byte[] getImageBytes(String imagePath) {
byte[] imageBytes = null;
try {
java.net.URL url = new java.net.URL(imagePath);
java.net.URLConnection urlc = url.openConnection();
if (logger.isLoggable(Level.FINER)) {
logger.finer("url content type: " + urlc.getContentType());
}
if (urlc == null || urlc.getContentType() == null) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("unable to connect to (tile might be unavailable): " + imagePath);
}
// text
} else if (urlc.getContentType().startsWith("text")) {
java.io.BufferedReader bin = new java.io.BufferedReader(new java.io.InputStreamReader(urlc.getInputStream()));
String st;
StringBuffer message = new StringBuffer();
while ((st = bin.readLine()) != null) {
message.append(st);
}
// Debug.error(message.toString());
// How about we toss the message out to the user
// instead?
logger.fine(message.toString());
// image
} else if (urlc.getContentType().startsWith("image")) {
InputStream in = urlc.getInputStream();
// ------- Testing without this
ByteArrayOutputStream out = new ByteArrayOutputStream();
int buflen = 2048; // 2k blocks
byte buf[] = new byte[buflen];
int len = -1;
while ((len = in.read(buf, 0, buflen)) != -1) {
out.write(buf, 0, len);
}
out.flush();
out.close();
imageBytes = out.toByteArray();
} // end if image
} catch (java.net.MalformedURLException murle) {
logger.warning("WholeWorldTileHandler: URL \"" + imagePath + "\" is malformed.");
} catch (java.io.IOException ioe) {
logger.fine("Couldn't connect to " + imagePath + ", connection problem");
}
return imageBytes;
}
/**
* Load the jar if necessary. Not called anymore, but left here as a note on
* how to dynamically modify the classpath to add a jar at runtime.
*
* @param jarFileName the absolute path to the jar.
* @return true if the jar was loaded, false if it was added before and no
* attempt made.
*/
protected synchronized boolean loadJar(String jarFileName) {
boolean ret = loadedJars.contains(jarFileName);
if (!ret) {
try {
logger.fine("adding " + jarFileName + " to classpath");
ClasspathHacker.addFile(jarFileName);
} catch (IOException ioe) {
logger.warning("couldn't add map data jar file: " + jarFileName);
}
/*
* We're going to add the jarFileName to the list regardless of
* whether it was successful or not, to avoid having repeated
* failures on subsequent jar loads.
*/
loadedJars.add(jarFileName);
}
return !ret;
}
public void setProperties(String prefix, Properties props) {
super.setProperties(prefix, props);
prefix = PropUtils.getScopedPropertyPrefix(prefix);
subFrameDefZoomLevel = PropUtils.intFromProperties(props, prefix
+ SUBJAR_ZOOMLEVEL_PROPERTY, subFrameDefZoomLevel);
worldWideZoomLevel = PropUtils.intFromProperties(props, prefix
+ WORLDWIDE_ZOOMLEVEL_PROPERTY, worldWideZoomLevel);
rootDirForJars = props.getProperty(prefix
+ StandardMapTileFactory.ROOT_DIR_PATH_PROPERTY, rootDirForJars);
String jarBaseName = props.getProperty(prefix + PARENT_JAR_NAME_PROPERTY);
if (jarBaseName != null) {
setParentJarName(jarBaseName);
}
}
public Properties getProperties(Properties props) {
props = super.getProperties(props);
String prefix = PropUtils.getScopedPropertyPrefix(this);
props.put(prefix + SUBJAR_ZOOMLEVEL_PROPERTY, Integer.toString(subFrameDefZoomLevel));
props.put(prefix + WORLDWIDE_ZOOMLEVEL_PROPERTY, Integer.toString(worldWideZoomLevel));
props.put(prefix + PARENT_JAR_NAME_PROPERTY, PropUtils.unnull(getParentJarName()));
return props;
}
/**
* Get the tile zoom level that the subjar borders are based on.
*
* @return the subFrameDefZoomLevel
*/
public int getSubFrameDefZoomLevel() {
return subFrameDefZoomLevel;
}
/**
* Set the tile zoom level that the subjar borders will be based on.
*
* @param subFrameZoomLevel the subFrameDefZoomLevel to set
*/
public void setSubFrameDefZoomLevel(int subFrameZoomLevel) {
this.subFrameDefZoomLevel = subFrameZoomLevel;
}
/**
* Get the name of the jar with worldwide coverage, for zoom levels
* 0-worldWideZoomLevel.
*
* @return the parentJarName
*/
public String getParentJarName() {
return parentJarName;
}
/**
* Set the name of the jar with worldwide coverage, for tiles with zoom
* levels 0-worldWideZoomLevel.
*
* @param parentJarName the parentJarName to set
*/
public void setParentJarName(String parentJarName) {
this.parentJarName = parentJarName;
}
/**
* Get the maximum tile zoom level that is contained in the worldwide jar.
*
* @return the worldWideZoomLevel
*/
public int getWorldWideZoomLevel() {
return worldWideZoomLevel;
}
/**
* Set the maximum tile zoom level that is contained in the worldwide jar.
*
* @param worldWideZoomLevel the worldWideZoomLevel to set
*/
public void setWorldWideZoomLevel(int worldWideZoomLevel) {
this.worldWideZoomLevel = worldWideZoomLevel;
}
/**
* The builder class takes a map tile directory and creates a set of jars
* from it, divided so that the WholeWorldTileHandler can deal with it. It
* will create one jar file that holds the tiles that cover the world for a
* selected set of tiles. It then creates jar files (sub-jars) that contain
* tiles for the other zoom levels. These sub-jars are organized by
* geographic zone as defined by tiles from a lower zoom level. The default
* level is 3. For instance, subjar_0_0.jar contains all the tiles for zoom
* levels 11-20 that fit in the geographic area covered by tile 0, 0 for
* zoom level 3.
* <P>
* There are options to control the geographic area definition zoom level,
* the zoom level for the tiles stored in the world-wide jar, and the
* maximum zoom level inserted in to the sub-jars.
*
* @author ddietrick
*/
public static class Builder {
protected File sourceFile;
protected File targetFile;
protected int subJarZoomDef = WholeWorldTileHandler.DEFAULT_SUBJAR_DEF_ZOOMLEVEL;
protected int worldWideZoomLevel = 10;
protected int maxZoomLevelInSubJars = 20;
protected String tileExt = ".png";
protected int minx = 0;
protected int miny = 0;
protected int maxx = -1;
protected int maxy = -1;
protected boolean doWorldJar = true;
protected boolean fill = false;
protected Level logLevel = Level.FINE;
public Builder(File source) throws FileNotFoundException {
if (source == null || !source.exists()) {
throw new FileNotFoundException("Source file invalid");
}
this.sourceFile = source;
}
public Builder targetFile(File targetFile) {
this.targetFile = targetFile;
return this;
}
public Builder tileExt(String tileExtension) {
if (!tileExtension.startsWith(".")) {
tileExtension = "." + tileExtension;
}
this.tileExt = tileExtension;
return this;
}
public Builder subJarZoomDef(int zoomLevel) throws NumberFormatException {
if (checkZoomLevel(zoomLevel)) {
this.subJarZoomDef = zoomLevel;
}
return this;
}
public Builder worldWideZoomLevel(int zoomLevel) throws NumberFormatException {
if (checkZoomLevel(zoomLevel)) {
this.worldWideZoomLevel = zoomLevel;
}
return this;
}
public Builder maxZoomLevelInSubJars(int zoomLevel) throws NumberFormatException {
if (checkZoomLevel(zoomLevel)) {
this.maxZoomLevelInSubJars = zoomLevel;
}
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(sourceFile).append(',');
sb.append("target:").append(targetFile).append(',');
sb.append("subJarZoomDef:").append(subJarZoomDef).append(',');
sb.append("worldWideZoomLevel:").append(worldWideZoomLevel).append(',');
sb.append("maxZoomLevelInSubJars:").append(maxZoomLevelInSubJars).append(',');
sb.append("tileExt:").append(tileExt);
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 void copyAndUpdateProperties(File sourceDir, File targetDir) {
File propertiesFile = new File(sourceDir, StandardMapTileFactory.TILE_PROPERTIES);
if (propertiesFile.exists()) {
try {
URL propertiesURL = propertiesFile.toURI().toURL();
Properties props = new Properties();
props.load(propertiesURL.openStream());
props.put(StandardMapTileFactory.EMPTY_TILE_HANDLER_PROPERTY, WholeWorldTileHandler.class.getName());
props.put(SUBJAR_ZOOMLEVEL_PROPERTY, Integer.toString(subJarZoomDef));
props.put(WORLDWIDE_ZOOMLEVEL_PROPERTY, Integer.toString(worldWideZoomLevel));
props.put(PARENT_JAR_NAME_PROPERTY, sourceDir.getName() + ".jar");
props.put(StandardMapTileFactory.ROOT_DIR_PROPERTY, sourceDir.getName());
FileOutputStream fos = new FileOutputStream(new File(targetDir, StandardMapTileFactory.TILE_PROPERTIES));
props.store(fos, "Properties for " + targetDir + " tile set");
fos.close();
} catch (MalformedURLException e) {
e.printStackTrace();
logger.warning("can't find/read properties file for tile set");
} catch (IOException e) {
e.printStackTrace();
logger.warning("exception reading/copying properties file for tile set");
}
}
}
public void go() throws FileNotFoundException, IOException {
if (targetFile == null) {
targetFile = new File(sourceFile, sourceFile.getName());
}
if (!targetFile.exists()) {
targetFile.mkdirs();
}
File parentOfSourceFile = sourceFile.getParentFile();
int numCharsTrimmedOffNameInZip = 0;
if (parentOfSourceFile != null) {
numCharsTrimmedOffNameInZip = parentOfSourceFile.getAbsolutePath().length() + 1;
}
logger.log(Level.INFO, "inspecting files in " + sourceFile + " ("
+ (parentOfSourceFile == null ? "CAUTION: nothing"
: parentOfSourceFile.getAbsolutePath())
+ " removed from names in jars).");
copyAndUpdateProperties(sourceFile, targetFile);
// Create the top-level worldwide jar
if (doWorldJar) {
List<File> jarDirs = new ArrayList<File>();
for (int zoomLevel = 0; zoomLevel <= worldWideZoomLevel; zoomLevel++) {
File zoomLevelDir = new File(sourceFile, Integer.toString(zoomLevel));
if (zoomLevelDir.exists()) {
jarDirs.add(zoomLevelDir);
}
}
if (!jarDirs.isEmpty() && sourceFile != null) {
String worldWideJarFile = targetFile + File.separator + sourceFile.getName()
+ ".jar";
logger.log(logLevel, "writing :" + worldWideJarFile);
if (fill) {
File fileCheck = new File(worldWideJarFile);
if (fileCheck.exists()) {
doWorldJar = false;
}
}
// after the fill check, check this again.
if (doWorldJar) {
FileOutputStream fos = new FileOutputStream(worldWideJarFile);
CheckedOutputStream checksum = new CheckedOutputStream(fos, new Adler32());
ZipOutputStream zoStream = new ZipOutputStream(new BufferedOutputStream(checksum));
// ZipOutputStream zoStream = new ZipOutputStream(fos);
// zoStream.setMethod(ZipOutputStream.DEFLATED);
for (File file : jarDirs) {
FileUtils.writeZipEntry(file, zoStream, numCharsTrimmedOffNameInZip);
}
zoStream.close();
} else {
logger.log(logLevel, worldWideJarFile + " already exists, skipping");
}
}
} else {
logger.log(logLevel, "skipping world file");
}
// Worldwide jar created.
// Now create subjars
int dimensionForZoom = (int) Math.pow(2, subJarZoomDef);
MapTileCoordinateTransform transform = new OSMMapTileCoordinateTransform();
int startx = 0;
int starty = 0;
int endx = dimensionForZoom;
int endy = dimensionForZoom;
// OK, look at the minx, miny, maxx, maxy to figure out the jar
// files to create.
if (minx >= 0) {
startx = minx;
}
if (maxx >= 0) {
endx = Math.min(maxx + 1, dimensionForZoom);
}
if (miny >= 0) {
starty = miny;
}
if (maxy >= 0) {
endy = Math.min(maxy + 1, dimensionForZoom);
}
// x, y tile coordinates for subjar files.
for (int x = startx; x < endx; x++) {
for (int y = starty; y < endy; y++) {
// Calculate the lat/lon limits of the current tile
Point2D llp1 = transform.tileUVToLatLon(new Point2D.Double(x, y), subJarZoomDef);
Point2D llp2 = transform.tileUVToLatLon(new Point2D.Double(x + 1, y
+ 2), subJarZoomDef);
File subJarFile = new File(targetFile, sourceFile.getName() + "_" + x + "_" + y
+ ".jar");
if (fill && subJarFile.exists()) {
logger.log(logLevel, subJarFile + " already exists, skipping");
continue;
}
logger.log(logLevel, "Creating: " + subJarFile);
long fileCount = 0;
ZipOutputStream zoStream = null;
for (int zoomLevel = worldWideZoomLevel
+ 1; zoomLevel <= maxZoomLevelInSubJars; zoomLevel++) {
File checkZoomLevelDir = new File(sourceFile, Integer.toString(zoomLevel));
if (checkZoomLevelDir.exists()) {
// These are the tile coordinate bounds for this
// subjar for this zoom level
Point2D uv1 = transform.latLonToTileUV(llp1, zoomLevel);
Point2D uv2 = transform.latLonToTileUV(llp2, zoomLevel);
logger.log(logLevel, "adding zoom level " + zoomLevel + " tiles to "
+ subJarFile.getName() + ": " + uv1 + "|" + uv2);
for (int u = (int) uv1.getX(); u < uv2.getX(); u++) {
for (int v = (int) uv1.getY(); v < uv2.getY(); v++) {
// add file to subjar stream
File tile = new File(sourceFile, zoomLevel + "/" + u + "/" + v
+ tileExt);
if (tile.exists()) {
if (zoStream == null) {
/*
* We need to do this because we
* need to make sure a zip output
* stream is created only when a
* file is going to be written to
* it. you can't create a zip file
* and then put nothing into it.
*/
@SuppressWarnings("resource")
FileOutputStream fos = new FileOutputStream(subJarFile);
CheckedOutputStream checksum = new CheckedOutputStream(fos, new Adler32());
zoStream = new ZipOutputStream(new BufferedOutputStream(checksum));
// zoStream = new
// ZipOutputStream(fos);
// zoStream.setMethod(ZipOutputStream.DEFLATED);
}
if (tile.getAbsolutePath().length()
- numCharsTrimmedOffNameInZip > 0) {
FileUtils.writeZipEntry(tile, zoStream, numCharsTrimmedOffNameInZip);
fileCount++;
} else {
logger.info("Problem, there's something wrong with tile name: "
+ tile.getAbsolutePath());
}
}
}
}
}
}
if (zoStream != null) {
zoStream.close();
zoStream = null;
logger.log(logLevel, "closing zip file (" + subJarFile.getPath()
+ "), added " + fileCount + " files to jar");
}
}
}
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
public void setDoWorldJar(boolean dwj) {
doWorldJar = dwj;
}
/**
* Check whether the build process will only create jars that don't
* exist.
*/
public boolean isFill() {
return fill;
}
/**
* Set whether the build process will only create jars 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("WholeWorldTileHandler");
ap.add(SOURCE, "Path to the tile root directory.", 1);
ap.add(TARGET, "Path to the tile root directory for the jarred tile files.", 1);
ap.add(SUB_JAR_ZOOM, "Zoom level tiles that subjar boundaries are based on (3 is default).", 1);
ap.add(MAX_ZOOM_IN_SUBJARS, "Maximum tile zoom level added to sub jars (20 is default).", 1);
ap.add(WORLD_WIDE_ZOOM_LEVEL, "Maximum tile zoom level to add to world wide jar (10 is default).", 1);
ap.add(TILE_EXT, "Tile extension (.png is default).", 1);
ap.add(MINX, "Subjar x minimum to create", 1);
ap.add(MINY, "Subjar y minimum to create", 1);
ap.add(MAXX, "Subjar x maximum to create", 1);
ap.add(MAXY, "Subjar y maximum to create", 1);
ap.add(NO_WORLD_JAR, "Don't create world level jar file");
ap.add(VERBOSE, "Comment on what's going on");
ap.add(FILL, "Just create jars that don't exist.");
if (!ap.parse(args)) {
ap.printUsage();
System.exit(0);
}
String[] arg = ap.getArgValues(SOURCE);
if (arg != null) {
try {
Builder wwthBuilder = new Builder(new File(arg[0]));
arg = ap.getArgValues(TARGET);
if (arg != null) {
wwthBuilder.targetFile(new File(arg[0]));
}
arg = ap.getArgValues(SUB_JAR_ZOOM);
if (arg != null) {
wwthBuilder.subJarZoomDef(Integer.parseInt(arg[0]));
}
arg = ap.getArgValues(MAX_ZOOM_IN_SUBJARS);
if (arg != null) {
wwthBuilder.maxZoomLevelInSubJars(Integer.parseInt(arg[0]));
}
arg = ap.getArgValues(WORLD_WIDE_ZOOM_LEVEL);
if (arg != null) {
wwthBuilder.worldWideZoomLevel(Integer.parseInt(arg[0]));
}
arg = ap.getArgValues(TILE_EXT);
if (arg != null) {
wwthBuilder.tileExt(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(VERBOSE);
if (arg != null) {
wwthBuilder.logLevel = Level.INFO;
}
arg = ap.getArgValues(FILL);
if (arg != null) {
wwthBuilder.setFill(true);
}
arg = ap.getArgValues(NO_WORLD_JAR);
if (arg != null) {
logger.log(wwthBuilder.logLevel, "setting build world file to false");
wwthBuilder.setDoWorldJar(false);
}
System.out.println(wwthBuilder.toString());
wwthBuilder.go();
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
logger.warning(nfe.getMessage());
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
logger.warning(fnfe.getMessage());
} catch (IOException ioe) {
ioe.printStackTrace();
logger.warning(ioe.getMessage());
}
} else {
ap.bail("Need a source directory.", true);
}
System.exit(0);
}
}