package mil.nga.geopackage.factory; import android.content.Context; import android.database.Cursor; import com.j256.ormlite.stmt.PreparedQuery; import com.j256.ormlite.stmt.QueryBuilder; import java.sql.SQLException; import java.util.List; import mil.nga.geopackage.BoundingBox; import mil.nga.geopackage.GeoPackage; import mil.nga.geopackage.GeoPackageException; import mil.nga.geopackage.attributes.AttributesCursor; import mil.nga.geopackage.attributes.AttributesDao; import mil.nga.geopackage.attributes.AttributesTable; import mil.nga.geopackage.attributes.AttributesTableReader; import mil.nga.geopackage.core.contents.Contents; import mil.nga.geopackage.core.contents.ContentsDao; import mil.nga.geopackage.core.contents.ContentsDataType; import mil.nga.geopackage.db.CoreSQLUtils; import mil.nga.geopackage.db.GeoPackageConnection; import mil.nga.geopackage.db.GeoPackageTableCreator; import mil.nga.geopackage.extension.RTreeIndexExtension; import mil.nga.geopackage.features.columns.GeometryColumns; import mil.nga.geopackage.features.columns.GeometryColumnsDao; import mil.nga.geopackage.features.index.FeatureIndexManager; import mil.nga.geopackage.features.user.FeatureCursor; import mil.nga.geopackage.features.user.FeatureDao; import mil.nga.geopackage.features.user.FeatureTable; import mil.nga.geopackage.features.user.FeatureTableReader; import mil.nga.geopackage.tiles.matrix.TileMatrix; import mil.nga.geopackage.tiles.matrix.TileMatrixDao; import mil.nga.geopackage.tiles.matrix.TileMatrixKey; import mil.nga.geopackage.tiles.matrixset.TileMatrixSet; import mil.nga.geopackage.tiles.matrixset.TileMatrixSetDao; import mil.nga.geopackage.tiles.user.TileCursor; import mil.nga.geopackage.tiles.user.TileDao; import mil.nga.geopackage.tiles.user.TileTable; import mil.nga.geopackage.tiles.user.TileTableReader; import mil.nga.geopackage.user.custom.UserCustomCursor; import mil.nga.geopackage.user.custom.UserCustomDao; import mil.nga.geopackage.user.custom.UserCustomTable; import mil.nga.geopackage.user.custom.UserCustomTableReader; import mil.nga.sf.proj.Projection; /** * A single GeoPackage database connection implementation * * @author osbornb */ public class GeoPackageImpl extends GeoPackageCoreImpl implements GeoPackage { /** * Context */ private final Context context; /** * Database connection */ private final GeoPackageConnection database; /** * Cursor factory */ private final GeoPackageCursorFactory cursorFactory; /** * Constructor * * @param context context * @param name GeoPackage name * @param path database path * @param database database connection * @param cursorFactory cursor factory * @param tableCreator table creator * @param writable writable flag */ GeoPackageImpl(Context context, String name, String path, GeoPackageConnection database, GeoPackageCursorFactory cursorFactory, GeoPackageTableCreator tableCreator, boolean writable) { super(name, path, database, tableCreator, writable); this.context = context; this.database = database; this.cursorFactory = cursorFactory; } /** * {@inheritDoc} */ @Override public BoundingBox getFeatureBoundingBox(Projection projection, String table, boolean manual) { BoundingBox boundingBox = null; FeatureIndexManager indexManager = new FeatureIndexManager(context, this, table); try { if (manual || indexManager.isIndexed()) { boundingBox = indexManager.getBoundingBox(projection); } } finally { indexManager.close(); } return boundingBox; } /** * {@inheritDoc} */ @Override public GeoPackageCursorFactory getCursorFactory() { return cursorFactory; } /** * {@inheritDoc} */ @Override public void registerCursorWrapper(String table, GeoPackageCursorWrapper cursorWrapper) { cursorFactory.registerTable(table, cursorWrapper); } /** * {@inheritDoc} */ @Override public FeatureDao getFeatureDao(GeometryColumns geometryColumns) { if (geometryColumns == null) { throw new GeoPackageException("Non null " + GeometryColumns.class.getSimpleName() + " is required to create " + FeatureDao.class.getSimpleName()); } // Read the existing table and create the dao FeatureTableReader tableReader = new FeatureTableReader(geometryColumns); final FeatureTable featureTable = tableReader.readTable(database); featureTable.setContents(geometryColumns.getContents()); FeatureDao dao = new FeatureDao(getName(), database, geometryColumns, featureTable); // Register the table name (with and without quotes) to wrap cursors with the feature cursor registerCursorWrapper(geometryColumns.getTableName(), new GeoPackageCursorWrapper() { @Override public Cursor wrapCursor(Cursor cursor) { return new FeatureCursor(featureTable, cursor); } }); // If the GeoPackage is writable and the feature table has a RTree Index // extension, drop the RTree triggers. User defined functions are currently not supported. if (writable) { RTreeIndexExtension rtree = new RTreeIndexExtension(this); rtree.dropTriggers(featureTable); } return dao; } /** * {@inheritDoc} */ @Override public FeatureDao getFeatureDao(Contents contents) { if (contents == null) { throw new GeoPackageException("Non null " + Contents.class.getSimpleName() + " is required to create " + FeatureDao.class.getSimpleName()); } GeometryColumns geometryColumns = contents.getGeometryColumns(); if (geometryColumns == null) { throw new GeoPackageException("No " + GeometryColumns.class.getSimpleName() + " exists for " + Contents.class.getSimpleName() + " " + contents.getId()); } return getFeatureDao(geometryColumns); } /** * {@inheritDoc} */ @Override public FeatureDao getFeatureDao(String tableName) { GeometryColumnsDao dao = getGeometryColumnsDao(); List<GeometryColumns> geometryColumnsList; try { geometryColumnsList = dao.queryForEq( GeometryColumns.COLUMN_TABLE_NAME, tableName); } catch (SQLException e) { throw new GeoPackageException("Failed to retrieve " + FeatureDao.class.getSimpleName() + " for table name: " + tableName + ". Exception retrieving " + GeometryColumns.class.getSimpleName() + ".", e); } if (geometryColumnsList.isEmpty()) { throw new GeoPackageException( "No Feature Table exists for table name: " + tableName); } else if (geometryColumnsList.size() > 1) { // This shouldn't happen with the table name unique constraint on // geometry columns throw new GeoPackageException("Unexpected state. More than one " + GeometryColumns.class.getSimpleName() + " matched for table name: " + tableName + ", count: " + geometryColumnsList.size()); } return getFeatureDao(geometryColumnsList.get(0)); } /** * {@inheritDoc} */ @Override public TileDao getTileDao(TileMatrixSet tileMatrixSet) { if (tileMatrixSet == null) { throw new GeoPackageException("Non null " + TileMatrixSet.class.getSimpleName() + " is required to create " + TileDao.class.getSimpleName()); } // Get the Tile Matrix collection, order by zoom level ascending & pixel // size descending per requirement 51 List<TileMatrix> tileMatrices; try { TileMatrixDao tileMatrixDao = getTileMatrixDao(); QueryBuilder<TileMatrix, TileMatrixKey> qb = tileMatrixDao .queryBuilder(); qb.where().eq(TileMatrix.COLUMN_TABLE_NAME, tileMatrixSet.getTableName()); qb.orderBy(TileMatrix.COLUMN_ZOOM_LEVEL, true); qb.orderBy(TileMatrix.COLUMN_PIXEL_X_SIZE, false); qb.orderBy(TileMatrix.COLUMN_PIXEL_Y_SIZE, false); PreparedQuery<TileMatrix> query = qb.prepare(); tileMatrices = tileMatrixDao.query(query); } catch (SQLException e) { throw new GeoPackageException("Failed to retrieve " + TileDao.class.getSimpleName() + " for table name: " + tileMatrixSet.getTableName() + ". Exception retrieving " + TileMatrix.class.getSimpleName() + " collection.", e); } // Read the existing table and create the dao TileTableReader tableReader = new TileTableReader( tileMatrixSet.getTableName()); final TileTable tileTable = tableReader.readTable(database); tileTable.setContents(tileMatrixSet.getContents()); TileDao dao = new TileDao(getName(), database, tileMatrixSet, tileMatrices, tileTable); // Register the table name (with and without quotes) to wrap cursors with the tile cursor registerCursorWrapper(tileMatrixSet.getTableName(), new GeoPackageCursorWrapper() { @Override public Cursor wrapCursor(Cursor cursor) { return new TileCursor(tileTable, cursor); } }); return dao; } /** * {@inheritDoc} */ @Override public TileDao getTileDao(Contents contents) { if (contents == null) { throw new GeoPackageException("Non null " + Contents.class.getSimpleName() + " is required to create " + TileDao.class.getSimpleName()); } TileMatrixSet tileMatrixSet = contents.getTileMatrixSet(); if (tileMatrixSet == null) { throw new GeoPackageException("No " + TileMatrixSet.class.getSimpleName() + " exists for " + Contents.class.getSimpleName() + " " + contents.getId()); } return getTileDao(tileMatrixSet); } /** * {@inheritDoc} */ @Override public TileDao getTileDao(String tableName) { TileMatrixSetDao dao = getTileMatrixSetDao(); List<TileMatrixSet> tileMatrixSetList; try { tileMatrixSetList = dao.queryForEq(TileMatrixSet.COLUMN_TABLE_NAME, tableName); } catch (SQLException e) { throw new GeoPackageException("Failed to retrieve " + TileDao.class.getSimpleName() + " for table name: " + tableName + ". Exception retrieving " + TileMatrixSet.class.getSimpleName() + ".", e); } if (tileMatrixSetList.isEmpty()) { throw new GeoPackageException( "No Tile Table exists for table name: " + tableName); } else if (tileMatrixSetList.size() > 1) { // This shouldn't happen with the table name primary key on tile // matrix set table throw new GeoPackageException("Unexpected state. More than one " + TileMatrixSet.class.getSimpleName() + " matched for table name: " + tableName + ", count: " + tileMatrixSetList.size()); } return getTileDao(tileMatrixSetList.get(0)); } /** * {@inheritDoc} */ @Override public AttributesDao getAttributesDao(Contents contents) { if (contents == null) { throw new GeoPackageException("Non null " + Contents.class.getSimpleName() + " is required to create " + AttributesDao.class.getSimpleName()); } if (contents.getDataType() != ContentsDataType.ATTRIBUTES) { throw new GeoPackageException(Contents.class.getSimpleName() + " is required to be of type " + ContentsDataType.ATTRIBUTES + ". Actual: " + contents.getDataTypeString()); } // Read the existing table and create the dao AttributesTableReader tableReader = new AttributesTableReader( contents.getTableName()); final AttributesTable attributesTable = tableReader.readTable(database); attributesTable.setContents(contents); AttributesDao dao = new AttributesDao(getName(), database, attributesTable); // Register the table name (with and without quotes) to wrap cursors with the attributes cursor registerCursorWrapper(attributesTable.getTableName(), new GeoPackageCursorWrapper() { @Override public Cursor wrapCursor(Cursor cursor) { return new AttributesCursor(attributesTable, cursor); } }); return dao; } /** * {@inheritDoc} */ @Override public AttributesDao getAttributesDao(String tableName) { ContentsDao dao = getContentsDao(); Contents contents = null; try { contents = dao.queryForId(tableName); } catch (SQLException e) { throw new GeoPackageException("Failed to retrieve " + Contents.class.getSimpleName() + " for table name: " + tableName, e); } if (contents == null) { throw new GeoPackageException( "No Contents Table exists for table name: " + tableName); } return getAttributesDao(contents); } /** * {@inheritDoc} */ @Override public UserCustomDao getUserCustomDao(String tableName) { UserCustomTable table = UserCustomTableReader.readTable(database, tableName); return getUserCustomDao(table); } /** * {@inheritDoc} */ @Override public UserCustomDao getUserCustomDao(UserCustomTable table) { UserCustomDao dao = new UserCustomDao(getName(), database, table); // Register the table name (with and without quotes) to wrap cursors with the user custom cursor final UserCustomTable userCustomTable = table; registerCursorWrapper(table.getTableName(), new GeoPackageCursorWrapper() { @Override public Cursor wrapCursor(Cursor cursor) { return new UserCustomCursor(userCustomTable, cursor); } }); return dao; } /** * {@inheritDoc} */ @Override public void execSQL(String sql) { database.execSQL(sql); } /** * {@inheritDoc} */ @Override public void beginTransaction() { database.beginTransaction(); } /** * {@inheritDoc} */ @Override public void endTransaction(boolean successful) { database.endTransaction(successful); } /** * {@inheritDoc} */ @Override public void commit() { database.commit(); } /** * {@inheritDoc} */ @Override public boolean inTransaction() { return database.inTransaction(); } /** * {@inheritDoc} */ @Override public Cursor rawQuery(String sql, String[] args) { return database.rawQuery(sql, args); } /** * {@inheritDoc} */ @Override public GeoPackageConnection getConnection() { return database; } /** * {@inheritDoc} */ @Override public Context getContext() { return context; } /** * {@inheritDoc} */ @Override public Cursor foreignKeyCheck() { return foreignKeyCheck(null); } /** * {@inheritDoc} */ @Override public Cursor foreignKeyCheck(String tableName) { Cursor cursor = rawQuery(CoreSQLUtils.foreignKeyCheckSQL(tableName), null); if (!cursor.moveToNext()) { cursor.close(); cursor = null; } return cursor; } /** * {@inheritDoc} */ @Override public Cursor integrityCheck() { return integrityCheck(rawQuery(CoreSQLUtils.integrityCheckSQL(), null)); } /** * {@inheritDoc} */ @Override public Cursor quickCheck() { return integrityCheck(rawQuery(CoreSQLUtils.quickCheckSQL(), null)); } /** * Check the cursor returned from the integrity check to see if things are "ok" * * @param cursor * @return null if ok, else the open cursor */ private Cursor integrityCheck(Cursor cursor) { if (cursor.moveToNext()) { String value = cursor.getString(0); if (value.equals("ok")) { cursor.close(); cursor = null; } } return cursor; } }