blob: 778b5efa259ee3a3693811835bb2ca4b0b79143d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.rya.indexing.accumulo.geo;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.minicluster.impl.MiniAccumuloClusterImpl;
import org.apache.accumulo.minicluster.impl.MiniAccumuloConfigImpl;
import org.apache.commons.io.FileUtils;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Polygon;
import mil.nga.giat.geowave.adapter.vector.FeatureDataAdapter;
import mil.nga.giat.geowave.adapter.vector.query.cql.CQLQuery;
import mil.nga.giat.geowave.core.geotime.GeometryUtils;
import mil.nga.giat.geowave.core.geotime.ingest.SpatialDimensionalityTypeProvider;
import mil.nga.giat.geowave.core.geotime.store.query.SpatialQuery;
import mil.nga.giat.geowave.core.store.CloseableIterator;
import mil.nga.giat.geowave.core.store.DataStore;
import mil.nga.giat.geowave.core.store.IndexWriter;
import mil.nga.giat.geowave.core.store.index.PrimaryIndex;
import mil.nga.giat.geowave.core.store.query.QueryOptions;
import mil.nga.giat.geowave.datastore.accumulo.AccumuloDataStore;
import mil.nga.giat.geowave.datastore.accumulo.BasicAccumuloOperations;
import mil.nga.giat.geowave.datastore.accumulo.minicluster.MiniAccumuloClusterFactory;
/**
* This class is intended to provide a self-contained, easy-to-follow example of
* a few GeoTools queries against GeoWave. For simplicity, a MiniAccumuloCluster
* is spun up and a few points from the DC area are ingested (Washington
* Monument, White House, FedEx Field). Two queries are executed against this
* data set.
*/
public class GeoWaveGTQueryTest {
private static File tempAccumuloDir;
private static MiniAccumuloClusterImpl accumulo;
private static DataStore dataStore;
private static final PrimaryIndex INDEX = new SpatialDimensionalityTypeProvider().createPrimaryIndex();
// Points (to be ingested into GeoWave Data Store)
private static final Coordinate WASHINGTON_MONUMENT = new Coordinate(-77.0352, 38.8895);
private static final Coordinate WHITE_HOUSE = new Coordinate(-77.0366, 38.8977);
private static final Coordinate FEDEX_FIELD = new Coordinate(-76.8644, 38.9078);
// cities used to construct Geometries for queries
private static final Coordinate BALTIMORE = new Coordinate(-76.6167, 39.2833);
private static final Coordinate RICHMOND = new Coordinate(-77.4667, 37.5333);
private static final Coordinate HARRISONBURG = new Coordinate(-78.8689, 38.4496);
private static final Map<String, Coordinate> CANNED_DATA = ImmutableMap.of(
"Washington Monument", WASHINGTON_MONUMENT,
"White House", WHITE_HOUSE,
"FedEx Field", FEDEX_FIELD
);
private static final FeatureDataAdapter ADAPTER = new FeatureDataAdapter(getPointSimpleFeatureType());
private static final String ACCUMULO_USER = "root";
private static final String ACCUMULO_PASSWORD = "password";
private static final String TABLE_NAMESPACE = "";
@BeforeClass
public static void setup() throws AccumuloException, AccumuloSecurityException, IOException, InterruptedException {
tempAccumuloDir = Files.createTempDir();
accumulo = MiniAccumuloClusterFactory.newAccumuloCluster(
new MiniAccumuloConfigImpl(tempAccumuloDir, ACCUMULO_PASSWORD),
GeoWaveGTQueryTest.class);
accumulo.start();
dataStore = new AccumuloDataStore(
new BasicAccumuloOperations(
accumulo.getZooKeepers(),
accumulo.getInstanceName(),
ACCUMULO_USER,
ACCUMULO_PASSWORD,
TABLE_NAMESPACE));
ingestCannedData();
}
private static void ingestCannedData() throws IOException {
final List<SimpleFeature> points = new ArrayList<>();
System.out.println("Building SimpleFeatures from canned data set...");
for (final Entry<String, Coordinate> entry : CANNED_DATA.entrySet()) {
System.out.println("Added point: " + entry.getKey());
points.add(buildSimpleFeature(entry.getKey(), entry.getValue()));
}
System.out.println("Ingesting canned data...");
try (final IndexWriter<SimpleFeature> indexWriter = dataStore.createWriter(ADAPTER, INDEX)) {
for (final SimpleFeature sf : points) {
indexWriter.write(sf);
}
}
System.out.println("Ingest complete.");
}
@Test
public void executeCQLQueryTest() throws IOException, CQLException {
System.out.println("Executing query, expecting to match two points...");
final Filter cqlFilter = ECQL.toFilter("BBOX(geometry,-77.6167,38.6833,-76.6,38.9200) and locationName like 'W%'");
final QueryOptions queryOptions = new QueryOptions(ADAPTER, INDEX);
final CQLQuery cqlQuery = new CQLQuery(null, cqlFilter, ADAPTER);
try (final CloseableIterator<SimpleFeature> iterator = dataStore.query(queryOptions, cqlQuery)) {
int count = 0;
while (iterator.hasNext()) {
System.out.println("Query match: " + iterator.next().getID());
count++;
}
System.out.println("executeCQLQueryTest count: " + count);
// Should match "Washington Monument" and "White House"
assertEquals(2, count);
}
}
@Test
public void executeBoundingBoxQueryTest() throws IOException {
System.out.println("Constructing bounding box for the area contained by [Baltimore, MD and Richmond, VA.");
final Geometry boundingBox = GeometryUtils.GEOMETRY_FACTORY.toGeometry(new Envelope(
BALTIMORE,
RICHMOND));
System.out.println("Executing query, expecting to match ALL points...");
final QueryOptions queryOptions = new QueryOptions(ADAPTER, INDEX);
final SpatialQuery spatialQuery = new SpatialQuery(boundingBox);
try (final CloseableIterator<SimpleFeature> iterator = dataStore.query(queryOptions, spatialQuery)) {
int count = 0;
while (iterator.hasNext()) {
System.out.println("Query match: " + iterator.next().getID());
count++;
}
System.out.println("executeBoundingBoxQueryTest count: " + count);
// Should match "FedEx Field", "Washington Monument", and "White House"
assertEquals(3, count);
}
}
@Test
public void executePolygonQueryTest() throws IOException {
System.out.println("Constructing polygon for the area contained by [Baltimore, MD; Richmond, VA; Harrisonburg, VA].");
final Polygon polygon = GeometryUtils.GEOMETRY_FACTORY.createPolygon(new Coordinate[] {
BALTIMORE,
RICHMOND,
HARRISONBURG,
BALTIMORE
});
System.out.println("Executing query, expecting to match ALL points...");
final QueryOptions queryOptions = new QueryOptions(ADAPTER, INDEX);
final SpatialQuery spatialQuery = new SpatialQuery(polygon);
/*
* NOTICE: In this query, the adapter is added to the query options. If
* an index has data from more than one adapter, the data associated
* with a specific adapter can be selected.
*/
try (final CloseableIterator<SimpleFeature> closableIterator = dataStore.query(queryOptions, spatialQuery)) {
int count = 0;
while (closableIterator.hasNext()) {
System.out.println("Query match: " + closableIterator.next().getID());
count++;
}
System.out.println("executePolygonQueryTest count: " + count);
// Should match "FedEx Field", "Washington Monument", and "White House"
assertEquals(3, count);
}
}
@AfterClass
public static void cleanup() throws IOException, InterruptedException {
try {
accumulo.stop();
} finally {
FileUtils.deleteDirectory(tempAccumuloDir);
}
}
private static SimpleFeatureType getPointSimpleFeatureType() {
final String name = "PointSimpleFeatureType";
final SimpleFeatureTypeBuilder sftBuilder = new SimpleFeatureTypeBuilder();
final AttributeTypeBuilder atBuilder = new AttributeTypeBuilder();
sftBuilder.setName(name);
sftBuilder.add(atBuilder.binding(String.class).nillable(false)
.buildDescriptor("locationName"));
sftBuilder.add(atBuilder.binding(Geometry.class).nillable(false)
.buildDescriptor("geometry"));
return sftBuilder.buildFeatureType();
}
private static SimpleFeature buildSimpleFeature(final String locationName, final Coordinate coordinate) {
final SimpleFeatureBuilder builder = new SimpleFeatureBuilder(getPointSimpleFeatureType());
builder.set("locationName", locationName);
builder.set("geometry", GeometryUtils.GEOMETRY_FACTORY.createPoint(coordinate));
return builder.buildFeature(locationName);
}
}