/* 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.nio.charset.Charset;
import java.util.logging.Logger;
import org.geoserver.kml.KMLLookAt.AltitudeMode;
import org.geotools.xml.transform.TransformerBase;
import org.geotools.xml.transform.Translator;
import org.xml.sax.ContentHandler;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
/**
* Transformer for a {@link KMLLookAt}
*
* @author Gabriel Roldan
*/
class KMLLookAtTransformer extends TransformerBase {
static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geoserver.kml");
private Envelope targetBounds;
/**
* @param targetBounds
* the bounds of the region to look at, overridden by the
* {@link KMLLookAt#getLookAt()} geometry if present.
*
*/
public KMLLookAtTransformer(final Envelope targetBounds, final int indentation, Charset charset) {
this.targetBounds = targetBounds;
setIndentation(indentation);
setEncoding(charset);
}
@Override
public Translator createTranslator(final ContentHandler handler) {
return new LookAtTranslator(handler);
}
private class LookAtTranslator extends TranslatorSupport {
public LookAtTranslator(ContentHandler handler) {
super(handler, null, null);
}
public void encode(Object o) throws IllegalArgumentException {
final KMLLookAt lookAt = (KMLLookAt) o;
Envelope lookAtEnvelope = targetBounds;
if (lookAt.getLookAt() != null) {
Geometry geometry = lookAt.getLookAt();
lookAtEnvelope = geometry.getEnvelopeInternal();
}
if (lookAtEnvelope.isNull()) {
return;
}
double lon1 = lookAtEnvelope.getMinX();
double lat1 = lookAtEnvelope.getMinY();
double lon2 = lookAtEnvelope.getMaxX();
double lat2 = lookAtEnvelope.getMaxY();
double R_EARTH = 6.371 * 1000000; // meters
double VIEWER_WIDTH = 22 * Math.PI / 180; // The field of view of the google maps
// camera, in radians
double[] p1 = getRect(lon1, lat1, R_EARTH);
double[] p2 = getRect(lon2, lat2, R_EARTH);
double[] midpoint = new double[] { (p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2,
(p1[2] + p2[2]) / 2 };
midpoint = getGeographic(midpoint[0], midpoint[1], midpoint[2]);
Double distance = lookAt.getRange();
if (null == distance) {
distance = distance(p1, p2);
}
double height = distance / (2 * Math.tan(VIEWER_WIDTH));
LOGGER.fine("lat1: " + lat1 + "; lon1: " + lon1);
LOGGER.fine("lat2: " + lat2 + "; lon2: " + lon2);
LOGGER.fine("latmid: " + midpoint[1] + "; lonmid: " + midpoint[0]);
final Double tilt = lookAt.getTilt() == null ? Double.valueOf(0) : lookAt.getTilt();
final Double heading = lookAt.getHeading() == null ? Double.valueOf(0) : lookAt
.getHeading();
final Double altitude = lookAt.getAltitude() == null ? Double.valueOf(height) : lookAt
.getAltitude();
final KMLLookAt.AltitudeMode altMode = lookAt.getAltitudeMode() == null ? AltitudeMode.clampToGround
: lookAt.getAltitudeMode();
start("LookAt");
element("longitude", String.valueOf(midpoint[0]));
element("latitude", String.valueOf(midpoint[1]));
element("altitude", String.valueOf(altitude));
element("range", String.valueOf(distance));
element("tilt", String.valueOf(tilt));
element("heading", String.valueOf(heading));
element("altitudeMode", String.valueOf(altMode));
end("LookAt");
}
private double[] getRect(double lat, double lon, double radius) {
double theta = (90 - lat) * Math.PI / 180;
double phi = (90 - lon) * Math.PI / 180;
double x = radius * Math.sin(phi) * Math.cos(theta);
double y = radius * Math.sin(phi) * Math.sin(theta);
double z = radius * Math.cos(phi);
return new double[] { x, y, z };
}
private double[] getGeographic(double x, double y, double z) {
double theta, phi, radius;
radius = distance(new double[] { x, y, z }, new double[] { 0, 0, 0 });
theta = Math.atan2(Math.sqrt(x * x + y * y), z);
phi = Math.atan2(y, x);
double lat = 90 - (theta * 180 / Math.PI);
double lon = 90 - (phi * 180 / Math.PI);
return new double[] { (lon > 180 ? lon - 360 : lon), lat, radius };
}
private double distance(double[] p1, double[] p2) {
double dx = p1[0] - p2[0];
double dy = p1[1] - p2[1];
double dz = p1[2] - p2[2];
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
}
}