Java Code Examples for mil.nga.sf.GeometryEnvelope

The following examples show how to use mil.nga.sf.GeometryEnvelope. These examples are extracted from open source projects. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source Project: geopackage-java   Source File: GeoPackageGeometryDataUtils.java    License: MIT License 6 votes vote down vote up
/**
 * Compare two geometry envelopes and verify they are equal
 * 
 * @param expected
 * @param actual
 */
private static void compareEnvelopes(GeometryEnvelope expected,
		GeometryEnvelope actual) {

	if (expected == null) {
		TestCase.assertNull(actual);
	} else {
		TestCase.assertNotNull(actual);

		TestCase.assertEquals(
				GeoPackageGeometryData.getIndicator(expected),
				GeoPackageGeometryData.getIndicator(actual));
		TestCase.assertEquals(expected.getMinX(), actual.getMinX());
		TestCase.assertEquals(expected.getMaxX(), actual.getMaxX());
		TestCase.assertEquals(expected.getMinY(), actual.getMinY());
		TestCase.assertEquals(expected.getMaxY(), actual.getMaxY());
		TestCase.assertEquals(expected.hasZ(), actual.hasZ());
		TestCase.assertEquals(expected.getMinZ(), actual.getMinZ());
		TestCase.assertEquals(expected.getMaxZ(), actual.getMaxZ());
		TestCase.assertEquals(expected.hasM(), actual.hasM());
		TestCase.assertEquals(expected.getMinM(), actual.getMinM());
		TestCase.assertEquals(expected.getMaxM(), actual.getMaxM());
	}

}
 
Example 2
Source Project: geopackage-core-java   Source File: FeatureTableCoreIndex.java    License: MIT License 6 votes vote down vote up
/**
 * Query for Geometry Index objects within the Geometry Envelope
 * 
 * @param envelope
 *            geometry envelope
 * @return geometry indices iterator
 */
public CloseableIterator<GeometryIndex> query(GeometryEnvelope envelope) {

	CloseableIterator<GeometryIndex> geometryIndices = null;

	QueryBuilder<GeometryIndex, GeometryIndexKey> qb = queryBuilder(
			envelope);
	try {
		geometryIndices = qb.iterator();
	} catch (SQLException e) {
		throw new GeoPackageException(
				"Failed to query for Geometry Indices. GeoPackage: "
						+ geoPackage.getName() + ", Table Name: "
						+ tableName + ", Column Name: " + columnName,
				e);
	}

	return geometryIndices;
}
 
Example 3
Source Project: geopackage-android   Source File: GeometryMetadataDataSource.java    License: MIT License 6 votes vote down vote up
/**
 * Populate a new geometry metadata from an envelope
 *
 * @param geoPackageId GeoPackage id
 * @param tableName    table name
 * @param geomId       geometry id
 * @param envelope     geometry envelope
 * @return geometry metadata
 */
public GeometryMetadata populate(long geoPackageId, String tableName, long geomId, GeometryEnvelope envelope) {

    GeometryMetadata metadata = new GeometryMetadata();
    metadata.setGeoPackageId(geoPackageId);
    metadata.setTableName(tableName);
    metadata.setId(geomId);
    metadata.setMinX(envelope.getMinX());
    metadata.setMaxX(envelope.getMaxX());
    metadata.setMinY(envelope.getMinY());
    metadata.setMaxY(envelope.getMaxY());
    if (envelope.hasZ()) {
        metadata.setMinZ(envelope.getMinZ());
        metadata.setMaxZ(envelope.getMaxZ());
    }
    if (envelope.hasM()) {
        metadata.setMinM(envelope.getMinM());
        metadata.setMaxM(envelope.getMaxM());
    }
    return metadata;
}
 
Example 4
Source Project: geopackage-java   Source File: RTreeIndexExtension.java    License: MIT License 5 votes vote down vote up
/**
 * {@inheritDoc}
 */
@Override
public void createMinYFunction() {
	createFunction(MIN_Y_FUNCTION, new GeometryFunction() {
		@Override
		public Object execute(GeoPackageGeometryData data) {
			Object value = null;
			GeometryEnvelope envelope = getEnvelope(data);
			if (envelope != null) {
				value = envelope.getMinY();
			}
			return value;
		}
	});
}
 
Example 5
Source Project: geopackage-core-java   Source File: GeoPackageGeometryData.java    License: MIT License 5 votes vote down vote up
/**
 * Get the envelope if it exists or build it from the geometry if not null
 * 
 * @return geometry envelope
 * @since 3.1.0
 */
public GeometryEnvelope getOrBuildEnvelope() {
	GeometryEnvelope envelope = getEnvelope();
	if (envelope == null) {
		Geometry geometry = getGeometry();
		if (geometry != null) {
			envelope = GeometryEnvelopeBuilder.buildEnvelope(geometry);
		}
	}
	return envelope;
}
 
Example 6
Source Project: geopackage-java   Source File: FeatureIndexManagerUtils.java    License: MIT License 5 votes vote down vote up
/**
 * Validate a Feature Row result
 *
 * @param featureIndexManager
 * @param featureRow
 * @param queryEnvelope
 */
private static void validateFeatureRow(
		FeatureIndexManager featureIndexManager, FeatureRow featureRow,
		GeometryEnvelope queryEnvelope, boolean includeEmpty) {
	TestCase.assertNotNull(featureRow);
	GeometryEnvelope envelope = featureRow.getGeometryEnvelope();

	if (!includeEmpty) {
		TestCase.assertNotNull(envelope);

		if (queryEnvelope != null) {
			TestCase.assertTrue(
					envelope.getMinX() <= queryEnvelope.getMaxX());
			TestCase.assertTrue(
					envelope.getMaxX() >= queryEnvelope.getMinX());
			TestCase.assertTrue(
					envelope.getMinY() <= queryEnvelope.getMaxY());
			TestCase.assertTrue(
					envelope.getMaxY() >= queryEnvelope.getMinY());
			if (envelope.isHasZ()) {
				if (queryEnvelope.hasZ()) {
					TestCase.assertTrue(
							envelope.getMinZ() <= queryEnvelope.getMaxZ());
					TestCase.assertTrue(
							envelope.getMaxZ() >= queryEnvelope.getMinZ());
				}
			}
			if (envelope.isHasM()) {
				if (queryEnvelope.hasM()) {
					TestCase.assertTrue(
							envelope.getMinM() <= queryEnvelope.getMaxM());
					TestCase.assertTrue(
							envelope.getMaxM() >= queryEnvelope.getMinM());
				}
			}
		}
	}
}
 
Example 7
Source Project: geopackage-core-java   Source File: FeatureTableCoreIndex.java    License: MIT License 5 votes vote down vote up
/**
 * Index the geometry id and geometry data
 * 
 * @param tableIndex
 *            table index
 * @param geomId
 *            geometry id
 * @param geomData
 *            geometry data
 * 
 * @return true if indexed
 */
protected boolean index(TableIndex tableIndex, long geomId,
		GeoPackageGeometryData geomData) {

	boolean indexed = false;

	if (geomData != null) {

		// Get or build the envelope
		GeometryEnvelope envelope = geomData.getOrBuildEnvelope();

		// Create the new index row
		if (envelope != null) {
			GeometryIndex geometryIndex = geometryIndexDao
					.populate(tableIndex, geomId, envelope);
			try {
				geometryIndexDao.createOrUpdate(geometryIndex);
				indexed = true;
			} catch (SQLException e) {
				throw new GeoPackageException(
						"Failed to create or update Geometry Index. GeoPackage: "
								+ geoPackage.getName() + ", Table Name: "
								+ tableName + ", Geom Id: " + geomId,
						e);
			}
		}
	}

	return indexed;
}
 
Example 8
Source Project: geopackage-core-java   Source File: BoundingBox.java    License: MIT License 5 votes vote down vote up
/**
 * Build a Geometry Envelope from the bounding box
 * 
 * @param boundingBox
 *            bounding box
 * @return geometry envelope
 * @since 3.2.0
 */
public static GeometryEnvelope buildEnvelope(BoundingBox boundingBox) {
	GeometryEnvelope envelope = new GeometryEnvelope();
	envelope.setMinX(boundingBox.getMinLongitude());
	envelope.setMaxX(boundingBox.getMaxLongitude());
	envelope.setMinY(boundingBox.getMinLatitude());
	envelope.setMaxY(boundingBox.getMaxLatitude());
	return envelope;
}
 
Example 9
Source Project: geopackage-android   Source File: FeatureIndexManagerUtils.java    License: MIT License 5 votes vote down vote up
private static FeatureIndexTestEnvelope createEnvelope(
        GeometryEnvelope envelope, int percentage) {

    FeatureIndexTestEnvelope testEnvelope = new FeatureIndexTestEnvelope();

    double minX;
    double maxX;
    double minY;
    double maxY;

    if (percentage < 100) {

        float percentageRatio = percentage / 100.0f;

        double width = envelope.getMaxX() - envelope.getMinX();
        double height = envelope.getMaxY() - envelope.getMinY();

        minX = envelope.getMinX()
                + (Math.random() * width * (1.0 - percentageRatio));
        minY = envelope.getMinY()
                + (Math.random() * height * (1.0 - percentageRatio));

        maxX = minX + (width * percentageRatio);
        maxY = minY + (height * percentageRatio);

    } else {
        minX = envelope.getMinX();
        maxX = envelope.getMaxX();
        minY = envelope.getMinY();
        maxY = envelope.getMaxY();
    }

    testEnvelope.envelope = new GeometryEnvelope(minX, minY, maxX, maxY);
    testEnvelope.percentage = percentage;

    return testEnvelope;
}
 
Example 10
Source Project: geopackage-android   Source File: FeatureIndexManager.java    License: MIT License 5 votes vote down vote up
/**
 * Query for feature index results within the Geometry Envelope
 *
 * @param columns   columns
 * @param envelope  geometry envelope
 * @param where     where clause
 * @param whereArgs where arguments
 * @return feature index results, close when done
 * @since 3.5.0
 */
public FeatureIndexResults query(String[] columns,
                                 GeometryEnvelope envelope, String where, String[] whereArgs) {
    FeatureIndexResults results = null;
    for (FeatureIndexType type : getLocation()) {
        try {
            switch (type) {
                case GEOPACKAGE:
                    FeatureCursor geoPackageCursor = featureTableIndex
                            .queryFeatures(columns, envelope, where, whereArgs);
                    results = new FeatureIndexFeatureResults(
                            geoPackageCursor);
                    break;
                case METADATA:
                    FeatureCursor geometryMetadataCursor = featureIndexer.queryFeatures(columns, envelope, where, whereArgs);
                    results = new FeatureIndexFeatureResults(geometryMetadataCursor);
                    break;
                case RTREE:
                    FeatureCursor rTreeCursor = rTreeIndexTableDao
                            .queryFeatures(columns, envelope, where, whereArgs);
                    results = new FeatureIndexFeatureResults(rTreeCursor);
                    break;
                default:
                    throw new GeoPackageException(
                            "Unsupported feature index type: " + type);
            }
            break;
        } catch (Exception e) {
            if (continueOnError) {
                Log.e(FeatureIndexManager.class.getSimpleName(), "Failed to query from feature index: " + type, e);
            } else {
                throw e;
            }
        }
    }
    if (results == null) {
        results = manualFeatureQuery.query(columns, envelope, where, whereArgs);
    }
    return results;
}
 
Example 11
Source Project: geopackage-java   Source File: RTreeIndexExtension.java    License: MIT License 5 votes vote down vote up
/**
 * {@inheritDoc}
 */
@Override
public void createMinXFunction() {
	createFunction(MIN_X_FUNCTION, new GeometryFunction() {
		@Override
		public Object execute(GeoPackageGeometryData data) {
			Object value = null;
			GeometryEnvelope envelope = getEnvelope(data);
			if (envelope != null) {
				value = envelope.getMinX();
			}
			return value;
		}
	});
}
 
Example 12
Source Project: geopackage-android   Source File: FeatureIndexManagerUtils.java    License: MIT License 5 votes vote down vote up
private static List<FeatureIndexTestEnvelope> createEnvelopes(
        GeometryEnvelope envelope) {
    List<FeatureIndexTestEnvelope> envelopes = new ArrayList<>();
    for (int percentage = 100; percentage >= 0; percentage -= 10) {
        envelopes.add(createEnvelope(envelope, percentage));
    }
    return envelopes;
}
 
Example 13
Source Project: geopackage-android   Source File: FeatureTableIndexUtils.java    License: MIT License 5 votes vote down vote up
/**
 * Validate a Geometry Index result
 *
 * @param featureTableIndex
 * @param geometryIndex
 */
private static void validateGeometryIndex(
        FeatureTableIndex featureTableIndex, GeometryIndex geometryIndex) {
    FeatureRow featureRow = featureTableIndex.getFeatureRow(geometryIndex);
    TestCase.assertNotNull(featureRow);
    TestCase.assertEquals(featureTableIndex.getTableName(),
            geometryIndex.getTableName());
    TestCase.assertEquals(geometryIndex.getGeomId(), featureRow.getId());
    GeometryEnvelope envelope = featureRow.getGeometryEnvelope();

    TestCase.assertNotNull(envelope);

    TestCase.assertEquals(envelope.getMinX(), geometryIndex.getMinX());
    TestCase.assertEquals(envelope.getMaxX(), geometryIndex.getMaxX());
    TestCase.assertEquals(envelope.getMinY(), geometryIndex.getMinY());
    TestCase.assertEquals(envelope.getMaxY(), geometryIndex.getMaxY());
    if (envelope.isHasZ()) {
        TestCase.assertEquals(envelope.getMinZ(), geometryIndex.getMinZ());
        TestCase.assertEquals(envelope.getMaxZ(), geometryIndex.getMaxZ());
    } else {
        TestCase.assertNull(geometryIndex.getMinZ());
        TestCase.assertNull(geometryIndex.getMaxZ());
    }
    if (envelope.isHasM()) {
        TestCase.assertEquals(envelope.getMinM(), geometryIndex.getMinM());
        TestCase.assertEquals(envelope.getMaxM(), geometryIndex.getMaxM());
    } else {
        TestCase.assertNull(geometryIndex.getMinM());
        TestCase.assertNull(geometryIndex.getMaxM());
    }
}
 
Example 14
Source Project: geopackage-core-java   Source File: GeoPackageGeometryData.java    License: MIT License 5 votes vote down vote up
/**
 * Get the envelope flag indicator
 * 
 * 1 for xy, 2 for xyz, 3 for xym, 4 for xyzm (null would be 0)
 * 
 * @param envelope
 *            geometry envelope
 * 
 * @return indicator
 */
public static int getIndicator(GeometryEnvelope envelope) {
	int indicator = 1;
	if (envelope.hasZ()) {
		indicator++;
	}
	if (envelope.hasM()) {
		indicator += 2;
	}
	return indicator;
}
 
Example 15
Source Project: geopackage-java   Source File: FeatureIndexManagerUtils.java    License: MIT License 5 votes vote down vote up
private static List<FeatureIndexTestEnvelope> createEnvelopes(
		GeometryEnvelope envelope) {
	List<FeatureIndexTestEnvelope> envelopes = new ArrayList<>();
	for (int percentage = 100; percentage >= 0; percentage -= 10) {
		envelopes.add(createEnvelope(envelope, percentage));
	}
	return envelopes;
}
 
Example 16
Source Project: geopackage-java   Source File: FeatureRow.java    License: MIT License 5 votes vote down vote up
/**
 * Get the geometry envelope
 * 
 * @return geometry envelope
 * @since 3.1.0
 */
public GeometryEnvelope getGeometryEnvelope() {
	GeoPackageGeometryData data = getGeometry();
	GeometryEnvelope envelope = null;
	if (data != null) {
		envelope = data.getOrBuildEnvelope();
	}
	return envelope;
}
 
Example 17
Source Project: geopackage-core-java   Source File: FeatureTableCoreIndex.java    License: MIT License 4 votes vote down vote up
/**
 * Build a query builder to query for Geometry Index objects within the
 * Geometry Envelope
 * 
 * @param envelope
 *            geometry envelope
 * @return query builder
 */
public QueryBuilder<GeometryIndex, GeometryIndexKey> queryBuilder(
		GeometryEnvelope envelope) {

	QueryBuilder<GeometryIndex, GeometryIndexKey> qb = geometryIndexDao
			.queryBuilder();
	try {

		double minX = envelope.getMinX() - tolerance;
		double maxX = envelope.getMaxX() + tolerance;
		double minY = envelope.getMinY() - tolerance;
		double maxY = envelope.getMaxY() + tolerance;

		Where<GeometryIndex, GeometryIndexKey> where = qb.where();
		where.eq(GeometryIndex.COLUMN_TABLE_NAME, tableName).and()
				.le(GeometryIndex.COLUMN_MIN_X, maxX).and()
				.ge(GeometryIndex.COLUMN_MAX_X, minX).and()
				.le(GeometryIndex.COLUMN_MIN_Y, maxY).and()
				.ge(GeometryIndex.COLUMN_MAX_Y, minY);

		if (envelope.hasZ()) {
			double minZ = envelope.getMinZ() - tolerance;
			double maxZ = envelope.getMaxZ() + tolerance;
			where.and().le(GeometryIndex.COLUMN_MIN_Z, maxZ).and()
					.ge(GeometryIndex.COLUMN_MAX_Z, minZ);
		}

		if (envelope.hasM()) {
			double minM = envelope.getMinM() - tolerance;
			double maxM = envelope.getMaxM() + tolerance;
			where.and().le(GeometryIndex.COLUMN_MIN_M, maxM).and()
					.ge(GeometryIndex.COLUMN_MAX_M, minM);
		}

	} catch (SQLException e) {
		throw new GeoPackageException(
				"Failed to build query for Geometry Indices. GeoPackage: "
						+ geoPackage.getName() + ", Table Name: "
						+ tableName + ", Column Name: " + columnName,
				e);
	}

	return qb;
}
 
Example 18
Source Project: geopackage-java   Source File: FeatureIndexManager.java    License: MIT License 3 votes vote down vote up
/**
 * Query for feature index results within the Geometry Envelope
 *
 * @param columns
 *            columns
 * @param envelope
 *            geometry envelope
 * @param fieldValues
 *            field values
 * @return feature index results, close when done
 * @since 3.5.0
 */
public FeatureIndexResults query(String[] columns,
		GeometryEnvelope envelope, Map<String, Object> fieldValues) {
	String where = featureDao.buildWhere(fieldValues.entrySet());
	String[] whereArgs = featureDao.buildWhereArgs(fieldValues.values());
	return query(columns, envelope, where, whereArgs);
}
 
Example 19
Source Project: geopackage-android   Source File: FeatureIndexManager.java    License: MIT License 3 votes vote down vote up
/**
 * Query for feature index results within the Geometry Envelope
 *
 * @param envelope    geometry envelope
 * @param fieldValues field values
 * @return feature index results, close when done
 * @since 3.4.0
 */
public FeatureIndexResults query(GeometryEnvelope envelope,
                                 Map<String, Object> fieldValues) {
    String where = featureDao.buildWhere(fieldValues.entrySet());
    String[] whereArgs = featureDao.buildWhereArgs(fieldValues.values());
    return query(envelope, where, whereArgs);
}
 
Example 20
Source Project: geopackage-android   Source File: FeatureIndexManager.java    License: MIT License 3 votes vote down vote up
/**
 * Query for feature index count within the Geometry Envelope
 *
 * @param envelope    geometry envelope
 * @param fieldValues field values
 * @return count
 * @since 3.4.0
 */
public long count(GeometryEnvelope envelope,
                  Map<String, Object> fieldValues) {
    String where = featureDao.buildWhere(fieldValues.entrySet());
    String[] whereArgs = featureDao.buildWhereArgs(fieldValues.values());
    return count(envelope, where, whereArgs);
}
 
Example 21
Source Project: geopackage-android   Source File: FeatureIndexer.java    License: MIT License 3 votes vote down vote up
/**
 * 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) {
    String where = featureDao.buildWhere(fieldValues.entrySet());
    String[] whereArgs = featureDao.buildWhereArgs(fieldValues.values());
    return queryFeatures(columns, envelope, where, whereArgs);
}
 
Example 22
Source Project: geopackage-android   Source File: FeatureIndexer.java    License: MIT License 3 votes vote down vote up
/**
 * 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) {
    String where = featureDao.buildWhere(fieldValues.entrySet());
    String[] whereArgs = featureDao.buildWhereArgs(fieldValues.values());
    return countFeatures(envelope, where, whereArgs);
}
 
Example 23
Source Project: geopackage-android   Source File: GeometryMetadataDataSource.java    License: MIT License 3 votes vote down vote up
/**
 * Create a new geometry metadata from an envelope
 *
 * @param geoPackageId GeoPackage id
 * @param tableName    table name
 * @param geomId       geometry id
 * @param envelope     geometry envelope
 * @return geometry metadata
 */
public GeometryMetadata create(long geoPackageId, String tableName, long geomId, GeometryEnvelope envelope) {

    GeometryMetadata metadata = populate(geoPackageId, tableName, geomId, envelope);
    create(metadata);
    return metadata;
}
 
Example 24
Source Project: geopackage-android   Source File: FeatureTableIndex.java    License: MIT License 2 votes vote down vote up
/**
 * 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));
}
 
Example 25
Source Project: geopackage-java   Source File: RTreeIndexTableDao.java    License: MIT License 2 votes vote down vote up
/**
 * Query for features within the geometry envelope
 * 
 * @param envelope
 *            geometry envelope
 * @param fieldValues
 *            field values
 * @return feature results
 * @since 3.4.0
 */
public FeatureResultSet queryFeatures(GeometryEnvelope envelope,
		Map<String, Object> fieldValues) {
	return queryFeatures(envelope.getMinX(), envelope.getMinY(),
			envelope.getMaxX(), envelope.getMaxY(), fieldValues);
}
 
Example 26
Source Project: geopackage-android   Source File: FeatureTableIndex.java    License: MIT License 2 votes vote down vote up
/**
 * 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));
}
 
Example 27
Source Project: geopackage-java   Source File: FeatureTableIndex.java    License: MIT License 2 votes vote down vote up
/**
 * 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 FeatureResultSet queryFeatures(String[] columns,
		GeometryEnvelope envelope, String where) {
	return queryFeatures(columns, envelope, where, null);
}
 
Example 28
Source Project: geopackage-android   Source File: FeatureTableIndex.java    License: MIT License 2 votes vote down vote up
/**
 * 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);
}
 
Example 29
Source Project: geopackage-android   Source File: FeatureTableIndex.java    License: MIT License 2 votes vote down vote up
/**
 * 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);
}
 
Example 30
Source Project: geopackage-android   Source File: FeatureTableIndex.java    License: MIT License 2 votes vote down vote up
/**
 * 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);
}