package mil.nga.geopackage.extension.index; import android.util.Log; import com.j256.ormlite.misc.TransactionManager; import com.j256.ormlite.support.ConnectionSource; import java.sql.SQLException; import java.util.Map; import java.util.concurrent.Callable; import mil.nga.geopackage.BoundingBox; import mil.nga.geopackage.GeoPackage; import mil.nga.geopackage.GeoPackageException; import mil.nga.geopackage.features.user.FeatureCursor; import mil.nga.geopackage.features.user.FeatureDao; import mil.nga.geopackage.features.user.FeatureRow; import mil.nga.geopackage.features.user.FeatureRowSync; import mil.nga.sf.GeometryEnvelope; import mil.nga.sf.proj.Projection; /** * Feature Table Index NGA Extension implementation. This extension is used to * index Geometries within a feature table by their minimum bounding box for * bounding box queries. This extension is required to provide an index * implementation when a SQLite version is used before SpatialLite support * (Android). * * @author osbornb * @since 1.1.0 */ public class FeatureTableIndex extends FeatureTableCoreIndex { /** * Feature DAO */ private final FeatureDao featureDao; /** * Feature Row Sync for simultaneous same row queries */ private final FeatureRowSync featureRowSync = new FeatureRowSync(); /** * Constructor * * @param geoPackage GeoPackage * @param featureDao feature dao */ public FeatureTableIndex(GeoPackage geoPackage, FeatureDao featureDao) { super(geoPackage, featureDao.getTableName(), featureDao .getGeometryColumnName()); this.featureDao = featureDao; } /** * {@inheritDoc} */ @Override public Projection getProjection() { return featureDao.getProjection(); } /** * Close the table index */ public void close() { // Don't close anything, leave the GeoPackage connection open } /** * Index the feature row. This method assumes that indexing has been * completed and maintained as the last indexed time is updated. * * @param row feature row * @return true if indexed */ public boolean index(FeatureRow row) { TableIndex tableIndex = getTableIndex(); if (tableIndex == null) { throw new GeoPackageException( "GeoPackage table is not indexed. GeoPackage: " + getGeoPackage().getName() + ", Table: " + getTableName()); } boolean indexed = index(tableIndex, row.getId(), row.getGeometry()); // Update the last indexed time updateLastIndexed(); return indexed; } /** * {@inheritDoc} */ @Override protected int indexTable(final TableIndex tableIndex) { int count = 0; long offset = 0; int chunkCount = 0; String[] columns = featureDao.getIdAndGeometryColumnNames(); while (chunkCount >= 0) { final long chunkOffset = offset; try { // Iterate through each row and index as a single transaction ConnectionSource connectionSource = getGeoPackage().getDatabase() .getConnectionSource(); chunkCount = TransactionManager.callInTransaction(connectionSource, new Callable<Integer>() { public Integer call() throws Exception { FeatureCursor cursor = featureDao.queryForChunk(columns, chunkLimit, chunkOffset); int count = indexRows(tableIndex, cursor); return count; } }); if (chunkCount > 0) { count += chunkCount; } } catch (SQLException e) { throw new GeoPackageException("Failed to Index Table. GeoPackage: " + getGeoPackage().getName() + ", Table: " + getTableName(), e); } offset += chunkLimit; } // Update the last indexed time if (progress == null || progress.isActive()) { updateLastIndexed(); } return count; } /** * Index the feature rows in the cursor * * @param tableIndex table index * @param cursor feature cursor * @return count, -1 if no results or canceled */ private int indexRows(TableIndex tableIndex, FeatureCursor cursor) { int count = -1; try { while ((progress == null || progress.isActive()) && cursor.moveToNext()) { if (count < 0) { count++; } try { FeatureRow row = cursor.getRow(); if (row.isValid()) { boolean indexed = index(tableIndex, row.getId(), row.getGeometry()); if (indexed) { count++; } if (progress != null) { progress.addProgress(1); } } } catch (Exception e) { Log.e(FeatureTableIndex.class.getSimpleName(), "Failed to index feature. Table: " + tableIndex.getTableName() + ", Position: " + cursor.getPosition(), e); } } } finally { cursor.close(); } return count; } /** * Delete the index for the feature row * * @param row feature row * @return deleted rows, should be 0 or 1 */ public int deleteIndex(FeatureRow row) { return deleteIndex(row.getId()); } /** * Get the feature row for the Geometry Index * * @param geometryIndex geometry index * @return feature row */ public FeatureRow getFeatureRow(GeometryIndex geometryIndex) { long geomId = geometryIndex.getGeomId(); // Get the row or lock for reading FeatureRow row = featureRowSync.getRowOrLock(geomId); if (row == null) { // Query for the row and set in the sync try { row = featureDao.queryForIdRow(geomId); } finally { featureRowSync.setRow(geomId, row); } } return row; } /** * Query for all Features * * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures() { return featureDao.queryIn(queryIdsSQL()); } /** * Query for all Features * * @param columns columns * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns) { return featureDao.queryIn(columns, queryIdsSQL()); } /** * Query for features * * @param fieldValues field values * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(Map<String, Object> fieldValues) { return featureDao.queryIn(queryIdsSQL(), fieldValues); } /** * Query for features * * @param columns columns * @param fieldValues field values * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, Map<String, Object> fieldValues) { return featureDao.queryIn(columns, queryIdsSQL(), fieldValues); } /** * Count features * * @param fieldValues field values * @return count * @since 3.4.0 */ public int countFeatures(Map<String, Object> fieldValues) { return featureDao.countIn(queryIdsSQL(), fieldValues); } /** * Query for features * * @param where where clause * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(String where) { return featureDao.queryIn(queryIdsSQL(), where); } /** * Query for features * * @param columns columns * @param where where clause * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, String where) { return featureDao.queryIn(columns, queryIdsSQL(), where); } /** * Count features * * @param where where clause * @return count * @since 3.4.0 */ public int countFeatures(String where) { return featureDao.countIn(queryIdsSQL(), where); } /** * Query for features * * @param where where clause * @param whereArgs where arguments * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(String where, String[] whereArgs) { return featureDao.queryIn(queryIdsSQL(), where, whereArgs); } /** * Query for features * * @param columns columns * @param where where clause * @param whereArgs where arguments * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, String where, String[] whereArgs) { return featureDao.queryIn(columns, queryIdsSQL(), where, whereArgs); } /** * Count features * * @param where where clause * @param whereArgs where arguments * @return count * @since 3.4.0 */ public int countFeatures(String where, String[] whereArgs) { return featureDao.countIn(queryIdsSQL(), where, whereArgs); } /** * Query for Features within the bounding box, projected correctly * * @param boundingBox bounding box * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(BoundingBox boundingBox) { return queryFeatures(boundingBox.buildEnvelope()); } /** * Query for Features within the bounding box, projected correctly * * @param columns columns * @param boundingBox bounding box * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, BoundingBox boundingBox) { return queryFeatures(columns, boundingBox.buildEnvelope()); } /** * Count the Features within the bounding box, projected correctly * * @param boundingBox bounding box * @return count * @since 3.4.0 */ public int countFeatures(BoundingBox boundingBox) { return countFeatures(boundingBox.buildEnvelope()); } /** * Query for Features within the bounding box, projected correctly * * @param boundingBox bounding box * @param fieldValues field values * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(BoundingBox boundingBox, Map<String, Object> fieldValues) { return queryFeatures(boundingBox.buildEnvelope(), fieldValues); } /** * Query for Features within the bounding box, projected correctly * * @param columns columns * @param boundingBox bounding box * @param fieldValues field values * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, BoundingBox boundingBox, Map<String, Object> fieldValues) { return queryFeatures(columns, boundingBox.buildEnvelope(), fieldValues); } /** * Count the Features within the bounding box, projected correctly * * @param boundingBox bounding box * @param fieldValues field values * @return count * @since 3.4.0 */ public int countFeatures(BoundingBox boundingBox, Map<String, Object> fieldValues) { return countFeatures(boundingBox.buildEnvelope(), fieldValues); } /** * Query for Features within the bounding box, projected correctly * * @param boundingBox bounding box * @param where where clause * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(BoundingBox boundingBox, String where) { return queryFeatures(boundingBox, where, null); } /** * Query for Features within the bounding box, projected correctly * * @param columns columns * @param boundingBox bounding box * @param where where clause * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, BoundingBox boundingBox, String where) { return queryFeatures(columns, boundingBox, where, null); } /** * Count the Features within the bounding box, projected correctly * * @param boundingBox bounding box * @param where where clause * @return count * @since 3.4.0 */ public int countFeatures(BoundingBox boundingBox, String where) { return countFeatures(boundingBox, where, null); } /** * Query for Features within the bounding box, projected correctly * * @param boundingBox bounding box * @param where where clause * @param whereArgs where arguments * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(BoundingBox boundingBox, String where, String[] whereArgs) { return queryFeatures(boundingBox.buildEnvelope(), where, whereArgs); } /** * Query for Features within the bounding box, projected correctly * * @param columns columns * @param boundingBox bounding box * @param where where clause * @param whereArgs where arguments * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, BoundingBox boundingBox, String where, String[] whereArgs) { return queryFeatures(columns, boundingBox.buildEnvelope(), where, whereArgs); } /** * Count the Features within the bounding box, projected correctly * * @param boundingBox bounding box * @param where where clause * @param whereArgs where arguments * @return count * @since 3.4.0 */ public int countFeatures(BoundingBox boundingBox, String where, String[] whereArgs) { return countFeatures(boundingBox.buildEnvelope(), where, whereArgs); } /** * Query for Features within the bounding box in the provided projection * * @param boundingBox bounding box * @param projection projection of the provided bounding box * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(BoundingBox boundingBox, Projection projection) { BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox, projection); return queryFeatures(featureBoundingBox); } /** * Query for Features within the bounding box in the provided projection * * @param columns columns * @param boundingBox bounding box * @param projection projection of the provided bounding box * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, BoundingBox boundingBox, Projection projection) { BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox, projection); return queryFeatures(columns, featureBoundingBox); } /** * Count the Features within the bounding box in the provided projection * * @param boundingBox bounding box * @param projection projection of the provided bounding box * @return count * @since 3.4.0 */ public int countFeatures(BoundingBox boundingBox, Projection projection) { BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox, projection); return countFeatures(featureBoundingBox); } /** * Query for Features within the bounding box in the provided projection * * @param boundingBox bounding box * @param projection projection of the provided bounding box * @param fieldValues field values * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(BoundingBox boundingBox, Projection projection, Map<String, Object> fieldValues) { BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox, projection); return queryFeatures(featureBoundingBox, fieldValues); } /** * Query for Features within the bounding box in the provided projection * * @param columns columns * @param boundingBox bounding box * @param projection projection of the provided bounding box * @param fieldValues field values * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, BoundingBox boundingBox, Projection projection, Map<String, Object> fieldValues) { BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox, projection); return queryFeatures(columns, featureBoundingBox, fieldValues); } /** * Count the Features within the bounding box in the provided projection * * @param boundingBox bounding box * @param projection projection of the provided bounding box * @param fieldValues field values * @return count * @since 3.4.0 */ public int countFeatures(BoundingBox boundingBox, Projection projection, Map<String, Object> fieldValues) { BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox, projection); return countFeatures(featureBoundingBox, fieldValues); } /** * Query for Features within the bounding box in the provided projection * * @param boundingBox bounding box * @param projection projection of the provided bounding box * @param where where clause * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(BoundingBox boundingBox, Projection projection, String where) { return queryFeatures(boundingBox, projection, where, null); } /** * Query for Features within the bounding box in the provided projection * * @param columns columns * @param boundingBox bounding box * @param projection projection of the provided bounding box * @param where where clause * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, BoundingBox boundingBox, Projection projection, String where) { return queryFeatures(columns, boundingBox, projection, where, null); } /** * Count the Features within the bounding box in the provided projection * * @param boundingBox bounding box * @param projection projection of the provided bounding box * @param where where clause * @return count * @since 3.4.0 */ public int countFeatures(BoundingBox boundingBox, Projection projection, String where) { return countFeatures(boundingBox, projection, where, null); } /** * Query for Features within the bounding box in the provided projection * * @param boundingBox bounding box * @param projection projection of the provided bounding box * @param where where clause * @param whereArgs where arguments * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(BoundingBox boundingBox, Projection projection, String where, String[] whereArgs) { BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox, projection); return queryFeatures(featureBoundingBox, where, whereArgs); } /** * Query for Features within the bounding box in the provided projection * * @param columns columns * @param boundingBox bounding box * @param projection projection of the provided bounding box * @param where where clause * @param whereArgs where arguments * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, BoundingBox boundingBox, Projection projection, String where, String[] whereArgs) { BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox, projection); return queryFeatures(columns, featureBoundingBox, where, whereArgs); } /** * Count the Features within the bounding box in the provided projection * * @param boundingBox bounding box * @param projection projection of the provided bounding box * @param where where clause * @param whereArgs where arguments * @return count * @since 3.4.0 */ public int countFeatures(BoundingBox boundingBox, Projection projection, String where, String[] whereArgs) { BoundingBox featureBoundingBox = getFeatureBoundingBox(boundingBox, projection); return countFeatures(featureBoundingBox, where, whereArgs); } /** * Query for Features within the Geometry Envelope * * @param envelope geometry envelope * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(GeometryEnvelope envelope) { return featureDao.queryIn(queryIdsSQL(envelope)); } /** * Query for Features within the Geometry Envelope * * @param columns columns * @param envelope geometry envelope * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, GeometryEnvelope envelope) { return featureDao.queryIn(columns, queryIdsSQL(envelope)); } /** * Count the Features within the Geometry Envelope * * @param envelope geometry envelope * @return count * @since 3.4.0 */ public int countFeatures(GeometryEnvelope envelope) { return featureDao.countIn(queryIdsSQL(envelope)); } /** * Query for Features within the Geometry Envelope * * @param envelope geometry envelope * @param fieldValues field values * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(GeometryEnvelope envelope, Map<String, Object> fieldValues) { return featureDao.queryIn(queryIdsSQL(envelope), fieldValues); } /** * Query for Features within the Geometry Envelope * * @param columns columns * @param envelope geometry envelope * @param fieldValues field values * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, GeometryEnvelope envelope, Map<String, Object> fieldValues) { return featureDao.queryIn(columns, queryIdsSQL(envelope), fieldValues); } /** * Count the Features within the Geometry Envelope * * @param envelope geometry envelope * @param fieldValues field values * @return count * @since 3.4.0 */ public int countFeatures(GeometryEnvelope envelope, Map<String, Object> fieldValues) { return featureDao.countIn(queryIdsSQL(envelope), fieldValues); } /** * Query for Features within the Geometry Envelope * * @param envelope geometry envelope * @param where where clause * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(GeometryEnvelope envelope, String where) { return queryFeatures(envelope, where, null); } /** * Query for Features within the Geometry Envelope * * @param columns columns * @param envelope geometry envelope * @param where where clause * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, GeometryEnvelope envelope, String where) { return queryFeatures(columns, envelope, where, null); } /** * Count the Features within the Geometry Envelope * * @param envelope geometry envelope * @param where where clause * @return count * @since 3.4.0 */ public int countFeatures(GeometryEnvelope envelope, String where) { return countFeatures(envelope, where, null); } /** * Query for Features within the Geometry Envelope * * @param envelope geometry envelope * @param where where clause * @param whereArgs where arguments * @return feature cursor * @since 3.4.0 */ public FeatureCursor queryFeatures(GeometryEnvelope envelope, String where, String[] whereArgs) { return featureDao.queryIn(queryIdsSQL(envelope), where, whereArgs); } /** * Query for Features within the Geometry Envelope * * @param columns columns * @param envelope geometry envelope * @param where where clause * @param whereArgs where arguments * @return feature results * @since 3.5.0 */ public FeatureCursor queryFeatures(String[] columns, GeometryEnvelope envelope, String where, String[] whereArgs) { return featureDao.queryIn(columns, queryIdsSQL(envelope), where, whereArgs); } /** * Count the Features within the Geometry Envelope * * @param envelope geometry envelope * @param where where clause * @param whereArgs where arguments * @return count * @since 3.4.0 */ public int countFeatures(GeometryEnvelope envelope, String where, String[] whereArgs) { return featureDao.countIn(queryIdsSQL(envelope), where, whereArgs); } }