/******************************************************************************* * Copyright (c) MOBAC developers * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package mobac.program.atlascreators; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.text.NumberFormat; import java.util.zip.ZipOutputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import mobac.exceptions.MapCreationException; import mobac.program.annotations.AtlasCreatorName; import mobac.program.atlascreators.tileprovider.TileProvider; import mobac.program.interfaces.LayerInterface; import mobac.program.interfaces.MapInterface; import mobac.program.interfaces.MapSpace; import mobac.program.interfaces.TileImageDataWriter; import mobac.program.tiledatawriter.TileImageJpegDataWriter; import mobac.utilities.Utilities; import mobac.utilities.stream.ZipStoreOutputStream; import org.w3c.dom.Document; import org.w3c.dom.Element; @AtlasCreatorName(value = "Google Earth Overlay (KMZ)", type = "GoogleEarthRasterOverlay") public class GoogleEarthOverlay extends AbstractPlainImage { protected File mapDir; protected String cleanedMapName; protected File kmzFile = null; protected ZipStoreOutputStream kmzOutputStream = null; private Document kmlDoc = null; private Element groundOverlayRoot = null; @Override public void initLayerCreation(LayerInterface layer) throws IOException { super.initLayerCreation(layer); Utilities.mkDirs(atlasDir); kmzFile = new File(atlasDir, layer.getName() + ".kmz"); kmzOutputStream = new ZipStoreOutputStream(kmzFile); kmzOutputStream.setMethod(ZipOutputStream.STORED); try { if (layer.getMapCount() <= 1) initKmlDoc(null); else initKmlDoc(layer.getName()); } catch (ParserConfigurationException e) { throw new IOException(e); } } @Override public void finishLayerCreation() throws IOException { try { writeKmlToZip(); } catch (Exception e) { throw new IOException(e); } Utilities.closeStream(kmzOutputStream); kmzOutputStream = null; kmzFile = null; super.finishLayerCreation(); } @Override public void abortAtlasCreation() throws IOException { Utilities.closeStream(kmzOutputStream); kmzOutputStream = null; kmzFile = null; super.abortAtlasCreation(); } @Override public void initializeMap(MapInterface map, TileProvider mapTileProvider) { super.initializeMap(map, mapTileProvider); mapDir = new File(atlasDir, map.getLayer().getName()); cleanedMapName = map.getName(); cleanedMapName = cleanedMapName.replaceAll("[[^\\p{Alnum}-_]]+", "_"); cleanedMapName = cleanedMapName.replaceAll("_{2,}", "_"); if (cleanedMapName.endsWith("_")) cleanedMapName = cleanedMapName.substring(0, cleanedMapName.length() - 1); if (cleanedMapName.startsWith("_")) cleanedMapName = cleanedMapName.substring(1, cleanedMapName.length()); } protected int getBufferedImageType() { return BufferedImage.TYPE_3BYTE_BGR; } protected void writeTileImage(BufferedImage tileImage) throws MapCreationException { TileImageDataWriter writer; if (parameters != null) { writer = parameters.getFormat().getDataWriter(); } else writer = new TileImageJpegDataWriter(0.9); writer.initialize(); try { int initialBufferSize = tileImage.getWidth() * tileImage.getHeight() / 4; ByteArrayOutputStream buf = new ByteArrayOutputStream(initialBufferSize); writer.processImage(tileImage, buf); String imageFileName = "files/" + cleanedMapName + "." + writer.getType(); kmzOutputStream.writeStoredEntry(imageFileName, buf.toByteArray()); addMapToKmz(imageFileName); } catch (Exception e) { throw new MapCreationException(map, e); } } protected void addMapToKmz(String imageFileName) throws ParserConfigurationException, TransformerFactoryConfigurationError, TransformerException, IOException { int startX = xMin * tileSize; int endX = (xMax + 1) * tileSize; int startY = yMin * tileSize; int endY = (yMax + 1) * tileSize; addKmlEntry(map.getName(), imageFileName, startX, startY, endX - startX, endY - startY); } private void initKmlDoc(String folderName) throws ParserConfigurationException { DocumentBuilder builder; builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); kmlDoc = builder.newDocument(); Element kml = kmlDoc.createElementNS("http://www.opengis.net/kml/2.2", "kml"); kmlDoc.appendChild(kml); groundOverlayRoot = kml; if (folderName != null) { groundOverlayRoot = kmlDoc.createElement("Folder"); kml.appendChild(groundOverlayRoot); Element name = kmlDoc.createElement("name"); name.setTextContent(folderName); Element open = kmlDoc.createElement("open"); open.setTextContent("1"); groundOverlayRoot.appendChild(name); groundOverlayRoot.appendChild(open); } } private void addKmlEntry(String imageName, String imageFileName, int startX, int startY, int width, int height) { Element go = kmlDoc.createElement("GroundOverlay"); Element name = kmlDoc.createElement("name"); Element ico = kmlDoc.createElement("Icon"); Element href = kmlDoc.createElement("href"); Element drawOrder = kmlDoc.createElement("drawOrder"); Element latLonBox = kmlDoc.createElement("LatLonBox"); Element north = kmlDoc.createElement("north"); Element south = kmlDoc.createElement("south"); Element east = kmlDoc.createElement("east"); Element west = kmlDoc.createElement("west"); Element rotation = kmlDoc.createElement("rotation"); name.setTextContent(imageName); href.setTextContent(imageFileName); drawOrder.setTextContent("0"); MapSpace mapSpace = mapSource.getMapSpace(); NumberFormat df = Utilities.FORMAT_6_DEC_ENG; //String longitudeMin = df.format(mapSpace.cXToLon(startX, zoom)); //String longitudeMax = df.format(mapSpace.cXToLon(startX + width, zoom)); //String latitudeMin = df.format(mapSpace.cYToLat(startY + height, zoom)); //String latitudeMax = df.format(mapSpace.cYToLat(startY, zoom)); Point2D.Double p1 = mapSpace.cXYToLonLat(startX, startY, zoom); Point2D.Double p2 = mapSpace.cXYToLonLat(startX + width, startY + height, zoom); String longitudeMin = df.format(p1.x); String longitudeMax = df.format(p2.x); String latitudeMin = df.format(p2.y); String latitudeMax = df.format(p1.y); north.setTextContent(latitudeMax); south.setTextContent(latitudeMin); west.setTextContent(longitudeMin); east.setTextContent(longitudeMax); rotation.setTextContent("0.0"); groundOverlayRoot.appendChild(go); go.appendChild(name); go.appendChild(ico); go.appendChild(latLonBox); ico.appendChild(href); ico.appendChild(drawOrder); latLonBox.appendChild(north); latLonBox.appendChild(south); latLonBox.appendChild(east); latLonBox.appendChild(west); latLonBox.appendChild(rotation); } private void writeKmlToZip() throws TransformerFactoryConfigurationError, TransformerException, IOException { Transformer serializer; serializer = TransformerFactory.newInstance().newTransformer(); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); ByteArrayOutputStream bos = new ByteArrayOutputStream(16000); serializer.transform(new DOMSource(kmlDoc), new StreamResult(bos)); kmzOutputStream.writeStoredEntry("doc.kml", bos.toByteArray()); kmlDoc = null; groundOverlayRoot = null; } }