001: package org.vfny.geoserver.wms.responses.map.kml;
002:
003: import java.io.IOException;
004: import java.util.logging.Logger;
005:
006: import org.geoserver.wms.util.WMSRequests;
007: import org.geotools.xml.transform.TransformerBase;
008: import org.geotools.xml.transform.Translator;
009: import org.vfny.geoserver.global.MapLayerInfo;
010: import org.vfny.geoserver.wms.requests.GetMapRequest;
011: import org.xml.sax.ContentHandler;
012:
013: import com.vividsolutions.jts.geom.Envelope;
014:
015: /**
016: * Encodes a KML document contianing a network link.
017: * <p>
018: * This transformer transforms a {@link GetMapRequest} object.
019: * </p>
020: *
021: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
022: *
023: */
024: public class KMLNetworkLinkTransformer extends TransformerBase {
025:
026: /**
027: * logger
028: */
029: static Logger LOGGER = org.geotools.util.logging.Logging
030: .getLogger("org.geoserver.kml");
031:
032: /**
033: * flag controlling wether the network link should be a super overlay.
034: */
035: boolean encodeAsRegion = false;
036:
037: public Translator createTranslator(ContentHandler handler) {
038: return new KMLNetworkLinkTranslator(handler);
039: }
040:
041: public void setEncodeAsRegion(boolean encodeAsRegion) {
042: this .encodeAsRegion = encodeAsRegion;
043: }
044:
045: class KMLNetworkLinkTranslator extends TranslatorSupport {
046:
047: public KMLNetworkLinkTranslator(ContentHandler contentHandler) {
048: super (contentHandler, null, null);
049: }
050:
051: public void encode(Object o) throws IllegalArgumentException {
052: GetMapRequest request = (GetMapRequest) o;
053:
054: start("kml");
055: start("Folder");
056:
057: if (encodeAsRegion) {
058: encodeAsSuperOverlay(request);
059: } else {
060: encodeAsOverlay(request);
061: }
062:
063: //look at
064: encodeLookAt(request);
065:
066: end("Folder");
067: end("kml");
068: }
069:
070: protected void encodeAsSuperOverlay(GetMapRequest request) {
071: MapLayerInfo[] layers = request.getLayers();
072: for (int i = 0; i < layers.length; i++) {
073: start("NetworkLink");
074: element("name", layers[i].getName());
075: element("open", "1");
076: element("visibility", "1");
077:
078: //region
079: start("Region");
080:
081: Envelope bbox = request.getBbox();
082: start("LatLonAltBox");
083: element("north", "" + bbox.getMaxY());
084: element("south", "" + bbox.getMinY());
085: element("east", "" + bbox.getMaxX());
086: element("west", "" + bbox.getMinX());
087: end("LatLonAltBox");
088:
089: start("Lod");
090: element("minLodPixels", "256");
091: element("maxLodPixels", "-1");
092: end("Lod");
093:
094: end("Region");
095:
096: //link
097: start("Link");
098:
099: String href = WMSRequests.getGetMapUrl(request,
100: layers[i].getName(), null, null, null);
101: start("href");
102: cdata(href);
103: end("href");
104:
105: element("viewRefreshMode", "onRegion");
106: end("Link");
107:
108: end("NetworkLink");
109: }
110: }
111:
112: protected void encodeAsOverlay(GetMapRequest request) {
113: MapLayerInfo[] layers = request.getLayers();
114: for (int i = 0; i < layers.length; i++) {
115: start("NetworkLink");
116: element("name", layers[i].getName());
117: element("open", "1");
118: element("visibility", "1");
119:
120: start("Url");
121:
122: //set bbox to null so its not included in the request, google
123: // earth will append it for us
124: request.setBbox(null);
125:
126: String href = WMSRequests.getGetMapUrl(request,
127: layers[i].getName(), null, null, null);
128: start("href");
129: cdata(href);
130: end("href");
131:
132: element("viewRefreshMode", "onStop");
133: element("viewRefreshTime", "1");
134: end("Url");
135:
136: end("NetworkLink");
137: }
138: }
139:
140: private void encodeLookAt(GetMapRequest request) {
141:
142: Envelope e = new Envelope();
143: e.setToNull();
144:
145: for (int i = 0; i < request.getLayers().length; i++) {
146: MapLayerInfo layer = request.getLayers()[i];
147:
148: Envelope b = null;
149: try {
150: b = request.getLayers()[i].getLatLongBoundingBox();
151: } catch (IOException e1) {
152: LOGGER.warning("Unable to calculate bounds for "
153: + layer.getName());
154: continue;
155: }
156: if (e.isNull()) {
157: e.init(b);
158: } else {
159: e.expandToInclude(b);
160: }
161: }
162:
163: if (e.isNull()) {
164: return;
165: }
166:
167: double lon1 = e.getMinX();
168: double lat1 = e.getMinY();
169: double lon2 = e.getMaxX();
170: double lat2 = e.getMaxY();
171:
172: double R_EARTH = 6.371 * 1000000; // meters
173: double VIEWER_WIDTH = 22 * Math.PI / 180; // The field of view of the google maps camera, in radians
174: double[] p1 = getRect(lon1, lat1, R_EARTH);
175: double[] p2 = getRect(lon2, lat2, R_EARTH);
176: double[] midpoint = new double[] { (p1[0] + p2[0]) / 2,
177: (p1[1] + p2[1]) / 2, (p1[2] + p2[2]) / 2 };
178:
179: midpoint = getGeographic(midpoint[0], midpoint[1],
180: midpoint[2]);
181:
182: double distance = distance(p1, p2);
183:
184: double height = distance / (2 * Math.tan(VIEWER_WIDTH));
185:
186: LOGGER.fine("lat1: " + lat1 + "; lon1: " + lon1);
187: LOGGER.fine("lat2: " + lat2 + "; lon2: " + lon2);
188: LOGGER.fine("latmid: " + midpoint[1] + "; lonmid: "
189: + midpoint[0]);
190:
191: start("LookAt");
192: element("longitude", "" + midpoint[0]);
193: element("latitude", "" + midpoint[1]);
194: element("altitude", "0");
195: element("range", "" + distance);
196: element("tilt", "0");
197: element("heading", "0");
198: element("altitudeMode", "clampToGround");
199: end("LookAt");
200: }
201:
202: private double[] getRect(double lat, double lon, double radius) {
203: double theta = (90 - lat) * Math.PI / 180;
204: double phi = (90 - lon) * Math.PI / 180;
205:
206: double x = radius * Math.sin(phi) * Math.cos(theta);
207: double y = radius * Math.sin(phi) * Math.sin(theta);
208: double z = radius * Math.cos(phi);
209: return new double[] { x, y, z };
210: }
211:
212: private double[] getGeographic(double x, double y, double z) {
213: double theta, phi, radius;
214: radius = distance(new double[] { x, y, z }, new double[] {
215: 0, 0, 0 });
216: theta = Math.atan2(Math.sqrt(x * x + y * y), z);
217: phi = Math.atan2(y, x);
218:
219: double lat = 90 - (theta * 180 / Math.PI);
220: double lon = 90 - (phi * 180 / Math.PI);
221:
222: return new double[] { (lon > 180 ? lon - 360 : lon), lat,
223: radius };
224: }
225:
226: private double distance(double[] p1, double[] p2) {
227: double dx = p1[0] - p2[0];
228: double dy = p1[1] - p2[1];
229: double dz = p1[2] - p2[2];
230: return Math.sqrt(dx * dx + dy * dy + dz * dz);
231: }
232: }
233: }
|