package mil.nga.giat.geowave.examples.ingest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import mil.nga.giat.geowave.adapter.vector.FeatureDataAdapter;
import mil.nga.giat.geowave.adapter.vector.GeotoolsFeatureDataAdapter;
import mil.nga.giat.geowave.core.geotime.GeometryUtils;
import mil.nga.giat.geowave.core.geotime.ingest.SpatialDimensionalityTypeProvider.SpatialIndexBuilder;
import mil.nga.giat.geowave.core.store.DataStore;
import mil.nga.giat.geowave.core.store.index.PrimaryIndex;
import mil.nga.giat.geowave.datastore.accumulo.AccumuloDataStore;
import mil.nga.giat.geowave.datastore.accumulo.BasicAccumuloOperations;
import mil.nga.giat.geowave.datastore.accumulo.index.secondary.AccumuloSecondaryIndexDataStore;
import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloAdapterIndexMappingStore;
import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloAdapterStore;
import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloDataStatisticsStore;
import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloIndexStore;
public class SimpleIngest
{
static Logger log = LoggerFactory.getLogger(SimpleIngest.class);
public static final String FEATURE_NAME = "GridPoint";
public static List<SimpleFeature> getGriddedFeatures(
final SimpleFeatureBuilder pointBuilder,
final int firstFeatureId ) {
int featureId = firstFeatureId;
final List<SimpleFeature> feats = new ArrayList<>();
for (int longitude = -180; longitude <= 180; longitude += 5) {
for (int latitude = -90; latitude <= 90; latitude += 5) {
pointBuilder.set(
"geometry",
GeometryUtils.GEOMETRY_FACTORY.createPoint(new Coordinate(
longitude,
latitude)));
pointBuilder.set(
"TimeStamp",
new Date());
pointBuilder.set(
"Latitude",
latitude);
pointBuilder.set(
"Longitude",
longitude);
// Note since trajectoryID and comment are marked as nillable we
// don't need to set them (they default ot null).
final SimpleFeature sft = pointBuilder.buildFeature(String.valueOf(featureId));
feats.add(sft);
featureId++;
}
}
return feats;
}
/***
* DataStore is essentially the controller that take the accumulo
* information, geowave configuration, and data type, and inserts/queries
* from accumulo
*
* @param instance
* Accumulo instance configuration
* @return DataStore object for the particular accumulo instance
*/
protected DataStore getAccumuloGeowaveDataStore(
final BasicAccumuloOperations instance ) {
// GeoWave persists both the index and data adapter to the same accumulo
// namespace as the data. The intent here
// is that all data is discoverable without configuration/classes stored
// outside of the accumulo instance.
return new AccumuloDataStore(
new AccumuloIndexStore(
instance),
new AccumuloAdapterStore(
instance),
new AccumuloDataStatisticsStore(
instance),
new AccumuloSecondaryIndexDataStore(
instance),
new AccumuloAdapterIndexMappingStore(
instance),
instance);
}
/***
* The class tells geowave about the accumulo instance it should connect to,
* as well as what tables it should create/store it's data in
*
* @param zookeepers
* Zookeepers associated with the accumulo instance, comma
* separate
* @param accumuloInstance
* Accumulo instance name
* @param accumuloUser
* User geowave should connect to accumulo as
* @param accumuloPass
* Password for user to connect to accumulo
* @param geowaveNamespace
* Different than an accumulo namespace (unfortunate naming
* usage) - this is basically a prefix on the table names geowave
* uses.
* @return Object encapsulating the accumulo connection information
* @throws AccumuloException
* @throws AccumuloSecurityException
*/
protected BasicAccumuloOperations getAccumuloOperationsInstance(
final String zookeepers,
final String accumuloInstance,
final String accumuloUser,
final String accumuloPass,
final String geowaveNamespace )
throws AccumuloException,
AccumuloSecurityException {
return new BasicAccumuloOperations(
zookeepers,
accumuloInstance,
accumuloUser,
accumuloPass,
geowaveNamespace);
}
/***
* The dataadapter interface describes how to serialize a data type. Here we
* are using an implementation that understands how to serialize OGC
* SimpleFeature types.
*
* @param sft
* simple feature type you want to generate an adapter from
* @return data adapter that handles serialization of the sft simple feature
* type
*/
public static GeotoolsFeatureDataAdapter createDataAdapter(
final SimpleFeatureType sft ) {
return new FeatureDataAdapter(
sft);
}
/***
* We need an index model that tells us how to index the data - the index
* determines -What fields are indexed -The precision of the index -The
* range of the index (min/max values) -The range type (bounded/unbounded)
* -The number of "levels" (different precisions, needed when the values
* indexed has ranges on any dimension)
*
* @return GeoWave index for a default SPATIAL index
*/
public static PrimaryIndex createSpatialIndex() {
// Reasonable values for spatial and spatial-temporal are provided
// through index builders.
// They are intended to be a reasonable starting place - though creating
// a custom index may provide better
// performance as the distribution/characterization of the data is well
// known. There are many such customizations available through setters
// on the builder.
// for example to create a spatial-temporal index with 8 randomized
// partitions (pre-splits on accumulo or hbase) and a temporal bias
// (giving more precision to time than space) you could do something
// like this:
//@formatter:off
// return new SpatialTemporalIndexBuilder().setBias(Bias.TEMPORAL).setNumPartitions(8);
//@formatter:on
return new SpatialIndexBuilder().createIndex();
}
/***
* A simple feature is just a mechanism for defining attributes (a feature
* is just a collection of attributes + some metadata) We need to describe
* what our data looks like so the serializer (FeatureDataAdapter for this
* case) can know how to store it. Features/Attributes are also a general
* convention of GIS systems in general.
*
* @return Simple Feature definition for our demo point feature
*/
public static SimpleFeatureType createPointFeatureType() {
final SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
final AttributeTypeBuilder ab = new AttributeTypeBuilder();
// Names should be unique (at least for a given GeoWave namespace) -
// think about names in the same sense as a full classname
// The value you set here will also persist through discovery - so when
// people are looking at a dataset they will see the
// type names associated with the data.
builder.setName(FEATURE_NAME);
// The data is persisted in a sparse format, so if data is nullable it
// will not take up any space if no values are persisted.
// Data which is included in the primary index (in this example
// lattitude/longtiude) can not be null
// Calling out latitude an longitude separately is not strictly needed,
// as the geometry contains that information. But it's
// convienent in many use cases to get a text representation without
// having to handle geometries.
builder.add(ab.binding(
Geometry.class).nillable(
false).buildDescriptor(
"geometry"));
builder.add(ab.binding(
Date.class).nillable(
true).buildDescriptor(
"TimeStamp"));
builder.add(ab.binding(
Double.class).nillable(
false).buildDescriptor(
"Latitude"));
builder.add(ab.binding(
Double.class).nillable(
false).buildDescriptor(
"Longitude"));
builder.add(ab.binding(
String.class).nillable(
true).buildDescriptor(
"TrajectoryID"));
builder.add(ab.binding(
String.class).nillable(
true).buildDescriptor(
"Comment"));
return builder.buildFeatureType();
}
}