/* Copyright (c) 2001 - 2011 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.kml;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.wfs.kvp.BBoxKvpParser;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.WKTReader2;
import org.geotools.util.logging.Logging;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
/**
* Data object to hold the KML <a
* href="http://code.google.com/apis/kml/documentation/kmlreference.html#lookat">lookAt<a>
* properties as they come from the WMS GetMap's FORMAT_OPTIONS vendor specific parameter.
* <p>
* The following parameters are parsed at construction time and held by an instance of this class:
* <ul>
* <li>LOOKATBBOX: {@code xmin,ymin,xmax,ymax}
* <li>LOOKATGEOM: {@code Geometry WKT}
* <li>ALTITUDE: Double
* <li>HEADING: Double
* <li>TILT: Double
* <li>RANGE: Double
* <li>ALTITUDEMODE: String literal. One of <clampToGround|relativeToGround|absolute>
* </ul>
* All of them are optional, and {@code null} will be returned by it's matching accessor method if
* not provided. LOOKATBBOX and LOOKATGEOM are mutually exclusive, and LOOKATBBOX takes precedence
* over LOOKATGEOM.
*
* Both LOOKATBBOX and LOOKATGEOM allow to define the area where the KML lookAt should be pointing
* at. If none is provided, GeoServer will use the aggregated lat lon bounding box of all the
* requested layers as it normally does.
* </p>
*
* @author Gabriel Roldan
*
*/
public class KMLLookAt {
private static final Logger LOGGER = Logging.getLogger(KMLLookAt.class);
private static final GeometryFactory gfac = new GeometryFactory();
private static final String KEY_BBOX = "LOOKATBBOX";
private static final String KEY_LOOKAT = "LOOKATGEOM";
private static final String KEY_ALTITUDE = "ALTITUDE";
private static final String KEY_HEADING = "HEADING";
private static final String KEY_TILT = "TILT";
private static final String KEY_RANGE = "RANGE";
private static final String KEY_ALTITUDEMODE = "ALTITUDEMODE";
public static enum AltitudeMode {
clampToGround, relativeToGround, absolute;
}
private Geometry lookAt;
private Double altitude;
private Double heading;
private Double tilt;
private Double range;
private AltitudeMode altitudeMode;
@SuppressWarnings("unchecked")
public KMLLookAt() {
this(Collections.EMPTY_MAP);
}
/**
* Creates a new KMLLookAt object by parsing the vendor specific parameters out of the provided
* map, using the properties defined in the class javadoc above as Map keys.
*
* @param options
*/
public KMLLookAt(final Map<String, Object> options) {
this.lookAt = parseLookAtBBOX(options.get(KEY_BBOX));
if (this.lookAt == null) {
this.lookAt = parseLookAt(options.get(KEY_LOOKAT));
}
this.altitude = parseDouble(options.get(KEY_ALTITUDE));
this.heading = parseDouble(options.get(KEY_HEADING));
this.tilt = parseDouble(options.get(KEY_TILT));
this.range = parseDouble(options.get(KEY_RANGE));
this.altitudeMode = parseAltitudeMode(options.get(KEY_ALTITUDEMODE));
}
private Geometry parseLookAtBBOX(Object object) {
Geometry bbox = null;
if (null != object) {
Envelope env;
try {
BBoxKvpParser parser = new BBoxKvpParser();
env = (Envelope) parser.parse(String.valueOf(object));
bbox = JTS.toGeometry(env);
} catch (Exception e) {
LOGGER.log(Level.FINER, e.getMessage(), e);
}
}
return bbox;
}
private Geometry parseLookAt(final Object object) {
Geometry geom = null;
if (object != null) {
String geomWKT = String.valueOf(object);
try {
WKTReader2 reader = new WKTReader2(gfac);
geom = reader.read(geomWKT);
} catch (Exception e) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Error parsing " + KEY_LOOKAT + " KML format option: "
+ e.getMessage() + ". Argument WKT: '" + geomWKT + "'");
}
}
}
return geom;
}
private Double parseDouble(final Object object) {
Double parsed = null;
if (object != null) {
try {
parsed = Double.valueOf(String.valueOf(object));
} catch (NumberFormatException ignored) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Not a number in KML format options: '" + object + "'");
}
}
}
return parsed;
}
private AltitudeMode parseAltitudeMode(final Object object) {
AltitudeMode mode = null;
if (object != null) {
try {
mode = AltitudeMode.valueOf(String.valueOf(object));
} catch (IllegalArgumentException ignore) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Illegal value for KML format option 'altitudeMode': '" + object
+ "'. Expected one of " + AltitudeMode.values());
}
}
}
return mode;
}
public Geometry getLookAt() {
return lookAt;
}
public Double getAltitude() {
return altitude;
}
public Double getHeading() {
return heading;
}
public Double getTilt() {
return tilt;
}
public Double getRange() {
return range;
}
public AltitudeMode getAltitudeMode() {
return altitudeMode;
}
}