package com.nutiteq.maps;
import java.io.IOException;
import com.mgmaps.utils.Tools;
import com.nutiteq.components.WgsPoint;
import com.nutiteq.components.ZoomRange;
import com.nutiteq.config.StoredMapConfig;
import com.nutiteq.fs.FileSystem;
import com.nutiteq.log.Log;
import com.nutiteq.maps.projections.EPSG3785;
import com.nutiteq.ui.Copyright;
import com.nutiteq.ui.StringCopyright;
import com.nutiteq.utils.Utils;
/**
*<p>
* <strong>Note:</strong> to use stored map You need to also insert
* {@link com.nutiteq.fs.FileSystem} into map component using
* {@link com.nutiteq.BasicMapComponent#setFileSystem(FileSystem)}
*</p>
*/
public class StoredMap extends EPSG3785 implements GeoMap, UnstreamedMap {
private static final String CONFIG_FILENAME = "cache.conf";
private final String path;
// properties
protected String name;
private ZoomRange zoomRange;
private int tilesPerFile;
private int hashSize;
// file extension
private String fileExt;
// tiles per file X & Y
private int tpfx;
private int tpfy;
private boolean initializeConf;
private StoredMapConfig config;
private WgsPoint centerLocation;
private int centerZoom;
private static final int DEF_MIN_ZOOM = 1;
private static final int DEF_MAX_ZOOM = 17;
/**
* Constructor for StoredMap. Default map name is the last component of the
* path.
*
* @param name
* map name
* @param path
* root path for the stored map data
* @param readCacheConf
* whether to read tiles_per_file and hash_size settings from
* cache.conf
*/
public StoredMap(final String name, final String path, final boolean readCacheConf) {
this(name, path, readCacheConf, new StringCopyright(name));
}
/**
* Constructor for StoredMap. Default map name is the last component of the
* path.
*
* @param name
* map name
* @param path
* root path for the stored map data
* @param readCacheConf
* whether to read tiles_per_file and hash_size settings from
* cache.conf
* @param copyright
* Text/image for the copyright overlay
*/
public StoredMap(final String name, final String path, final boolean readCacheConf, final Copyright copyright) {
super(copyright, 256, DEF_MIN_ZOOM, DEF_MAX_ZOOM);
setName(name);
String p = path;
while (p.endsWith("/")) {
p = p.substring(0, p.length() - 1);
}
if (p.startsWith("file://")) {
p = p.substring(7);
}
if (p.startsWith("/")) {
p = p.substring(1);
}
this.path = p;
// default property values
this.zoomRange = new ZoomRange(DEF_MIN_ZOOM, DEF_MAX_ZOOM);
this.tilesPerFile = 1;
this.tpfx = 1;
this.tpfy = 1;
this.hashSize = 1;
this.fileExt = "mgm";
this.config = new StoredMapConfig(tilesPerFile, tpfx, tpfy, hashSize);
this.initializeConf = readCacheConf;
}
/**
* Read cache.conf
*/
public void initializeConfigUsingFs(final FileSystem fs) {
this.initializeConf = false;
this.config = readConfig(fs);
}
public StoredMapConfig readConfig(final FileSystem fs) {
try {
final String filename = path + "/" + CONFIG_FILENAME;
final byte[] data = fs.readFile(filename);
final String sdata = new String(data);
final String[] lines = Utils.split(sdata, "\n");
for (int i = 0; i < lines.length; i++) {
// split into at most 2 tokens
final String[] tokens = Tools.split(lines[i].trim(), '=', false, 2);
if (tokens.length == 2) {
final String name = tokens[0].trim().toLowerCase();
final String value = tokens[1].trim();
// ignore empty values
if (value.length() == 0) {
continue;
}
// ignore comments
if (name.startsWith("#")) {
continue;
}
if (name.equals("tiles_per_file")) {
final int tpf = Integer.parseInt(value);
if (tpf > 0 && (tpf & (-tpf)) == tpf) {
setTilesPerFile(tpf);
} else {
throw new IOException("Invalid tiles_per_file");
}
} else if (name.equals("hash_size")) {
final int hs = Integer.parseInt(value);
if (hs >= 1 && hs < 100) {
setHashSize(hs);
} else {
throw new IOException("Invalid hash_size");
}
} else if (name.equals("center")) {
try {
final String[] xyz = Tools.split(value.trim(), ',', false, 4);
double lat = Float.parseFloat(xyz[0].trim());
double lon = Float.parseFloat(xyz[1].trim());
int zoom = Integer.parseInt(xyz[2].trim());
Log.debug("center zoom found = " + lat + " " + lon + " " + zoom);
setCenterLocation(new WgsPoint(lon, lat), zoom);
} catch (final Exception ex) {
throw new IOException("invalid center location");
}
}
}
}
} catch (final IOException ex) {
Log.error("Error reading " + CONFIG_FILENAME);
Log.printStackTrace(ex);
return null;
}
return new StoredMapConfig(tilesPerFile, tpfx, tpfy, hashSize);
}
private void setCenterLocation(WgsPoint wgsPoint, int zoom) {
this.centerLocation = wgsPoint;
this.centerZoom = zoom;
}
public WgsPoint getCenterLocation() {
Log.debug("returning centerLocation "+centerLocation);
return centerLocation;
}
public int getCenterZoom() {
Log.debug("returning centerZoom"+centerZoom);
return centerZoom;
}
/**
* Set the name of this map.
*
* @param name
* new name
*/
public void setName(final String name) {
this.name = name;
}
public int getMaxZoom() {
return zoomRange.getMaxZoom();
}
public int getMinZoom() {
return zoomRange.getMinZoom();
}
public ZoomRange getZoomRange() {
return zoomRange;
}
public String buildPath(final int mapX, final int mapY, final int zoom) {
final int mx = mapX / getTileSize() & ((1 << zoom) - 1);
final int my = mapY / getTileSize();
final StringBuffer result = new StringBuffer(path);
result.append('/');
result.append(name);
result.append('_');
result.append(zoom);
result.append('/');
if (hashSize > 1) {
result.append((int) ((((long) mx) * getTileSize()) + my) % hashSize);
result.append('/');
}
result.append((tilesPerFile > 1) ? (mx / tpfx) : mx);
result.append('_');
result.append((tilesPerFile > 1) ? (my / tpfy) : my);
result.append('.');
result.append(fileExt);
// put dx and dy in filename, it's used as map tile ID
if (tilesPerFile > 1) {
result.append('|');
result.append(mx % tpfx);
result.append('_');
result.append(my % tpfy);
}
return result.toString();
}
// set parameters
/**
* Set tiles per file.
*
* @param tilesPerFile
* new value for tiles per file
*/
public void setTilesPerFile(final int tilesPerFile) {
this.tilesPerFile = tilesPerFile;
final int tpflog = Utils.log2(tilesPerFile);
tpfx = 1 << (tpflog / 2 + tpflog % 2);
tpfy = 1 << (tpflog / 2);
}
/**
* Set maximum zoom level.
*
* @param maxZoom
* new max zoom level
*/
public void setMaxZoom(final int maxZoom) {
this.zoomRange = new ZoomRange(this.zoomRange.getMinZoom(), maxZoom);
}
/**
* Set minimum zoom level.
*
* @param minZoom
* new min zoom level
*/
public void setMinZoom(final int minZoom) {
this.zoomRange = new ZoomRange(minZoom, this.zoomRange.getMaxZoom());
}
/**
* Set zoom range.
*
* @param zoomRange
* new zoom range
*/
public void setZoomRange(final ZoomRange zoomRange) {
this.zoomRange = zoomRange;
}
/**
* Set hash size.
*
* @param hashSize
* new hash size value.
*/
public void setHashSize(final int hashSize) {
this.hashSize = hashSize;
}
/**
* Get the hash size.
*
* @return hashSize, default 1
*/
public int getHashSize() {
return hashSize;
}
/**
* Get the number of tiles per file.
*
* @return tilesPerFile, default 1
*/
public int getTilesPerFile() {
return tilesPerFile;
}
/**
* Set extension for files used by stored maps.
*
* @param fileExt
* file extension
*/
public void setFileExtension(final String fileExt) {
this.fileExt = fileExt;
}
/**
* Get extension for files used by stored maps.
*
* @return file extension, default "mgm"
*/
public String getFileExtension() {
return fileExt;
}
public StoredMapConfig getConfig() {
return config;
}
public boolean isInitializeConf() {
return initializeConf;
}
}