geomesa.tutorial.QueryTutorial.java Source code

Java tutorial

Introduction

Here is the source code for geomesa.tutorial.QueryTutorial.java

Source

package geomesa.tutorial;

import org.locationtech.geomesa.core.data.AccumuloFeatureStore;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.Query;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.filter.text.cql2.CQLException;
import org.opengis.feature.Feature;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;

import java.io.IOException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

/**
 * Copyright 2014 Commonwealth Computer Research, Inc.
 * <p/>
 * Licensed 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
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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.
 */

public class QueryTutorial {

    private static final String FEATURE_NAME_ARG = "featureName";

    /**
     * Creates a base filter that will return a small subset of our results. This can be tweaked to
     * return different results if desired. Currently it should return 16 results.
     *
     * @return
     *
     * @throws CQLException
     * @throws IOException
     */
    static Filter createBaseFilter() throws CQLException, IOException {

        // Get a FilterFactory2 to build up our query
        FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();

        // We are going to query for events in Ukraine during the
        // civil unrest.

        // We'll start by looking at a particular day in February of 2014
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.YEAR, 2014);
        calendar.set(Calendar.MONTH, Calendar.FEBRUARY);
        calendar.set(Calendar.DAY_OF_MONTH, 2);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        Date start = calendar.getTime();

        calendar.set(Calendar.HOUR_OF_DAY, 23);
        Date end = calendar.getTime();

        Filter timeFilter = ff.between(ff.property(GdeltFeature.Attributes.SQLDATE.getName()), ff.literal(start),
                ff.literal(end));

        // We'll bound our query spatially to Ukraine
        Filter spatialFilter = ff.bbox(GdeltFeature.Attributes.geom.getName(), 22.1371589, 44.386463, 40.228581,
                52.379581, "EPSG:4326");

        // we'll also restrict our query to only articles about the US, UK or UN
        Filter attributeFilter = ff.like(ff.property(GdeltFeature.Attributes.Actor1Name.getName()), "UNITED%");

        // Now we can combine our filters using a boolean AND operator
        Filter conjunction = ff.and(Arrays.asList(timeFilter, spatialFilter, attributeFilter));

        return conjunction;
    }

    /**
     * Executes a basic bounding box query without any projections.
     *
     * @param simpleFeatureTypeName
     * @param featureSource
     *
     * @throws IOException
     * @throws CQLException
     */
    static void basicQuery(String simpleFeatureTypeName, FeatureSource featureSource)
            throws IOException, CQLException {

        System.out.println("Submitting basic query with no projections\n");

        // start with our basic filter to narrow the results
        Filter cqlFilter = createBaseFilter();

        // use the 2-arg constructor for the query - this will not restrict the attributes returned
        Query query = new Query(simpleFeatureTypeName, cqlFilter);

        // execute the query
        FeatureCollection results = featureSource.getFeatures(query);

        // loop through all results
        FeatureIterator iterator = results.features();
        try {
            printResults(iterator);
        } finally {
            iterator.close();
        }
    }

    /**
     * Executes a query that restricts the attributes coming back.
     *
     * @param simpleFeatureTypeName
     * @param featureSource
     *
     * @throws IOException
     * @throws CQLException
     */
    static void basicProjectionQuery(String simpleFeatureTypeName, FeatureSource featureSource)
            throws IOException, CQLException {
        System.out.println("Submitting basic projection query");

        // start with our basic filter to narrow the results
        Filter cqlFilter = createBaseFilter();

        // define the properties (attributes) that we want returned as a string array
        // each element of the array is a property name we want returned
        String[] properties = new String[] { GdeltFeature.Attributes.Actor1Name.getName(),
                GdeltFeature.Attributes.geom.getName() };

        // create the query - we use the extended constructor to pass in our projection
        Query query = new Query(simpleFeatureTypeName, cqlFilter, properties);

        // execute the query
        FeatureCollection results = featureSource.getFeatures(query);

        // loop through all results
        FeatureIterator iterator = results.features();
        try {
            printResults(iterator);
        } finally {
            iterator.close();
        }
    }

    /**
     * Executes a query that transforms the results coming back to say 'hello' to each result.
     *
     * @param simpleFeatureTypeName
     * @param featureSource
     *
     * @throws IOException
     * @throws CQLException
     */
    static void basicTransformationQuery(String simpleFeatureTypeName, FeatureSource featureSource)
            throws IOException, CQLException {
        System.out.println("Submitting basic tranformation query");

        // start with our basic filter to narrow the results
        Filter cqlFilter = createBaseFilter();

        // define the properties that we want returned
        // this also allows us to manipulate properties using various GeoTools transforms.
        // In this case, we are using a string concatenation to say 'hello' to our results. We
        // are overwriting the existing field with the results of the transform.
        String[] properties = new String[] {
                GdeltFeature.Attributes.Actor1Name.getName() + "=strConcat('hello '," + ""
                        + GdeltFeature.Attributes.Actor1Name.getName() + ")",
                GdeltFeature.Attributes.geom.getName() };

        // create the query - we use the extended constructor to pass in our transform
        Query query = new Query(simpleFeatureTypeName, cqlFilter, properties);

        // execute the query
        FeatureCollection results = featureSource.getFeatures(query);

        // loop through all results
        FeatureIterator iterator = results.features();
        try {
            printResults(iterator);
        } finally {
            iterator.close();
        }
    }

    /**
     * Executes a query that returns a new dynamic field name created by transforming a field.
     *
     * @param simpleFeatureTypeName
     * @param featureSource
     *
     * @throws IOException
     * @throws CQLException
     */
    static void renamedTransformationQuery(String simpleFeatureTypeName, FeatureSource featureSource)
            throws IOException, CQLException {
        System.out.println("Submitting renaming tranformation query");

        // start with our basic filter to narrow the results
        Filter cqlFilter = createBaseFilter();

        // define the properties that we want returned
        // this also allows us to manipulate properties using various GeoTools transforms.
        // In this case, we are using a string concatenation to say 'hello' to our results. We are
        // storing the result of the transform in a new dynamic field, called 'derived'. We also
        // return the original attribute unchanged.
        String[] properties = new String[] { GdeltFeature.Attributes.Actor1Name.getName(),
                "derived=strConcat('hello '," + GdeltFeature.Attributes.Actor1Name + ")",
                GdeltFeature.Attributes.geom.getName() };

        // create the query - we use the extended constructor to pass in our transform
        Query query = new Query(simpleFeatureTypeName, cqlFilter, properties);

        // execute the query
        FeatureCollection results = featureSource.getFeatures(query);

        // loop through all results
        FeatureIterator iterator = results.features();
        try {
            printResults(iterator, "derived");
        } finally {
            iterator.close();
        }
    }

    /**
     * Executes a query with a transformation on multiple fields.
     *
     * @param simpleFeatureTypeName
     * @param featureSource
     *
     * @throws IOException
     * @throws CQLException
     */
    static void mutliFieldTransformationQuery(String simpleFeatureTypeName, FeatureSource featureSource)
            throws IOException, CQLException {
        System.out.println("Submitting mutli-field tranformation query");

        // start with our basic filter to narrow the results
        Filter cqlFilter = createBaseFilter();

        // define the properties that we want returned
        // this also allows us to manipulate properties using various GeoTools transforms.
        // In this case, we are concatenating two different attributes.
        String[] properties = new String[] {
                "derived=strConcat(strConcat(" + GdeltFeature.Attributes.Actor1Name + ",' - '),"
                        + GdeltFeature.Attributes.Actor1Geo_FullName + ")",
                GdeltFeature.Attributes.geom.getName() };

        // create the query - we use the extended constructor to pass in our transform
        Query query = new Query(simpleFeatureTypeName, cqlFilter, properties);

        // execute the query
        FeatureCollection results = featureSource.getFeatures(query);

        // loop through all results
        FeatureIterator iterator = results.features();
        try {
            printResults(iterator, "derived");
        } finally {
            iterator.close();
        }
    }

    /**
     * Executes a query that performs a geometric function transform on the result set.
     *
     * @param simpleFeatureTypeName
     * @param featureSource
     *
     * @throws IOException
     * @throws CQLException
     */
    static void geometricTransformationQuery(String simpleFeatureTypeName, FeatureSource featureSource)
            throws IOException, CQLException {
        System.out.println("Submitting geometric tranformation query");

        // start with our basic filter to narrow the results
        Filter cqlFilter = createBaseFilter();

        // define the properties that we want returned
        // this also allows us to manipulate properties using various GeoTools transforms.
        // In this case, we are buffering the point to create a polygon. The transformed field gets
        // renamed to 'derived'.
        String[] properties = new String[] { GdeltFeature.Attributes.geom.getName(),
                "derived=buffer(" + GdeltFeature.Attributes.geom.getName() + ", 2)" };

        // create the query - we use the extended constructor to pass in our transform
        Query query = new Query(simpleFeatureTypeName, cqlFilter, properties);

        // execute the query
        FeatureCollection results = featureSource.getFeatures(query);

        // loop through all results
        FeatureIterator iterator = results.features();
        try {
            printResults(iterator, "derived");
        } finally {
            iterator.close();
        }
    }

    /**
     * Iterates through the given iterator and prints out the properties (attributes) for each entry.
     *
     * @param iterator
     */
    private static void printResults(FeatureIterator iterator, String... derivedAttributes) {

        if (iterator.hasNext()) {
            System.out.println("Results:");
        } else {
            System.out.println("No results");
        }
        int n = 0;
        while (iterator.hasNext()) {
            Feature feature = iterator.next();
            StringBuilder result = new StringBuilder();
            result.append(++n);

            for (GdeltFeature.Attributes attribute : GdeltFeature.Attributes.values()) {
                try {
                    Property property = feature.getProperty(attribute.getName());
                    appendResult(result, property);
                } catch (Exception e) {
                    // GEOMESA-280 - currently asking for non-existing properties throws an NPE
                }
            }
            for (String derivedAttibute : derivedAttributes) {
                Property property = feature.getProperty(derivedAttibute);
                appendResult(result, property);
            }
            System.out.println(result.toString());
        }
        System.out.println();
    }

    /**
     * Append the property to the result
     *
     * @param string
     * @param property
     */
    private static void appendResult(StringBuilder string, Property property) {
        if (property != null) {
            string.append("|").append(property.getName()).append('=').append(property.getValue());
        }
    }

    /**
     * Main entry point. Executes queries against an existing GDELT dataset.
     *
     * @param args
     *
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        // read command line options - this contains the connection to accumulo and the table to query
        CommandLineParser parser = new BasicParser();
        Options options = SetupUtil.getCommonRequiredOptions();
        options.addOption(OptionBuilder.withArgName(FEATURE_NAME_ARG).hasArg().isRequired()
                .withDescription("the FeatureTypeName used to store the GDELT data, e.g.:  gdelt")
                .create(FEATURE_NAME_ARG));
        CommandLine cmd = parser.parse(options, args);

        // verify that we can see this Accumulo destination in a GeoTools manner
        Map<String, String> dsConf = SetupUtil.getAccumuloDataStoreConf(cmd);
        //Disable states collection
        dsConf.put("collectStats", "false");
        DataStore dataStore = DataStoreFinder.getDataStore(dsConf);
        assert dataStore != null;

        // create the simple feature type for our test
        String simpleFeatureTypeName = cmd.getOptionValue(FEATURE_NAME_ARG);
        SimpleFeatureType simpleFeatureType = GdeltFeature.buildGdeltFeatureType(simpleFeatureTypeName);

        // get the feature store used to query the GeoMesa data
        FeatureStore featureStore = (AccumuloFeatureStore) dataStore.getFeatureSource(simpleFeatureTypeName);

        // execute some queries
        basicQuery(simpleFeatureTypeName, featureStore);
        basicProjectionQuery(simpleFeatureTypeName, featureStore);
        basicTransformationQuery(simpleFeatureTypeName, featureStore);
        renamedTransformationQuery(simpleFeatureTypeName, featureStore);
        mutliFieldTransformationQuery(simpleFeatureTypeName, featureStore);
        geometricTransformationQuery(simpleFeatureTypeName, featureStore);

        // the list of available transform functions is available here:
        // http://docs.geotools.org/latest/userguide/library/main/filter.html - scroll to 'Function List'
    }

}