/******************************************************************************* * 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.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.util.LinkedList; import mobac.exceptions.MapCreationException; import mobac.program.annotations.AtlasCreatorName; import mobac.program.atlascreators.impl.MapTileWriter; import mobac.program.interfaces.LayerInterface; import mobac.program.interfaces.MapSpace; import mobac.utilities.GUIExceptionHandler; import mobac.utilities.Utilities; import mobac.utilities.stream.LittleEndianOutputStream; /** * General structure of an GMF file (Little Endian) * * <pre> * DWORD Version // 0xff000002 * DWORD cnt // Number of tiles in the file * * for each tile: * DWORD len; // number of characters in tile name * wchar_t name[len] // map/tile name in UTF_16LE * DWORD filepos // offset where image data starts in this file * DWORD width // tile width in pixel * DWORD height // tile height in pixel * DWORD cntCalPoints // calibration point count (usually 2 or 4) * for each tile calibration point * DWORD x // calibration point x position in tile * DWORD y // calibration point y position in tile * double dLong // longitude of calibration point * double dLat // latitude of calibration point * END OF FILE HEADER * Afterwards the tile image data follows as specified by each filepos * offset. * </pre> * */ @AtlasCreatorName(value = "Glopus Map File (GMF)", type = "Gmf") public class GlopusMapFile extends TrekBuddy { @Override public void initLayerCreation(LayerInterface layer) throws IOException { super.initLayerCreation(layer); mapTileWriter = new GlopusTileWriter(layer); } @Override public void finishLayerCreation() throws IOException { mapTileWriter.finalizeMap(); mapTileWriter = null; super.finishLayerCreation(); } @Override public void createMap() throws MapCreationException, InterruptedException { try { ((GlopusTileWriter) mapTileWriter).initMap(); // Select the tile creator instance based on whether tile image // parameters has been set or not if (parameters != null) createCustomTiles(); else createTiles(); } catch (MapCreationException e) { throw e; } catch (InterruptedException e) { throw e; } catch (Exception e) { throw new MapCreationException(map, e); } } @Override public void createAtlasTbaFile(String name) { } @Override public void abortAtlasCreation() throws IOException { mapTileWriter = null; super.abortAtlasCreation(); } private class GlopusTileWriter implements MapTileWriter { final LayerInterface layer; LinkedList<GlopusTile> tiles; int xCoordStart; int yCoordStart; int tileHeight = 256; int tileWidth = 256; int zoom; MapSpace mapSpace; String tileType; public GlopusTileWriter(LayerInterface layer) { super(); this.layer = layer; tiles = new LinkedList<GlopusTile>(); } public void initMap() { if (parameters != null) { tileHeight = parameters.getHeight(); tileWidth = parameters.getWidth(); } zoom = map.getZoom(); mapSpace = mapSource.getMapSpace(); xCoordStart = GlopusMapFile.this.xMin * mapSpace.getTileSize(); yCoordStart = GlopusMapFile.this.yMin * mapSpace.getTileSize(); } public void writeTile(int tilex, int tiley, String tileType, byte[] tileData) throws IOException { this.tileType = tileType; int xCooord = xCoordStart + tilex * tileWidth; int yCooord = yCoordStart + tiley * tileHeight; //double calWLon = mapSpace.cXToLon(xCooord, zoom); //double calNLat = mapSpace.cYToLat(yCooord, zoom); //double calELon = mapSpace.cXToLon(xCooord + tileWidth, zoom); //double calSLat = mapSpace.cYToLat(yCooord + tileHeight, zoom); Point2D.Double p1 = mapSpace.cXYToLonLat(xCooord, yCooord, zoom); Point2D.Double p2 = mapSpace.cXYToLonLat(xCooord + tileWidth, yCooord + tileWidth, zoom); double calWLon = p1.x; double calNLat = p1.y; double calELon = p2.x; double calSLat = p2.y; GlopusTile gt = new GlopusTile(tileData, calNLat, calWLon, calSLat, calELon); tiles.add(gt); } public void finalizeMap() { File gmfFile = new File(atlasDir, layer.getName() + ".gmf"); FileOutputStream fout = null; try { int count = tiles.size(); int offset = 8 + count * ( // 20 // nameLength, offset and calibration point count, // tile height & width + (12 * 2) // name bytes + (4 * 24) // four calibration points ); fout = new FileOutputStream(gmfFile); LittleEndianOutputStream out = new LittleEndianOutputStream(new BufferedOutputStream(fout, 16384)); out.writeInt((int) 0xff000002); out.writeInt(count); int mapNumber = 0; Charset charset = Charset.forName("UTF-16LE"); for (GlopusTile gt : tiles) { String mapName = String.format("%08d.%s", mapNumber++, tileType); byte[] nameBytes = mapName.getBytes(charset); out.writeInt(mapName.length());// Name length out.write(nameBytes); out.writeInt(offset); out.writeInt(tileWidth); out.writeInt(tileHeight); out.writeInt(4); // number of calibration points out.writeInt(0); out.writeInt(0); out.writeDouble(gt.calWLon); out.writeDouble(gt.calNLat); out.writeInt(tileHeight); out.writeInt(tileWidth); out.writeDouble(gt.calELon); out.writeDouble(gt.calSLat); out.writeInt(tileHeight); out.writeInt(0); out.writeDouble(gt.calELon); out.writeDouble(gt.calNLat); out.writeInt(0); out.writeInt(tileWidth); out.writeDouble(gt.calWLon); out.writeDouble(gt.calSLat); if (log.isTraceEnabled()) log.trace(String.format("Offset %f %f %f %f \"%s\": 0x%x", gt.calWLon, gt.calNLat, gt.calELon, gt.calELon, mapName, offset)); offset += gt.data.length; } out.flush(); out = null; for (GlopusTile gt : tiles) { fout.write(gt.data); } fout.flush(); } catch (IOException e) { GUIExceptionHandler.showExceptionDialog(e); } finally { Utilities.closeStream(fout); } } } private static class GlopusTile { byte[] data; double calNLat; double calWLon; double calSLat; double calELon; public GlopusTile(byte[] data, double calNLat, double calWLon, double calSLat, double calELon) { super(); this.data = data; this.calNLat = calNLat; this.calWLon = calWLon; this.calSLat = calSLat; this.calELon = calELon; } } }