/******************************************************************************* * 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.io.File; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import javax.imageio.ImageIO; import mobac.exceptions.AtlasTestException; import mobac.exceptions.MapCreationException; import mobac.program.annotations.AtlasCreatorName; import mobac.program.annotations.SupportedParameters; import mobac.program.atlascreators.tileprovider.ConvertedRawTileProvider; import mobac.program.atlascreators.tileprovider.TileProvider; import mobac.program.interfaces.AtlasInterface; import mobac.program.interfaces.MapInterface; import mobac.program.interfaces.MapSource; import mobac.program.interfaces.MapSpace.ProjectionCategory; import mobac.program.interfaces.RequiresSQLite; import mobac.program.model.TileImageParameters.Name; import mobac.utilities.jdbc.SQLiteLoader; /** * http://sourceforge.net/p/mobac/patches/22/ */ @AtlasCreatorName("Osmdroid SQLite") @SupportedParameters(names = { Name.format }) public class OsmdroidSQLite extends AtlasCreator implements RequiresSQLite { private static final int MAX_BATCH_SIZE = 1000; protected Connection conn = null; public OsmdroidSQLite() { super(); SQLiteLoader.loadSQLiteOrShowError(); } @Override public boolean testMapSource(MapSource mapSource) { return mapSource.getMapSpace().getProjectionCategory().equals(ProjectionCategory.SPHERE); } @Override public void startAtlasCreation(AtlasInterface atlas, File customAtlasDir) throws IOException, AtlasTestException, InterruptedException { super.startAtlasCreation(atlas, customAtlasDir); String databaseFile = new File(atlasDir, atlas.getName() + ".sqlite").getAbsolutePath(); try { SQLiteLoader.loadSQLite(); } catch (SQLException e) { throw new AtlasTestException(SQLiteLoader.getMsgSqliteMissing()); } try { conn = DriverManager.getConnection("jdbc:sqlite:" + databaseFile); Statement stat = conn.createStatement(); stat.executeUpdate("CREATE TABLE IF NOT EXISTS tiles (key INTEGER PRIMARY KEY, provider TEXT, tile BLOB)"); stat.close(); } catch (SQLException e) { throw new IOException("Error creating SQL database \"" + databaseFile + "\": " + e.getMessage(), e); } log.debug("SQLite Database file: " + databaseFile); } @Override public void initializeMap(MapInterface map, TileProvider mapTileProvider) { super.initializeMap(map, mapTileProvider); if (parameters != null) mapDlTileProvider = new ConvertedRawTileProvider(mapDlTileProvider, parameters.getFormat()); } @Override public void createMap() throws MapCreationException, InterruptedException { try { String provider = map.getMapSource().getName(); int maxMapProgress = 2 * (xMax - xMin + 1) * (yMax - yMin + 1); atlasProgress.initMapCreation(maxMapProgress); conn.setAutoCommit(false); int batchTileCount = 0; ImageIO.setUseCache(false); PreparedStatement prep = conn.prepareStatement("INSERT or REPLACE INTO tiles VALUES (?, ?, ?);"); Runtime r = Runtime.getRuntime(); long heapMaxSize = r.maxMemory(); for (long x = xMin; x <= xMax; x++) for (long y = yMin; y <= yMax; y++) { checkUserAbort(); atlasProgress.incMapCreationProgress(); byte[] sourceTileData = mapDlTileProvider.getTileData((int) x, (int) y); if (sourceTileData != null) { long z = zoom; long index = (((z << z) + x) << z) + y; prep.setLong(1, index); prep.setString(2, provider); prep.setBytes(3, sourceTileData); prep.addBatch(); long heapAvailable = heapMaxSize - r.totalMemory() + r.freeMemory(); if ((heapAvailable < HEAP_MIN) || (batchTileCount >= MAX_BATCH_SIZE)) { log.trace("Executing batch containing " + batchTileCount + " tiles"); prep.executeBatch(); prep.clearBatch(); System.gc(); conn.commit(); atlasProgress.incMapCreationProgress(batchTileCount); batchTileCount = 0; } } } prep.executeBatch(); conn.setAutoCommit(true); atlasProgress.setMapCreationProgress(maxMapProgress); } catch (SQLException e) { throw new MapCreationException("Error writing tile image: " + e.getMessage(), map, e); } catch (IOException e) { throw new MapCreationException("Error writing tile image: " + e.getMessage(), map, e); } } @Override public void abortAtlasCreation() throws IOException { SQLiteLoader.closeConnection(conn); conn = null; super.abortAtlasCreation(); } @Override public void finishAtlasCreation() throws IOException, InterruptedException { SQLiteLoader.closeConnection(conn); conn = null; super.finishAtlasCreation(); } }