/******************************************************************************* * 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.Color; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.Locale; import mobac.exceptions.AtlasTestException; import mobac.exceptions.MapCreationException; import mobac.mapsources.mapspace.MercatorPower2MapSpace; import mobac.program.annotations.AtlasCreatorName; import mobac.program.annotations.SupportedParameters; import mobac.program.atlascreators.impl.MapTileBuilder; import mobac.program.atlascreators.impl.MapTileWriter; import mobac.program.atlascreators.tileprovider.CacheTileProvider; import mobac.program.atlascreators.tileprovider.TileProvider; import mobac.program.interfaces.LayerInterface; import mobac.program.interfaces.MapInterface; import mobac.program.interfaces.MapSource; import mobac.program.interfaces.MapSpace; import mobac.program.model.TileImageFormat; import mobac.program.model.TileImageParameters; import mobac.program.model.TileImageParameters.Name; import mobac.utilities.Charsets; import mobac.utilities.Utilities; /** * Creates maps using the OruxMaps (Android) atlas format. * * @author orux */ @AtlasCreatorName("OruxMaps") @SupportedParameters(names = { Name.format }) public class OruxMaps extends AtlasCreator { // Calibration file extension protected static final String ORUXMAPS_EXT = ".otrk2.xml"; // OruxMaps tile size protected static final int TILE_SIZE = 512; protected String calVersionCode; // OruxMaps background color protected static final Color BG_COLOR = new Color(0xcb, 0xd3, 0xf3); // Each layer is a Main map for OruxMaps protected File oruxMapsMainDir; // Each map is a Layer map for OruxMaps protected File oruxMapsLayerDir; // Images directory for each map protected File oruxMapsImagesDir; protected LayerInterface currentLayer; // We need to override the map name, All maps must have the same prefix (layer name) protected String mapName; public OruxMaps() { super(); calVersionCode = "2.1"; } @Override public boolean testMapSource(MapSource mapSource) { return (mapSource.getMapSpace() instanceof MercatorPower2MapSpace); } @Override protected void testAtlas() throws AtlasTestException { for (LayerInterface layer : atlas) { int cont = layer.getMapCount(); for (int i = 0; i < cont; i++){ MapInterface currMap = layer.getMap(i); int currZoomLevel = currMap.getZoom(); for (int j = i + 1; j < cont; j++){ MapInterface nextMap = layer.getMap(j); int nextZoomLevel = nextMap.getZoom(); if (currZoomLevel == nextZoomLevel) throw new AtlasTestException( "Unable to create a map with more than a layer with the same zoom level: " + currMap + " & " + nextMap + "\nPossible causes:\n" + "You are combining several layers (using drag & drop in 'Atlas Content')\n" + "You are creating a large map, and you have not selected the maximum value in 'Settings - Map size'"); } } } } /* * @see mobac.program.atlascreators.AtlasCreator#initLayerCreation(mobac.program .interfaces.LayerInterface) */ @Override public void initLayerCreation(LayerInterface layer) throws IOException { super.initLayerCreation(layer); currentLayer = layer; oruxMapsMainDir = new File(atlasDir, layer.getName()); Utilities.mkDirs(oruxMapsMainDir); } @Override public void finishLayerCreation() throws IOException { super.finishLayerCreation(); writeMainOtrk2File(currentLayer.getName()); } /* * (non-Javadoc) * * @see mobac.program.atlascreators.AtlasCreator#initializeMap(mobac.program. interfaces.MapInterface, * mobac.utilities.tar.TarIndex) */ @Override public void initializeMap(MapInterface map, TileProvider mapTileProvider) { super.initializeMap(map, mapTileProvider); // OruxMaps default image format, jpeg90; always TILE_SIZE=512; if (parameters == null) parameters = new TileImageParameters(TILE_SIZE, TILE_SIZE, TileImageFormat.JPEG90); else parameters = new TileImageParameters(TILE_SIZE, TILE_SIZE, parameters.getFormat()); mapName = String.format("%s %02d", currentLayer.getName(), map.getZoom()); } @Override public void createMap() throws MapCreationException, InterruptedException { oruxMapsLayerDir = new File(oruxMapsMainDir, mapName); oruxMapsImagesDir = new File(oruxMapsLayerDir, "set"); try { Utilities.mkDir(oruxMapsLayerDir); Utilities.mkDir(oruxMapsImagesDir); writeOtrk2File(); createTiles(); } catch (InterruptedException e) { // User has aborted process return; } catch (Exception e) { throw new MapCreationException(map, e); } } protected void createTiles() throws InterruptedException, MapCreationException { CacheTileProvider ctp = new CacheTileProvider(mapDlTileProvider); try { mapDlTileProvider = ctp; OruxMapTileBuilder mapTileBuilder = new OruxMapTileBuilder(this, new OruxMapTileWriter()); atlasProgress.initMapCreation(mapTileBuilder.getCustomTileCount()); mapTileBuilder.createTiles(); } finally { ctp.cleanup(); } } /** * Main calibration file * * @param name */ private void writeMainOtrk2File(String name) { OutputStreamWriter writer; FileOutputStream otrk2FileStream = null; File otrk2 = new File(oruxMapsMainDir, name + ORUXMAPS_EXT); try { writer = new OutputStreamWriter(new FileOutputStream(otrk2), Charsets.UTF_8); writer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); writer.append("<OruxTracker " + "xmlns=\"http://oruxtracker.com/app/res/calibration\"\n" + " versionCode=\"" + calVersionCode + "\">\n"); writer.append("<MapCalibration layers=\"true\" layerLevel=\"0\">\n"); writer.append("<MapName><![CDATA[" + name + "]]></MapName>\n"); writer.append(appendMapContent()); writer.append("</MapCalibration>\n"); writer.append("</OruxTracker>\n"); writer.flush(); } catch (IOException e) { log.error("", e); } finally { Utilities.closeStream(otrk2FileStream); } } protected String appendMapContent() { return ""; } /** * Main calibration file per layer * */ protected void writeOtrk2File() { FileOutputStream stream = null; OutputStreamWriter mapWriter; File otrk2File = new File(oruxMapsLayerDir, mapName + ORUXMAPS_EXT); try { stream = new FileOutputStream(otrk2File); mapWriter = new OutputStreamWriter(stream, "UTF8"); mapWriter.append(prepareOtrk2File()); mapWriter.flush(); } catch (IOException e) { log.error("", e); } finally { Utilities.closeStream(stream); } } /** * Main calibration file per layer * */ protected String prepareOtrk2File() { StringBuilder mapWriter = new StringBuilder(); MapSpace mapSpace = mapSource.getMapSpace(); //double longitudeMin = mapSpace.cXToLon(xMin * tileSize, zoom); //double longitudeMax = mapSpace.cXToLon((xMax + 1) * tileSize, zoom); //double latitudeMin = mapSpace.cYToLat((yMax + 1) * tileSize, zoom); //double latitudeMax = mapSpace.cYToLat(yMin * tileSize, zoom); Point2D.Double p1 = mapSpace.cXYToLonLat(xMin * tileSize, yMin * tileSize, zoom); Point2D.Double p2 = mapSpace.cXYToLonLat((xMax + 1) * tileSize, (yMax + 1) * tileSize, zoom); double longitudeMin = p1.x; double longitudeMax = p2.x; double latitudeMin = p2.y; double latitudeMax = p1.y; mapWriter.append("<OruxTracker " + "xmlns=\"http://oruxtracker.com/app/res/calibration\"\n" + " versionCode=\"2.1\">\n"); mapWriter.append("<MapCalibration layers=\"false\" layerLevel=\"" + map.getZoom() + "\">\n"); mapWriter.append("<MapName><![CDATA[" + mapName + "]]></MapName>\n"); // convert ampersands and others String mapFileName = mapName; mapFileName = mapFileName.replaceAll("&", "&"); mapFileName = mapFileName.replaceAll("<", "<"); mapFileName = mapFileName.replaceAll(">", ">"); mapFileName = mapFileName.replaceAll("\"", """); mapFileName = mapFileName.replaceAll("'", "'"); int mapWidth = (xMax - xMin + 1) * tileSize; int mapHeight = (yMax - yMin + 1) * tileSize; int numXimg = (mapWidth + TILE_SIZE - 1) / TILE_SIZE; int numYimg = (mapHeight + TILE_SIZE - 1) / TILE_SIZE; mapWriter.append("<MapChunks xMax=\"" + numXimg + "\" yMax=\"" + numYimg + "\" datum=\"" + "WGS84" + "\" projection=\"" + mapSpace.getMapSpaceType().getProjection() + "\" img_height=\"" + TILE_SIZE + "\" img_width=\"" + TILE_SIZE + "\" file_name=\"" + mapFileName + "\" />\n"); mapWriter.append("<MapDimensions height=\"" + mapHeight + "\" width=\"" + mapWidth + "\" />\n"); mapWriter.append("<MapBounds minLat=\"" + latitudeMin + "\" maxLat=\"" + latitudeMax + "\" minLon=\"" + longitudeMin + "\" maxLon=\"" + longitudeMax + "\" />\n"); mapWriter.append("<CalibrationPoints>\n"); String cb = "<CalibrationPoint corner=\"%s\" lon=\"%2.6f\" lat=\"%2.6f\" />\n"; mapWriter.append(String.format(Locale.ENGLISH, cb, "TL", longitudeMin, latitudeMax)); mapWriter.append(String.format(Locale.ENGLISH, cb, "BR", longitudeMax, latitudeMin)); mapWriter.append(String.format(Locale.ENGLISH, cb, "TR", longitudeMax, latitudeMax)); mapWriter.append(String.format(Locale.ENGLISH, cb, "BL", longitudeMin, latitudeMin)); mapWriter.append("</CalibrationPoints>\n"); mapWriter.append("</MapCalibration>\n"); mapWriter.append("</OruxTracker>\n"); return mapWriter.toString(); } protected class OruxMapTileBuilder extends MapTileBuilder { public OruxMapTileBuilder(AtlasCreator atlasCreator, MapTileWriter mapTileWriter) { super(atlasCreator, mapTileWriter, false); } @Override protected void prepareTile(Graphics2D graphics) { graphics.setColor(BG_COLOR); graphics.fillRect(0, 0, TILE_SIZE, TILE_SIZE); } } private class OruxMapTileWriter implements MapTileWriter { public void writeTile(int tilex, int tiley, String tileType, byte[] tileData) throws IOException { String tileFileName = String.format("%s_%d_%d.omc2", mapName, tilex, tiley); FileOutputStream out = new FileOutputStream(new File(oruxMapsImagesDir, tileFileName)); try { out.write(tileData); } finally { Utilities.closeStream(out); } } public void finalizeMap() { // Nothing to do } } }