//**********************************************************************
//
//<copyright>
//
//BBN Technologies
//10 Moulton Street
//Cambridge, MA 02138
//(617) 873-8000
//
//Copyright (C) BBNT Solutions LLC. All rights reserved.
//
//</copyright>
//**********************************************************************
//
//$Source:
///cvs/darwars/ambush/aar/src/com/bbn/ambush/mission/MissionHandler.java,v
//$
//$RCSfile: MissionHandler.java,v $
//$Revision: 1.10 $
//$Date: 2004/10/21 20:08:31 $
//$Author: dietrick $
//
//**********************************************************************
package com.bbn.openmap.dataAccess.mapTile;
import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import com.bbn.openmap.Environment;
import com.bbn.openmap.I18n;
import com.bbn.openmap.Layer;
import com.bbn.openmap.proj.Proj;
import com.bbn.openmap.util.ComponentFactory;
import com.bbn.openmap.util.PropUtils;
/**
* The ZoomLevelInfo class is used by the TileMaker and handles how tiles are
* defined and created for a particular zoom level. It handles any bounds
* restrictions, what layers should be rendered at this zoom level, and the path
* to the tiles from the root directory. The properties for this component are:
* <p>
*
* <pre>
* #Needed for property file creation of TileMaker
* zoomMarker.class=com.bbn.openmap.image.ZoomLevelInfo
* #Optional, to limit tile areas created, in sets of 4, must be in lat,lon order.
* zoomMarker.bounds=lat lon lat lon
* zoomMarker.description=Tiles for zoom level 4
* #Marker names for layers to be rendered, the property prefixes for the layers held by TileMaker
* zoomMarker.layers=lakes shape
* zoomMarker.name=ZoomLayerInfo 4
* zoomMarker.zoomLevel=4
* zoomMarker.range=0
*
* </pre>
*
* Note that the zoomMarker keyword should be stored in the TileMaker zoomLevels
* property list.
*
* @author dietrick
*/
public class ZoomLevelMaker extends ZoomLevelInfo {
public final static String BOUNDS_PROPERTY = "bounds";
public final static String NAME_PROPERTY = "name";
public final static String DESCRIPTION_PROPERTY = "description";
public final static String ZOOM_LEVEL_PROPERTY = "zoomLevel";
public final static String LAYERS_PROPERTY = "layers";
public final static String RANGE_PROPERTY = "range";
public final static int RANGE_NOT_SET = -1;
protected String name;
protected String description;
protected List<String> layers;
protected List<Layer> layerList;
/**
* The range should be equal or smaller than the zoom level, describing how
* many other zoom levels should be created from the tiles created for this
* zoom level (scaling). If the range is -1, then it hasn't been set and the
* zoom level will be returned for this value.
*/
protected int range = RANGE_NOT_SET;
protected List<Rectangle2D> bounds = new LinkedList<Rectangle2D>();
/**
* Need this to create it from properties
*/
public ZoomLevelMaker() {
}
/**
* Create a ZoomLevelInfo object that contains information about what map
* tiles should be created for this zoom level.
*
* @param name
* @param desc
* @param zoomLevel
*/
public ZoomLevelMaker(String name, String desc, int zoomLevel) {
this.name = name;
this.description = desc;
this.zoomLevel = zoomLevel;
}
public void setProperties(String prefix, Properties props) {
super.setProperties(prefix, props);
prefix = PropUtils.getScopedPropertyPrefix(prefix);
name = props.getProperty(prefix + NAME_PROPERTY, name);
description = props.getProperty(prefix + DESCRIPTION_PROPERTY, description);
zoomLevel = PropUtils.intFromProperties(props, prefix + ZOOM_LEVEL_PROPERTY, zoomLevel);
range = PropUtils.intFromProperties(props, prefix + RANGE_PROPERTY, range);
String boundsPropertyStrings = props.getProperty(prefix + BOUNDS_PROPERTY);
if (boundsPropertyStrings != null) {
Vector<String> boundsStrings = PropUtils.parseSpacedMarkers(boundsPropertyStrings);
int count = 0;
while (boundsStrings != null && !boundsStrings.isEmpty()
&& boundsStrings.size() >= count + 4) {
double lat1 = Double.parseDouble(boundsStrings.get(count));
double lon1 = Double.parseDouble(boundsStrings.get(count + 1));
double lat2 = Double.parseDouble(boundsStrings.get(count + 2));
double lon2 = Double.parseDouble(boundsStrings.get(count + 3));
bounds.add(createProperBounds(lon1, lat1, lon2, lat2));
count += 4;
}
}
String layerPropertyStrings = props.getProperty(prefix + LAYERS_PROPERTY);
if (layerPropertyStrings != null) {
Vector<String> layerStrings = PropUtils.parseSpacedMarkers(layerPropertyStrings);
if (layerStrings != null && !layerStrings.isEmpty()) {
getLayers().addAll(layerStrings);
}
}
}
public Properties getProperties(Properties props) {
props = super.getProperties(props);
String prefix = PropUtils.getScopedPropertyPrefix(this);
props.put(prefix + ComponentFactory.ClassNameProperty, getClass().getName());
props.put(prefix + NAME_PROPERTY, PropUtils.unnull(name));
props.put(prefix + DESCRIPTION_PROPERTY, PropUtils.unnull(description));
props.put(prefix + ZOOM_LEVEL_PROPERTY, Integer.toString(zoomLevel));
if (range != RANGE_NOT_SET) {
props.put(prefix + RANGE_PROPERTY, Integer.toString(range));
}
StringBuffer buf = new StringBuffer();
for (String layerMarkerName : layers) {
buf.append(layerMarkerName).append(" ");
}
props.put(prefix + LAYERS_PROPERTY, buf.toString().trim());
buf = new StringBuffer();
for (Rectangle2D bound : getBounds()) {
double x = bound.getX();
double y = bound.getY();
buf.append(y).append(" ").append(x).append(" ").append((y + bound.getHeight())).append(" ").append((x + bound.getWidth())).append(" ");
}
props.put(prefix + BOUNDS_PROPERTY, buf.toString().trim());
return props;
}
public Properties getPropertyInfo(Properties props) {
props = super.getPropertyInfo(props);
I18n i18n = Environment.getI18n();
PropUtils.setI18NPropertyInfo(i18n, props, com.bbn.openmap.dataAccess.mapTile.ZoomLevelMaker.class, NAME_PROPERTY, "Name", "Name for zoom level tiles", null);
PropUtils.setI18NPropertyInfo(i18n, props, com.bbn.openmap.dataAccess.mapTile.ZoomLevelMaker.class, DESCRIPTION_PROPERTY, "Descroption", "Description for zoom level tiles", null);
PropUtils.setI18NPropertyInfo(i18n, props, com.bbn.openmap.dataAccess.mapTile.ZoomLevelMaker.class, ZOOM_LEVEL_PROPERTY, "Zoom Level (1-20)", "Number for zoom level", null);
PropUtils.setI18NPropertyInfo(i18n, props, com.bbn.openmap.dataAccess.mapTile.ZoomLevelMaker.class, BOUNDS_PROPERTY, "Bounds", "Bounds for tile creation (lat lon lat lon)", null);
PropUtils.setI18NPropertyInfo(i18n, props, com.bbn.openmap.dataAccess.mapTile.ZoomLevelMaker.class, LAYERS_PROPERTY, "Layers", "Space separated marker names for layers used in tiles.", null);
PropUtils.setI18NPropertyInfo(i18n, props, com.bbn.openmap.dataAccess.mapTile.ZoomLevelMaker.class, RANGE_PROPERTY, "Range", "Zoom level to create tiles down to, using the tiles created at this zoom level.", null);
return props;
}
/**
* @return the name of this zoom level info.
*/
public String getName() {
return name;
}
/**
* Set the name of this zoom level info.
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* Get the description of this zoom level.
*
* @return string description of zoom level
*/
public String getDescription() {
return description;
}
/**
* Set the description for this zoom level.
*
* @param description
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Get the current marker name (property prefix) for layers considered for
* this zoom level.
*
* @return layers used for zoom level
*/
public List<String> getLayers() {
if (layers == null) {
layers = new LinkedList<String>();
}
return layers;
}
/**
* Set the marker names (property prefixes) for the layers that should be
* considered for this zoom level.
*
* @param layers
*/
public void setLayers(List<String> layers) {
this.layers = layers;
}
/**
* Get the List of Layer Objects, if it's been set.
*
* @return List of Layers
*/
public List<Layer> getLayerList() {
return layerList;
}
/**
* Set a List of Layer objects. If this is set, the layer marker names won't
* be used. This is a more programmatic approach, rather than using
* properties and property prefixes of the layers to set them for this zoom
* level.
*
* @param layerList
*/
public void setLayerList(List<Layer> layerList) {
this.layerList = layerList;
}
public void setZoomLevel(int zoomLevel) {
super.setZoomLevel(zoomLevel);
scale = -1;
}
/**
* Get bounds, defined as world coordinates (i.e. lat/lon). Does not cross
* over date line.
*
* @return the bounds for this zoom level
*/
public List<Rectangle2D> getBounds() {
return bounds;
}
/**
* Set world coordinate bounds for tiles to be created. Should not cross
* over date line.
*
* @param bounds No checks performed - x, y have to be the min, height and
* width must not exceed boundary limits (lat +/- 85, lon +/- 180)
* when added to x, y.
*/
public void addBounds(Rectangle2D bounds) {
this.bounds.add(bounds);
}
/**
* Get the bounds as defined as UV tile limits.
*
* @return a List of Rectangle2D of uv bounds for this zoom level
*/
public List<Rectangle2D> getUVBounds(MapTileCoordinateTransform mtct, int zoomLevel) {
List<Rectangle2D> ret = new LinkedList<Rectangle2D>();
for (Rectangle2D bounds : getBounds()) {
ret.add(getUVBounds(bounds, mtct, zoomLevel));
}
if (ret.isEmpty()) {
int etc = getEdgeTileCount();
ret.add(new Rectangle2D.Double(0, 0, etc, etc));
}
return ret;
}
/**
* Create a bounding rectangle given the four coordinates, where the upper
* left corner of the rectangle is the minimum x, y values and the width and
* height are the difference between xs and ys.
*
* @param x1
* @param y1
* @param x2
* @param y2
* @return Rect2D, properly constructed from coordinates
*/
public Rectangle2D createProperBounds(double x1, double y1, double x2, double y2) {
double x = Math.min(x1, x2);
double y = Math.min(y1, y2);
double w = Math.abs(x1 - x2);
double h = Math.abs(y1 - y2);
return new Rectangle2D.Double(x, y, w, h);
}
/**
* Get the range of this ZoomLevelMaker.
*
* @return the range set for this zlm, or the current zoom level if the
* range has not been set.
*/
public int getRange() {
if (range <= RANGE_NOT_SET) {
return getZoomLevel();
}
return range;
}
public void setRange(int range) {
this.range = range;
}
/**
* @param uvx
* @param uvy
* @param mapTileMaker
* @param proj
* @return byte array of the tile image, raw image bytes.
*/
public byte[] makeTile(double uvx, double uvy, MapTileMaker mapTileMaker, Proj proj) {
if (layerList != null) {
return mapTileMaker.makeTile(uvx, uvy, getZoomLevel(), layerList, proj, mapTileMaker.getBackground());
}
return mapTileMaker.makeTile(uvx, uvy, this, proj);
}
}