001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.vfny.geoserver.wms.responses.map.kml;
006:
007: import com.vividsolutions.jts.geom.Envelope;
008: import org.geotools.geometry.jts.ReferencedEnvelope;
009: import org.geotools.map.MapLayer;
010: import org.geotools.referencing.crs.DefaultGeographicCRS;
011: import org.geotools.xml.transform.TransformerBase;
012: import org.geotools.xml.transform.Translator;
013: import org.vfny.geoserver.global.WMS;
014: import org.vfny.geoserver.util.Requests;
015: import org.vfny.geoserver.wms.WMSMapContext;
016: import org.xml.sax.ContentHandler;
017: import java.util.Map;
018: import java.util.logging.Logger;
019:
020: public class KMLSuperOverlayTransformer extends KMLTransformerBase {
021: /**
022: * logger
023: */
024: static Logger LOGGER = org.geotools.util.logging.Logging
025: .getLogger("org.geoserver.kml");
026:
027: /**
028: * the world bounds
029: */
030: final static ReferencedEnvelope world = new ReferencedEnvelope(
031: -180, 180, -90, 90, DefaultGeographicCRS.WGS84);
032:
033: /**
034: * resolutions
035: */
036: final static double[] resolutions = new double[100];
037:
038: static {
039: for (int i = 0; i < resolutions.length; i++) {
040: resolutions[i] = world.getWidth() / ((0x01 << i) * 256);
041: }
042: }
043:
044: /**
045: * The map context
046: */
047: final WMSMapContext mapContext;
048:
049: public KMLSuperOverlayTransformer(WMSMapContext mapContext) {
050: this .mapContext = mapContext;
051: setNamespaceDeclarationEnabled(false);
052: }
053:
054: public Translator createTranslator(ContentHandler handler) {
055: return new KMLSuperOverlayerTranslator(handler);
056: }
057:
058: class KMLSuperOverlayerTranslator extends KMLTranslatorSupport {
059: public KMLSuperOverlayerTranslator(ContentHandler contentHandler) {
060: super (contentHandler);
061: }
062:
063: public void encode(Object o) throws IllegalArgumentException {
064: MapLayer mapLayer = (MapLayer) o;
065:
066: //calculate closest resolution
067: ReferencedEnvelope extent = mapContext.getAreaOfInterest();
068: double resolution = Math.max(extent.getWidth() / 256d,
069: extent.getHeight() / 256d);
070:
071: //calculate the closest zoom level
072: int i = 1;
073:
074: for (; i < resolutions.length; i++) {
075: if (resolution > resolutions[i]) {
076: i--;
077:
078: break;
079: }
080: }
081:
082: LOGGER.fine("resolution = " + resolution);
083: LOGGER.fine("zoom level = " + i);
084:
085: //zoom out until the entire bounds requested is covered by a
086: //single tile
087: Envelope top = null;
088:
089: while (i > 0) {
090: resolution = resolutions[i];
091:
092: double tilelon = resolution * 256;
093: double tilelat = resolution * 256;
094:
095: double lon0 = extent.getMinX() - world.getMinX();
096: double lon1 = extent.getMaxX() - world.getMinX();
097:
098: int col0 = (int) Math.floor(lon0 / tilelon);
099: int col1 = (int) Math.floor((lon1 / tilelon) - 1E-9);
100:
101: double lat0 = extent.getMinY() - world.getMinY();
102: double lat1 = extent.getMaxY() - world.getMinY();
103:
104: int row0 = (int) Math.floor(lat0 / tilelat);
105: int row1 = (int) Math.floor((lat1 / tilelat) - 1E-9);
106:
107: if ((col0 == col1) && (row0 == row1)) {
108: double tileoffsetlon = world.getMinX()
109: + (col0 * tilelon);
110: double tileoffsetlat = world.getMinY()
111: + (row0 * tilelat);
112:
113: top = new Envelope(tileoffsetlon, tileoffsetlon
114: + tilelon, tileoffsetlat, tileoffsetlat
115: + tilelat);
116:
117: break;
118: } else {
119: i--;
120: }
121: }
122:
123: if (top == null) {
124: top = world;
125: }
126:
127: LOGGER.fine("request = " + extent);
128: LOGGER.fine("top level = " + top);
129:
130: //start document
131: if (isStandAlone()) {
132: start("kml");
133: }
134:
135: start("Document");
136:
137: //encode top level region
138: encodeRegion(top, 256, 1024);
139:
140: //encode the network links
141: if (top != world) {
142: //top left
143: Envelope e00 = new Envelope(top.getMinX(), top
144: .getMinX()
145: + (top.getWidth() / 2d), top.getMaxY()
146: - (top.getHeight() / 2d), top.getMaxY());
147:
148: //top right
149: Envelope e01 = new Envelope(e00.getMaxX(), top
150: .getMaxX(), e00.getMinY(), e00.getMaxY());
151:
152: //bottom left
153: Envelope e10 = new Envelope(e00.getMinX(), e00
154: .getMaxX(), top.getMinY(), e00.getMinY());
155:
156: //bottom right
157: Envelope e11 = new Envelope(e01.getMinX(), e01
158: .getMaxX(), e10.getMinY(), e10.getMaxY());
159:
160: encodeNetworkLink(e00, "00", mapLayer);
161: encodeNetworkLink(e01, "01", mapLayer);
162: encodeNetworkLink(e10, "10", mapLayer);
163: encodeNetworkLink(e11, "11", mapLayer);
164: } else {
165: //divide up horizontally by two
166: Envelope e0 = new Envelope(top.getMinX(), top.getMinX()
167: + (top.getWidth() / 2d), top.getMinY(), top
168: .getMaxY());
169: Envelope e1 = new Envelope(e0.getMaxX(), top.getMaxX(),
170: top.getMinY(), top.getMaxY());
171:
172: encodeNetworkLink(e0, "0", mapLayer);
173: encodeNetworkLink(e1, "1", mapLayer);
174: }
175:
176: //encode the ground overlay(s)
177: if (top == world) {
178: //special case for top since it does not line up as a propery
179: // tile -> split it in two
180: encodeGroundOverlay(mapLayer, i, new Envelope(-180, 0,
181: -90, 90));
182: encodeGroundOverlay(mapLayer, i, new Envelope(0, 180,
183: -90, 90));
184: } else {
185: //encode straight up
186: encodeGroundOverlay(mapLayer, i, top);
187: }
188:
189: //end document
190: end("Document");
191:
192: if (isStandAlone()) {
193: end("kml");
194: }
195: }
196:
197: void encodeGroundOverlay(MapLayer mapLayer, int drawOrder,
198: Envelope box) {
199: start("GroundOverlay");
200: element("drawOrder", "" + drawOrder);
201:
202: start("Icon");
203:
204: String href = KMLUtils.getMapUrl(mapContext, mapLayer, box,
205: new String[] { "width", "256", "height", "256" },
206: true);
207: element("href", href);
208: LOGGER.fine(href);
209: end("Icon");
210:
211: encodeLatLonBox(box);
212: end("GroundOverlay");
213: }
214:
215: void encodeRegion(Envelope box, int minLodPixels,
216: int maxLodPixels) {
217: //top level region
218: start("Region");
219:
220: start("Lod");
221: element("minLodPixels", "128");
222: //element("minLodPixels", "" + minLodPixels );
223: element("maxLodPixels", "" + maxLodPixels);
224: //element("maxLodPixels", "-1" );
225: end("Lod");
226:
227: encodeLatLonAltBox(box);
228:
229: end("Region");
230: }
231:
232: void encodeNetworkLink(Envelope box, String name,
233: MapLayer mapLayer) {
234: start("NetworkLink");
235: element("name", name);
236:
237: encodeRegion(box, 512, 2048);
238:
239: start("Link");
240:
241: String getMap = KMLUtils.getMapUrl(mapContext, mapLayer,
242: box, new String[] { "format",
243: KMLMapProducerFactory.MIME_TYPE, "width",
244: "256", "height", "256", "superoverlay",
245: "true" }, false);
246:
247: element("href", getMap);
248: LOGGER.fine("Network link " + name + ":" + getMap);
249:
250: element("viewRefreshMode", "onRegion");
251:
252: end("Link");
253:
254: end("NetworkLink");
255: }
256:
257: void encodeLatLonAltBox(Envelope box) {
258: start("LatLonAltBox");
259: encodeBox(box);
260: end("LatLonAltBox");
261: }
262:
263: void encodeLatLonBox(Envelope box) {
264: start("LatLonBox");
265: encodeBox(box);
266: end("LatLonBox");
267: }
268:
269: void encodeBox(Envelope box) {
270: element("north", String.valueOf(box.getMaxY()));
271: element("south", String.valueOf(box.getMinY()));
272: element("east", String.valueOf(box.getMaxX()));
273: element("west", String.valueOf(box.getMinX()));
274: }
275: }
276: }
|