package com.nutiteq.maps;
import henson.midp.Float11;
import java.io.IOException;
import java.util.Vector;
import com.mgmaps.utils.Tools;
import com.nutiteq.components.MapPos;
import com.nutiteq.components.MultiMapSingleConfig;
import com.nutiteq.components.TileMapBounds;
import com.nutiteq.components.WgsPoint;
import com.nutiteq.config.StoredMapConfig;
import com.nutiteq.fs.FileSystem;
import com.nutiteq.log.Log;
import com.nutiteq.ui.Copyright;
import com.nutiteq.ui.StringCopyright;
import com.nutiteq.utils.Utils;
public class MultiStoredMap extends StoredMap {
private static final String CONFIG_FILENAME = "cache.conf";
private final String baseBath;
private boolean needToInitialize = true;
private final String name;
private MultiMapSingleConfig[] areaConfigurations;
private final String fileExt;
private StoredMapConfig storedMapConfig;
// private ZoomRange zoomRange = new ZoomRange(Integer.MAX_VALUE,Integer.MIN_VALUE);
/**
* Enables to use multiple stored map packages.
* All packages must be in the same directory tree, as next level sub-directories, and must use the same map name
* example usage: mapComponent.setMap(new MultiStoredMap("OpenStreetMap", "root1/maps"));
*
* @param name Name of map. Must be the same as used in package directory structure
* @param basePath File System root directory for the map packages, e.g. "root1/maps"
*/
public MultiStoredMap(final String name, final String basePath) {
this(name,basePath,new StringCopyright(name));
}
/**
* Enables to use multiple stored map packages.
* All packages must be in the same directory tree, as next level sub-directories, and must use the same map name
* example usage: mapComponent.setMap(new MultiStoredMap("OpenStreetMap", "root1/maps",new StringCopyright("(c) OSM")));
*
* @param name Name of map. Must be the same as used in package directory structure
* @param basePath File System root directory for the map packages, e.g. "root1/maps"
* @param copyright CopyRight text/image for the overlay
*/
public MultiStoredMap(final String name, final String basePath, final Copyright copyright ) {
super(name, "", false, copyright);
this.name = name;
String p = basePath;
if (p.startsWith("/")) {
p = p.substring(1);
}
this.baseBath = p;
this.fileExt = "mgm";
}
public void initializeConfigUsingFs(final FileSystem fs) {
final Vector confs = new Vector();
try {
final Vector listFiles = fs.listFiles(baseBath);
for (int i = 0; i < listFiles.size(); i++) {
final String file = (String) listFiles.elementAt(i);
Log.debug(file);
if (file.startsWith(".")) {
continue;
}
final String maybeTilesDir = baseBath + "/" + file;
if (fs.isDirectory(maybeTilesDir) && containsCacheConf(fs, maybeTilesDir)) {
Log.debug("found cache conf in " + maybeTilesDir);
final MultiMapSingleConfig conf = loadConfig(fs, maybeTilesDir);
if (conf != null && conf.isValid()) {
Log.debug("adding conf for: " + conf.toString());
confs.addElement(conf);
}
}
}
} catch (final IOException e) {
Log.error("MultiStoredMap: " + e.getClass() + " : " + e.getMessage());
Log.printStackTrace(e);
}
Log.debug("loaded " + confs.size() + " confs");
areaConfigurations = new MultiMapSingleConfig[confs.size()];
confs.copyInto(areaConfigurations);
if (areaConfigurations.length == 0) {
throw new RuntimeException("No cofigurations found in " + baseBath);
}
needToInitialize = false;
}
public String buildPath(final int mapX, final int mapY, final int zoom) {
if (areaConfigurations != null) {
for (int i = 0; i < areaConfigurations.length; i++) {
final MultiMapSingleConfig config = areaConfigurations[i];
if (config.contains(mapX, mapY, zoom, getTileSize())) {
return buildPath(config.getTilesDir(), mapX, mapY, zoom,
config.getHashSize(), config.getTilesPerFile(),
config.getTpfx(), config.getTpfy());
}
}
}else{
Log.debug("areaConfigurations is null");
}
Log.debug(new StringBuffer("could_not_map_").append(mapX).append("_").append(mapY).append("_")
.append(zoom).toString());
return "";
}
//TODO jaanus : refactor this
public String buildPath(final String path, final int mapX, final int mapY, final int zoom,
final int hashSize, final int tilesPerFile, final int tpfx, final int tpfy) {
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();
}
public StoredMapConfig getConfig() {
//TODO jaanus : needed for reading in ReadStoredMapTileTask. change it!
if (storedMapConfig == null) {
storedMapConfig = new StoredMapConfig(areaConfigurations[0].getTilesPerFile(),
areaConfigurations[0].getTpfx(), areaConfigurations[0].getTpfy(), areaConfigurations[0]
.getTilesPerFile());
}
return storedMapConfig;
}
private MultiMapSingleConfig loadConfig(final FileSystem fs, final String tilesDir)
throws IOException {
final MultiMapSingleConfig result = new MultiMapSingleConfig(tilesDir, name);
final Vector areas = new Vector();
final byte[] data = fs.readFile(tilesDir + "/" + CONFIG_FILENAME);
final String sdata = new String(data);
Log.debug("read config >> " + sdata);
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) {
result.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) {
result.setHashSize(hs);
} else {
throw new IOException("Invalid hash_size");
}
}
} else if (lines[i].indexOf(":") > 0) {
final String[] areaTokens = Tools.split(lines[i].trim(), ':', false, 3);
if (areaTokens.length != 3) {
continue;
}
final String zoomLevels = areaTokens[0];
final int minZoom = Integer.parseInt(zoomLevels.substring(0, zoomLevels.indexOf('-')));
final int maxZoom = Integer.parseInt(zoomLevels.substring(zoomLevels.indexOf('-') + 1));
if(minZoom<result.getMinZoom()){
result.setMinZoom(minZoom);
}
if(maxZoom>result.getMaxZoom()){
result.setMaxZoom(maxZoom);
}
final WgsPoint areaMin = WgsPoint.parsePoint(WgsPoint.FORMAT_LAT_LON, areaTokens[1], ",");
final WgsPoint areaMax = WgsPoint.parsePoint(WgsPoint.FORMAT_LAT_LON, areaTokens[2], ",");
for (int z = minZoom; z <= maxZoom; z++) {
final MapPos min = wgsToMapPos(new WgsPoint(areaMin.getLon(), areaMax.getLat())
.toInternalWgs(), z);
final MapPos max = wgsToMapPos(new WgsPoint(areaMax.getLon(), areaMin.getLat())
.toInternalWgs(), z);
areas.addElement(new TileMapBounds(min, max));
}
}
}
Log.debug("found " + areas.size() + " areas for " + tilesDir);
final TileMapBounds[] bounds = new TileMapBounds[areas.size()];
areas.copyInto(bounds);
result.setTileBounds(bounds);
return result;
}
private boolean containsCacheConf(final FileSystem fs, final String maybeTilesDir)
throws IOException {
final Vector files = fs.listFiles(maybeTilesDir);
for (int i = 0; i < files.size(); i++) {
final String file = (String) files.elementAt(i);
if (CONFIG_FILENAME.equals(file)) {
return true;
}
}
return false;
}
public boolean isInitializeConf() {
return needToInitialize;
}
/*
public int getMaxZoom() {
return zoomRange.getMaxZoom();
}
public int getMinZoom() {
return zoomRange.getMinZoom();
}
public ZoomRange getZoomRange() {
return zoomRange;
}
*/
public int[] getSupportedZoomLevels() {
if (areaConfigurations==null){
return new int[0];
}
int levels = 0; // will have bitmap of supported levels
for (int i=0;i<areaConfigurations.length;i++){
TileMapBounds[] bounds = areaConfigurations[i].getTileBounds();
for (int j=0;j<bounds.length;j++){
// Log.debug("found zoom level " + bounds[j].getZoomLevel());
levels|=(int) Float11.pow(2,bounds[j].getZoomLevel()); // set n'th bit
}
}
// number of bits set?
byte c = 0;
for(int i=0;i<32;i++){ // 32 bits per int
if((levels & (1<<i))>0){
c++;
}
}
// create result array
int[] levelsArray = new int[c];
byte j=0;
for(int i=0;i<32;i++){
if((levels & (1<<i)) > 0){
levelsArray[j++] = i;
}
}
return levelsArray;
}
}