You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by mi...@apache.org on 2016/10/12 20:28:42 UTC

[2/6] incubator-rya git commit: RYA-177 adding optional geoindexing profile

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/7727b165/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoMesaGeoIndexer.java
----------------------------------------------------------------------
diff --git a/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoMesaGeoIndexer.java b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoMesaGeoIndexer.java
new file mode 100644
index 0000000..fe173af
--- /dev/null
+++ b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoMesaGeoIndexer.java
@@ -0,0 +1,518 @@
+package mvm.rya.indexing.accumulo.geo;
+
+/*
+ * 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.
+ */
+
+
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.Instance;
+import org.apache.accumulo.core.client.mock.MockInstance;
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.log4j.Logger;
+import org.geotools.data.DataStore;
+import org.geotools.data.DataStoreFinder;
+import org.geotools.data.DataUtilities;
+import org.geotools.data.FeatureSource;
+import org.geotools.data.FeatureStore;
+import org.geotools.data.Query;
+import org.geotools.factory.CommonFactoryFinder;
+import org.geotools.factory.Hints;
+import org.geotools.feature.DefaultFeatureCollection;
+import org.geotools.feature.FeatureIterator;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+import org.geotools.filter.text.cql2.CQLException;
+import org.geotools.filter.text.ecql.ECQL;
+import org.locationtech.geomesa.accumulo.data.AccumuloDataStore;
+import org.locationtech.geomesa.accumulo.index.Constants;
+import org.locationtech.geomesa.utils.geotools.SimpleFeatureTypes;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.filter.Filter;
+import org.opengis.filter.FilterFactory;
+import org.opengis.filter.identity.Identifier;
+import org.openrdf.model.Literal;
+import org.openrdf.model.Statement;
+import org.openrdf.model.URI;
+import org.openrdf.query.QueryEvaluationException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.io.ParseException;
+import com.vividsolutions.jts.io.WKTReader;
+
+import info.aduna.iteration.CloseableIteration;
+import mvm.rya.accumulo.experimental.AbstractAccumuloIndexer;
+import mvm.rya.api.RdfCloudTripleStoreConfiguration;
+import mvm.rya.api.domain.RyaStatement;
+import mvm.rya.api.resolver.RyaToRdfConversions;
+import mvm.rya.indexing.GeoIndexer;
+import mvm.rya.indexing.Md5Hash;
+import mvm.rya.indexing.OptionalConfigUtils;
+import mvm.rya.indexing.StatementConstraints;
+import mvm.rya.indexing.StatementSerializer;
+import mvm.rya.indexing.accumulo.ConfigUtils;
+
+/**
+ * A {@link GeoIndexer} wrapper around a GeoMesa {@link AccumuloDataStore}. This class configures and connects to the Datastore, creates the
+ * RDF Feature Type, and interacts with the Datastore.
+ * <p>
+ * Specifically, this class creates a RDF Feature type and stores each RDF Statement as a RDF Feature in the datastore. Each feature
+ * contains the standard set of GeoMesa attributes (Geometry, Start Date, and End Date). The GeoMesaGeoIndexer populates the Geometry
+ * attribute by parsing the Well-Known Text contained in the RDF Statement\u2019s object literal value.
+ * <p>
+ * The RDF Feature contains four additional attributes for each component of the RDF Statement. These attributes are:
+ * <p>
+ * <table border="1">
+ * <tr>
+ * <th>Name</th>
+ * <th>Symbol</th>
+ * <th>Type</th>
+ * </tr>
+ * <tr>
+ * <td>Subject Attribute</td>
+ * <td>S</td>
+ * <td>String</td>
+ * </tr>
+ * </tr>
+ * <tr>
+ * <td>Predicate Attribute</td>
+ * <td>P</td>
+ * <td>String</td>
+ * </tr>
+ * </tr>
+ * <tr>
+ * <td>Object Attribute</td>
+ * <td>O</td>
+ * <td>String</td>
+ * </tr>
+ * </tr>
+ * <tr>
+ * <td>Context Attribute</td>
+ * <td>C</td>
+ * <td>String</td>
+ * </tr>
+ * </table>
+ */
+public class GeoMesaGeoIndexer extends AbstractAccumuloIndexer implements GeoIndexer  {
+
+    private static final String TABLE_SUFFIX = "geo";
+
+    private static final Logger logger = Logger.getLogger(GeoMesaGeoIndexer.class);
+
+    private static final String FEATURE_NAME = "RDF";
+
+    private static final String SUBJECT_ATTRIBUTE = "S";
+    private static final String PREDICATE_ATTRIBUTE = "P";
+    private static final String OBJECT_ATTRIBUTE = "O";
+    private static final String CONTEXT_ATTRIBUTE = "C";
+
+    private Set<URI> validPredicates;
+    private Configuration conf;
+    private FeatureStore<SimpleFeatureType, SimpleFeature> featureStore;
+    private FeatureSource<SimpleFeatureType, SimpleFeature> featureSource;
+    private SimpleFeatureType featureType;
+    private boolean isInit = false;
+
+    //initialization occurs in setConf because index is created using reflection
+    @Override
+    public void setConf(final Configuration conf) {
+        this.conf = conf;
+        if (!isInit) {
+            try {
+            	initInternal();
+                isInit = true;
+            } catch (final IOException e) {
+                logger.warn("Unable to initialize index.  Throwing Runtime Exception. ", e);
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Override
+    public Configuration getConf() {
+        return conf;
+    }
+
+
+    private void initInternal() throws IOException {
+        validPredicates = ConfigUtils.getGeoPredicates(conf);
+
+        final DataStore dataStore = createDataStore(conf);
+
+        try {
+            featureType = getStatementFeatureType(dataStore);
+        } catch (final IOException e) {
+            throw new IOException(e);
+        } catch (final SchemaException e) {
+            throw new IOException(e);
+        }
+
+        featureSource = dataStore.getFeatureSource(featureType.getName());
+        if (!(featureSource instanceof FeatureStore)) {
+            throw new IllegalStateException("Could not retrieve feature store");
+        }
+        featureStore = (FeatureStore<SimpleFeatureType, SimpleFeature>) featureSource;
+    }
+
+    private static DataStore createDataStore(final Configuration conf) throws IOException {
+        // get the configuration parameters
+        final Instance instance = ConfigUtils.getInstance(conf);
+        final boolean useMock = instance instanceof MockInstance;
+        final String instanceId = instance.getInstanceName();
+        final String zookeepers = instance.getZooKeepers();
+        final String user = ConfigUtils.getUsername(conf);
+        final String password = ConfigUtils.getPassword(conf);
+        final String auths = ConfigUtils.getAuthorizations(conf).toString();
+        final String tableName = getTableName(conf);
+        final int numParitions = OptionalConfigUtils.getGeoNumPartitions(conf);
+
+        final String featureSchemaFormat = "%~#s%" + numParitions + "#r%" + FEATURE_NAME
+                + "#cstr%0,3#gh%yyyyMMdd#d::%~#s%3,2#gh::%~#s%#id";
+        // build the map of parameters
+        final Map<String, Serializable> params = new HashMap<String, Serializable>();
+        params.put("instanceId", instanceId);
+        params.put("zookeepers", zookeepers);
+        params.put("user", user);
+        params.put("password", password);
+        params.put("auths", auths);
+        params.put("tableName", tableName);
+        params.put("indexSchemaFormat", featureSchemaFormat);
+        params.put("useMock", Boolean.toString(useMock));
+
+        // fetch the data store from the finder
+        return DataStoreFinder.getDataStore(params);
+    }
+
+    private static SimpleFeatureType getStatementFeatureType(final DataStore dataStore) throws IOException, SchemaException {
+        SimpleFeatureType featureType;
+
+        final String[] datastoreFeatures = dataStore.getTypeNames();
+        if (Arrays.asList(datastoreFeatures).contains(FEATURE_NAME)) {
+            featureType = dataStore.getSchema(FEATURE_NAME);
+        } else {
+            final String featureSchema = SUBJECT_ATTRIBUTE + ":String," //
+                    + PREDICATE_ATTRIBUTE + ":String," //
+                    + OBJECT_ATTRIBUTE + ":String," //
+                    + CONTEXT_ATTRIBUTE + ":String," //
+                    + Constants.SF_PROPERTY_GEOMETRY + ":Geometry:srid=4326";
+            featureType = SimpleFeatureTypes.createType(FEATURE_NAME, featureSchema);
+            dataStore.createSchema(featureType);
+        }
+        return featureType;
+    }
+
+    @Override
+    public void storeStatements(final Collection<RyaStatement> ryaStatements) throws IOException {
+        // create a feature collection
+        final DefaultFeatureCollection featureCollection = new DefaultFeatureCollection();
+        for (final RyaStatement ryaStatement : ryaStatements) {
+            final Statement statement = RyaToRdfConversions.convertStatement(ryaStatement);
+            // if the predicate list is empty, accept all predicates.
+            // Otherwise, make sure the predicate is on the "valid" list
+            final boolean isValidPredicate = validPredicates.isEmpty() || validPredicates.contains(statement.getPredicate());
+
+            if (isValidPredicate && (statement.getObject() instanceof Literal)) {
+                try {
+                    final SimpleFeature feature = createFeature(featureType, statement);
+                    featureCollection.add(feature);
+                } catch (final ParseException e) {
+                    logger.warn("Error getting geo from statement: " + statement.toString(), e);
+                }
+            }
+        }
+
+        // write this feature collection to the store
+        if (!featureCollection.isEmpty()) {
+            featureStore.addFeatures(featureCollection);
+        }
+    }
+
+
+    @Override
+    public void storeStatement(final RyaStatement statement) throws IOException {
+        storeStatements(Collections.singleton(statement));
+    }
+
+    private static SimpleFeature createFeature(final SimpleFeatureType featureType, final Statement statement) throws ParseException {
+        final String subject = StatementSerializer.writeSubject(statement);
+        final String predicate = StatementSerializer.writePredicate(statement);
+        final String object = StatementSerializer.writeObject(statement);
+        final String context = StatementSerializer.writeContext(statement);
+
+        // create the feature
+        final Object[] noValues = {};
+
+        // create the hash
+        final String statementId = Md5Hash.md5Base64(StatementSerializer.writeStatement(statement));
+        final SimpleFeature newFeature = SimpleFeatureBuilder.build(featureType, noValues, statementId);
+
+        // write the statement data to the fields
+        final Geometry geom = GeoParseUtils.getGeometry(statement); 
+        if(geom == null || geom.isEmpty() || !geom.isValid()) {
+            throw new ParseException("Could not create geometry for statement " + statement);
+        }
+        newFeature.setDefaultGeometry(geom);
+
+        newFeature.setAttribute(SUBJECT_ATTRIBUTE, subject);
+        newFeature.setAttribute(PREDICATE_ATTRIBUTE, predicate);
+        newFeature.setAttribute(OBJECT_ATTRIBUTE, object);
+        newFeature.setAttribute(CONTEXT_ATTRIBUTE, context);
+
+        // preserve the ID that we created for this feature
+        // (set the hint to FALSE to have GeoTools generate IDs)
+        newFeature.getUserData().put(Hints.USE_PROVIDED_FID, java.lang.Boolean.TRUE);
+
+        return newFeature;
+    }
+
+    private CloseableIteration<Statement, QueryEvaluationException> performQuery(final String type, final Geometry geometry,
+            final StatementConstraints contraints) {
+        final List<String> filterParms = new ArrayList<String>();
+
+        filterParms.add(type + "(" + Constants.SF_PROPERTY_GEOMETRY + ", " + geometry + " )");
+
+        if (contraints.hasSubject()) {
+            filterParms.add("( " + SUBJECT_ATTRIBUTE + "= '" + contraints.getSubject() + "') ");
+        }
+        if (contraints.hasContext()) {
+            filterParms.add("( " + CONTEXT_ATTRIBUTE + "= '" + contraints.getContext() + "') ");
+        }
+        if (contraints.hasPredicates()) {
+            final List<String> predicates = new ArrayList<String>();
+            for (final URI u : contraints.getPredicates()) {
+                predicates.add("( " + PREDICATE_ATTRIBUTE + "= '" + u.stringValue() + "') ");
+            }
+            filterParms.add("(" + StringUtils.join(predicates, " OR ") + ")");
+        }
+
+        final String filterString = StringUtils.join(filterParms, " AND ");
+        logger.info("Performing geomesa query : " + filterString);
+
+        return getIteratorWrapper(filterString);
+    }
+
+    private CloseableIteration<Statement, QueryEvaluationException> getIteratorWrapper(final String filterString) {
+
+        return new CloseableIteration<Statement, QueryEvaluationException>() {
+
+            private FeatureIterator<SimpleFeature> featureIterator = null;
+
+            FeatureIterator<SimpleFeature> getIterator() throws QueryEvaluationException {
+                if (featureIterator == null) {
+                    Filter cqlFilter;
+                    try {
+                        cqlFilter = ECQL.toFilter(filterString);
+                    } catch (final CQLException e) {
+                        logger.error("Error parsing query: " + filterString, e);
+                        throw new QueryEvaluationException(e);
+                    }
+
+                    final Query query = new Query(featureType.getTypeName(), cqlFilter);
+                    try {
+                        featureIterator = featureSource.getFeatures(query).features();
+                    } catch (final IOException e) {
+                        logger.error("Error performing query: " + filterString, e);
+                        throw new QueryEvaluationException(e);
+                    }
+
+                }
+                return featureIterator;
+            }
+
+            @Override
+            public boolean hasNext() throws QueryEvaluationException {
+                return getIterator().hasNext();
+            }
+
+            @Override
+            public Statement next() throws QueryEvaluationException {
+                final SimpleFeature feature = getIterator().next();
+                final String subjectString = feature.getAttribute(SUBJECT_ATTRIBUTE).toString();
+                final String predicateString = feature.getAttribute(PREDICATE_ATTRIBUTE).toString();
+                final String objectString = feature.getAttribute(OBJECT_ATTRIBUTE).toString();
+                final String contextString = feature.getAttribute(CONTEXT_ATTRIBUTE).toString();
+                final Statement statement = StatementSerializer.readStatement(subjectString, predicateString, objectString, contextString);
+                return statement;
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException("Remove not implemented");
+            }
+
+            @Override
+            public void close() throws QueryEvaluationException {
+                getIterator().close();
+            }
+        };
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryEquals(final Geometry query, final StatementConstraints contraints) {
+        return performQuery("EQUALS", query, contraints);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryDisjoint(final Geometry query, final StatementConstraints contraints) {
+        return performQuery("DISJOINT", query, contraints);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryIntersects(final Geometry query, final StatementConstraints contraints) {
+        return performQuery("INTERSECTS", query, contraints);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryTouches(final Geometry query, final StatementConstraints contraints) {
+        return performQuery("TOUCHES", query, contraints);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryCrosses(final Geometry query, final StatementConstraints contraints) {
+        return performQuery("CROSSES", query, contraints);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryWithin(final Geometry query, final StatementConstraints contraints) {
+        return performQuery("WITHIN", query, contraints);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryContains(final Geometry query, final StatementConstraints contraints) {
+        return performQuery("CONTAINS", query, contraints);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryOverlaps(final Geometry query, final StatementConstraints contraints) {
+        return performQuery("OVERLAPS", query, contraints);
+    }
+
+    @Override
+    public Set<URI> getIndexablePredicates() {
+        return validPredicates;
+    }
+
+    @Override
+    public void flush() throws IOException {
+        // TODO cache and flush features instead of writing them one at a time
+    }
+
+    @Override
+    public void close() throws IOException {
+        flush();
+    }
+
+
+    @Override
+    public String getTableName() {
+            return getTableName(conf);
+    }
+
+    /**
+     * Get the Accumulo table that will be used by this index.  
+     * @param conf
+     * @return table name guaranteed to be used by instances of this index
+     */
+    public static String getTableName(Configuration conf) {
+        return ConfigUtils.getTablePrefix(conf)  + TABLE_SUFFIX;
+    }
+
+    private void deleteStatements(final Collection<RyaStatement> ryaStatements) throws IOException {
+        // create a feature collection
+        final DefaultFeatureCollection featureCollection = new DefaultFeatureCollection();
+
+        for (final RyaStatement ryaStatement : ryaStatements) {
+            final Statement statement = RyaToRdfConversions.convertStatement(ryaStatement);
+            // if the predicate list is empty, accept all predicates.
+            // Otherwise, make sure the predicate is on the "valid" list
+            final boolean isValidPredicate = validPredicates.isEmpty() || validPredicates.contains(statement.getPredicate());
+
+            if (isValidPredicate && (statement.getObject() instanceof Literal)) {
+                try {
+                    final SimpleFeature feature = createFeature(featureType, statement);
+                    featureCollection.add(feature);
+                } catch (final ParseException e) {
+                    logger.warn("Error getting geo from statement: " + statement.toString(), e);
+                }
+            }
+        }
+
+        // remove this feature collection from the store
+        if (!featureCollection.isEmpty()) {
+            final Set<Identifier> featureIds = new HashSet<Identifier>();
+            final FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(null);
+            final Set<String> stringIds = DataUtilities.fidSet(featureCollection);
+            for (final String id : stringIds) {
+                featureIds.add(filterFactory.featureId(id));
+            }
+            final Filter filter = filterFactory.id(featureIds);
+            featureStore.removeFeatures(filter);
+        }
+    }
+
+
+    @Override
+    public void deleteStatement(final RyaStatement statement) throws IOException {
+        deleteStatements(Collections.singleton(statement));
+    }
+
+	@Override
+	public void init() {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void setConnector(final Connector connector) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void destroy() {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void purge(final RdfCloudTripleStoreConfiguration configuration) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void dropAndDestroy() {
+		// TODO Auto-generated method stub
+
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/7727b165/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoParseUtils.java
----------------------------------------------------------------------
diff --git a/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoParseUtils.java b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoParseUtils.java
new file mode 100644
index 0000000..fa3699a
--- /dev/null
+++ b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoParseUtils.java
@@ -0,0 +1,119 @@
+package mvm.rya.indexing.accumulo.geo;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+/*
+ * 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.
+ */
+
+
+import org.apache.log4j.Logger;
+import org.geotools.gml3.GMLConfiguration;
+import org.geotools.xml.Parser;
+import org.openrdf.model.Literal;
+import org.openrdf.model.Statement;
+import org.xml.sax.SAXException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.io.ParseException;
+import com.vividsolutions.jts.io.WKTReader;
+
+import mvm.rya.indexing.GeoConstants;
+
+public class GeoParseUtils {
+    static final Logger logger = Logger.getLogger(GeoParseUtils.class);
+    /** 
+     * @deprecated  Not needed since geo literals may be WKT or GML.
+     *
+     *    This method warns on a condition that must already be tested.  Replaced by
+     *    {@link #getLiteral(Statement)} and {@link #getGeometry(Statement}
+     *    and getLiteral(statement).toString()
+     *    and getLiteral(statement).getDatatype()
+     */
+    @Deprecated
+	public static String getWellKnownText(Statement statement) throws ParseException {
+	    Literal lit = getLiteral(statement);
+	    if (!GeoConstants.XMLSCHEMA_OGC_WKT.equals(lit.getDatatype())) {
+	        logger.warn("Literal is not of type " + GeoConstants.XMLSCHEMA_OGC_WKT + ": " + statement.toString());
+	    }
+	    return lit.getLabel().toString();
+	}
+
+    public static Literal getLiteral(Statement statement) throws ParseException {
+        org.openrdf.model.Value v = statement.getObject();
+        if (!(v instanceof Literal)) {
+            throw new ParseException("Statement does not contain Literal: " + statement.toString());
+        }
+        Literal lit = (Literal) v;
+        return lit;
+    }
+
+    /**
+     * Parse GML/wkt literal to Geometry
+     * 
+     * @param statement
+     * @return
+     * @throws ParseException
+     * @throws ParserConfigurationException 
+     * @throws SAXException 
+     * @throws IOException 
+     */
+    public static Geometry getGeometry(Statement statement) throws ParseException {
+        // handle GML or WKT
+        Literal lit = getLiteral(statement);
+        if (GeoConstants.XMLSCHEMA_OGC_WKT.equals(lit.getDatatype())) {
+            final String wkt = lit.getLabel().toString();
+            return (new WKTReader()).read(wkt);
+        } else if (GeoConstants.XMLSCHEMA_OGC_GML.equals(lit.getDatatype())) {
+            String gml = lit.getLabel().toString();
+            try {
+                return getGeometryGml(gml);
+            } catch (IOException | SAXException | ParserConfigurationException e) {
+                throw new ParseException(e);
+            }
+        } else {
+            throw new ParseException("Literal is unknown geo type, expecting WKT or GML: " + statement.toString());
+        }
+    }
+    /**
+     * Convert GML/XML string into a geometry that can be indexed.
+     * @param gmlString
+     * @return
+     * @throws IOException
+     * @throws SAXException
+     * @throws ParserConfigurationException
+     */
+    public static Geometry getGeometryGml(String gmlString) throws IOException, SAXException, ParserConfigurationException {
+        Reader reader = new StringReader(gmlString); 
+        GMLConfiguration gmlConfiguration = new GMLConfiguration();
+        Parser gmlParser = new Parser(gmlConfiguration);
+        //  gmlParser.setStrict(false);  // attempt at allowing deprecated elements, but no.
+        //  gmlParser.setValidating(false);
+        final Geometry geometry = (Geometry) gmlParser.parse(reader);
+        // This sometimes gets populated with the SRS/CRS: geometry.getUserData()
+        // Always returns 0 : geometry.getSRID() 
+        //TODO geometry.setUserData(some default CRS); OR geometry.setSRID(some default CRS)
+        
+        return geometry;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/7727b165/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoTupleSet.java
----------------------------------------------------------------------
diff --git a/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoTupleSet.java b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoTupleSet.java
new file mode 100644
index 0000000..a3a0630
--- /dev/null
+++ b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/accumulo/geo/GeoTupleSet.java
@@ -0,0 +1,363 @@
+package mvm.rya.indexing.accumulo.geo;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.openrdf.model.Statement;
+import org.openrdf.model.URI;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.QueryEvaluationException;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Maps;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.io.ParseException;
+import com.vividsolutions.jts.io.WKTReader;
+
+/*
+ * 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.
+ */
+
+
+import info.aduna.iteration.CloseableIteration;
+import mvm.rya.indexing.GeoConstants;
+import mvm.rya.indexing.GeoIndexer;
+import mvm.rya.indexing.IndexingExpr;
+import mvm.rya.indexing.IteratorFactory;
+import mvm.rya.indexing.SearchFunction;
+import mvm.rya.indexing.StatementConstraints;
+import mvm.rya.indexing.external.tupleSet.ExternalTupleSet;
+
+//Indexing Node for geo expressions to be inserted into execution plan
+//to delegate geo portion of query to geo index
+public class GeoTupleSet extends ExternalTupleSet {
+
+    private final Configuration conf;
+    private final GeoIndexer geoIndexer;
+    private final IndexingExpr filterInfo;
+
+
+    public GeoTupleSet(final IndexingExpr filterInfo, final GeoIndexer geoIndexer) {
+        this.filterInfo = filterInfo;
+        this.geoIndexer = geoIndexer;
+        conf = geoIndexer.getConf();
+    }
+
+    @Override
+    public Set<String> getBindingNames() {
+        return filterInfo.getBindingNames();
+    }
+
+    @Override
+	public GeoTupleSet clone() {
+        return new GeoTupleSet(filterInfo, geoIndexer);
+    }
+
+    @Override
+    public double cardinality() {
+        return 0.0; // No idea how the estimate cardinality here.
+    }
+
+
+    @Override
+    public String getSignature() {
+        return "(GeoTuple Projection) " + "variables: " + Joiner.on(", ").join(getBindingNames()).replaceAll("\\s+", " ");
+    }
+
+
+
+    @Override
+    public boolean equals(final Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (!(other instanceof GeoTupleSet)) {
+            return false;
+        }
+        final GeoTupleSet arg = (GeoTupleSet) other;
+        return filterInfo.equals(arg.filterInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31*result + filterInfo.hashCode();
+
+        return result;
+    }
+
+
+
+    /**
+     * Returns an iterator over the result set of the contained IndexingExpr.
+     * <p>
+     * Should be thread-safe (concurrent invocation {@link OfflineIterable} this
+     * method can be expected with some query evaluators.
+     */
+    @Override
+    public CloseableIteration<BindingSet, QueryEvaluationException> evaluate(final BindingSet bindings)
+            throws QueryEvaluationException {
+
+
+        final URI funcURI = filterInfo.getFunction();
+        final SearchFunction searchFunction = new GeoSearchFunctionFactory(conf).getSearchFunction(funcURI);
+        if(filterInfo.getArguments().length > 1) {
+            throw new IllegalArgumentException("Index functions do not support more than two arguments.");
+        }
+
+        final String queryText = filterInfo.getArguments()[0].stringValue();
+
+        return IteratorFactory.getIterator(filterInfo.getSpConstraint(), bindings, queryText, searchFunction);
+    }
+
+
+
+    //returns appropriate search function for a given URI
+    //search functions used in GeoMesaGeoIndexer to access index
+    public class GeoSearchFunctionFactory {
+
+        Configuration conf;
+
+        private final Map<URI, SearchFunction> SEARCH_FUNCTION_MAP = Maps.newHashMap();
+
+        public GeoSearchFunctionFactory(final Configuration conf) {
+            this.conf = conf;
+        }
+
+
+        /**
+         * Get a {@link GeoSearchFunction} for a given URI.
+         *
+         * @param searchFunction
+         * @return
+         */
+        public SearchFunction getSearchFunction(final URI searchFunction) {
+
+            SearchFunction geoFunc = null;
+
+            try {
+                geoFunc = getSearchFunctionInternal(searchFunction);
+            } catch (final QueryEvaluationException e) {
+                e.printStackTrace();
+            }
+
+            return geoFunc;
+        }
+
+        private SearchFunction getSearchFunctionInternal(final URI searchFunction) throws QueryEvaluationException {
+            final SearchFunction sf = SEARCH_FUNCTION_MAP.get(searchFunction);
+
+            if (sf != null) {
+                return sf;
+            } else {
+                throw new QueryEvaluationException("Unknown Search Function: " + searchFunction.stringValue());
+            }
+        }
+
+        private final SearchFunction GEO_EQUALS = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(final String queryText,
+                    final StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    final WKTReader reader = new WKTReader();
+                    final Geometry geometry = reader.read(queryText);
+                    final CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (final ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_EQUALS";
+            };
+        };
+
+        private final SearchFunction GEO_DISJOINT = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(final String queryText,
+                    final StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    final WKTReader reader = new WKTReader();
+                    final Geometry geometry = reader.read(queryText);
+                    final CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (final ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_DISJOINT";
+            };
+        };
+
+        private final SearchFunction GEO_INTERSECTS = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(final String queryText,
+                    final StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    final WKTReader reader = new WKTReader();
+                    final Geometry geometry = reader.read(queryText);
+                    final CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (final ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_INTERSECTS";
+            };
+        };
+
+        private final SearchFunction GEO_TOUCHES = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(final String queryText,
+                    final StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    final WKTReader reader = new WKTReader();
+                    final Geometry geometry = reader.read(queryText);
+                    final CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (final ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_TOUCHES";
+            };
+        };
+
+        private final SearchFunction GEO_CONTAINS = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(final String queryText,
+                    final StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    final WKTReader reader = new WKTReader();
+                    final Geometry geometry = reader.read(queryText);
+                    final CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (final ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_CONTAINS";
+            };
+        };
+
+        private final SearchFunction GEO_OVERLAPS = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(final String queryText,
+                    final StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    final WKTReader reader = new WKTReader();
+                    final Geometry geometry = reader.read(queryText);
+                    final CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (final ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_OVERLAPS";
+            };
+        };
+
+        private final SearchFunction GEO_CROSSES = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(final String queryText,
+                    final StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    final WKTReader reader = new WKTReader();
+                    final Geometry geometry = reader.read(queryText);
+                    final CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (final ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_CROSSES";
+            };
+        };
+
+        private final SearchFunction GEO_WITHIN = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(final String queryText,
+                    final StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    final WKTReader reader = new WKTReader();
+                    final Geometry geometry = reader.read(queryText);
+                    final CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (final ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_WITHIN";
+            };
+        };
+
+        {
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_EQUALS, GEO_EQUALS);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_DISJOINT, GEO_DISJOINT);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_INTERSECTS, GEO_INTERSECTS);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_TOUCHES, GEO_TOUCHES);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_CONTAINS, GEO_CONTAINS);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_OVERLAPS, GEO_OVERLAPS);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_CROSSES, GEO_CROSSES);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_WITHIN, GEO_WITHIN);
+        }
+
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/7727b165/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/GeoMongoDBStorageStrategy.java
----------------------------------------------------------------------
diff --git a/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/GeoMongoDBStorageStrategy.java b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/GeoMongoDBStorageStrategy.java
new file mode 100644
index 0000000..9411330
--- /dev/null
+++ b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/GeoMongoDBStorageStrategy.java
@@ -0,0 +1,143 @@
+package mvm.rya.indexing.mongodb.geo;
+
+/*
+ * 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.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.openrdf.model.Statement;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.io.ParseException;
+import com.vividsolutions.jts.io.WKTReader;
+
+import mvm.rya.api.domain.RyaStatement;
+import mvm.rya.api.resolver.RyaToRdfConversions;
+import mvm.rya.indexing.accumulo.geo.GeoParseUtils;
+import mvm.rya.indexing.mongodb.IndexingMongoDBStorageStrategy;
+
+public class GeoMongoDBStorageStrategy extends IndexingMongoDBStorageStrategy {
+    private static final Logger LOG = Logger.getLogger(GeoMongoDBStorageStrategy.class);
+
+    private static final String GEO = "location";
+    public enum GeoQueryType {
+        INTERSECTS {
+            @Override
+            public String getKeyword() {
+                return "$geoIntersects";
+            }
+        }, WITHIN {
+            @Override
+            public String getKeyword() {
+                return "$geoWithin";
+            }
+        },
+        EQUALS {
+            @Override
+            public String getKeyword() {
+                return "$near";
+            }
+        };
+
+        public abstract String getKeyword();
+    }
+
+    static class GeoQuery {
+        private final GeoQueryType queryType;
+        private final Geometry geo;
+
+        public GeoQuery(final GeoQueryType queryType, final Geometry geo) {
+            this.queryType = queryType;
+            this.geo = geo;
+        }
+
+        public GeoQueryType getQueryType() {
+            return queryType;
+        }
+        public Geometry getGeo() {
+            return geo;
+        }
+    }
+
+    private final double maxDistance;
+
+    public GeoMongoDBStorageStrategy(final double maxDistance) {
+        this.maxDistance = maxDistance;
+    }
+
+    @Override
+    public void createIndices(final DBCollection coll){
+        coll.createIndex("{" + GEO + " : \"2dsphere\"" );
+    }
+
+    public DBObject getQuery(final GeoQuery queryObj) {
+        final Geometry geo = queryObj.getGeo();
+        final GeoQueryType queryType = queryObj.getQueryType();
+
+        BasicDBObject query;
+        if (queryType.equals(GeoQueryType.EQUALS)){
+            final List<double[]> points = getCorrespondingPoints(geo);
+            if (points.size() == 1){
+                final List circle = new ArrayList();
+                circle.add(points.get(0));
+                circle.add(maxDistance);
+                final BasicDBObject polygon = new BasicDBObject("$centerSphere", circle);
+                query = new BasicDBObject(GEO,  new BasicDBObject(GeoQueryType.WITHIN.getKeyword(), polygon));
+            } else {
+                query = new BasicDBObject(GEO, points);
+            }
+        } else {
+            query = new BasicDBObject(GEO, new BasicDBObject(queryType.getKeyword(), new BasicDBObject("$polygon", getCorrespondingPoints(geo))));
+        }
+
+        return query;
+    }
+
+    @Override
+    public DBObject serialize(final RyaStatement ryaStatement) {
+        // if the object is wkt, then try to index it
+        // write the statement data to the fields
+        try {
+            final Statement statement = RyaToRdfConversions.convertStatement(ryaStatement);
+            final Geometry geo = (new WKTReader()).read(GeoParseUtils.getWellKnownText(statement));
+            final BasicDBObject base = (BasicDBObject) super.serialize(ryaStatement);
+            base.append(GEO, getCorrespondingPoints(geo));
+            return base;
+        } catch(final ParseException e) {
+            LOG.error("Could not create geometry for statement " + ryaStatement, e);
+            return null;
+        }
+    }
+
+    private List<double[]> getCorrespondingPoints(final Geometry geo){
+       final List<double[]> points = new ArrayList<double[]>();
+        for (final Coordinate coord : geo.getCoordinates()){
+            points.add(new double[] {
+                coord.x, coord.y
+            });
+        }
+        return points;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/7727b165/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/MongoGeoIndexer.java
----------------------------------------------------------------------
diff --git a/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/MongoGeoIndexer.java b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/MongoGeoIndexer.java
new file mode 100644
index 0000000..7589f03
--- /dev/null
+++ b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/MongoGeoIndexer.java
@@ -0,0 +1,112 @@
+package mvm.rya.indexing.mongodb.geo;
+/*
+ * 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.
+ */
+
+import static mvm.rya.indexing.mongodb.geo.GeoMongoDBStorageStrategy.GeoQueryType.EQUALS;
+import static mvm.rya.indexing.mongodb.geo.GeoMongoDBStorageStrategy.GeoQueryType.INTERSECTS;
+import static mvm.rya.indexing.mongodb.geo.GeoMongoDBStorageStrategy.GeoQueryType.WITHIN;
+
+import org.apache.log4j.Logger;
+import org.openrdf.model.Statement;
+import org.openrdf.query.QueryEvaluationException;
+
+import com.mongodb.DBObject;
+import com.vividsolutions.jts.geom.Geometry;
+
+import info.aduna.iteration.CloseableIteration;
+import mvm.rya.indexing.GeoIndexer;
+import mvm.rya.indexing.StatementConstraints;
+import mvm.rya.indexing.accumulo.ConfigUtils;
+import mvm.rya.indexing.mongodb.AbstractMongoIndexer;
+import mvm.rya.indexing.mongodb.geo.GeoMongoDBStorageStrategy.GeoQuery;
+import mvm.rya.mongodb.MongoDBRdfConfiguration;
+
+public class MongoGeoIndexer extends AbstractMongoIndexer<GeoMongoDBStorageStrategy> implements GeoIndexer {
+    private static final String COLLECTION_SUFFIX = "geo";
+    private static final Logger logger = Logger.getLogger(MongoGeoIndexer.class);
+
+    @Override
+	public void init() {
+        initCore();
+        predicates = ConfigUtils.getGeoPredicates(conf);
+        storageStrategy = new GeoMongoDBStorageStrategy(Double.valueOf(conf.get(MongoDBRdfConfiguration.MONGO_GEO_MAXDISTANCE, "1e-10")));
+        storageStrategy.createIndices(collection);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryEquals(
+            final Geometry query, final StatementConstraints constraints) {
+        final DBObject queryObj = storageStrategy.getQuery(new GeoQuery(EQUALS, query));
+        return withConstraints(constraints, queryObj);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryDisjoint(
+            final Geometry query, final StatementConstraints constraints) {
+        throw new UnsupportedOperationException(
+                "Disjoint queries are not supported in Mongo DB.");
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryIntersects(
+            final Geometry query, final StatementConstraints constraints) {
+        final DBObject queryObj = storageStrategy.getQuery(new GeoQuery(INTERSECTS, query));
+        return withConstraints(constraints, queryObj);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryTouches(
+            final Geometry query, final StatementConstraints constraints) {
+        throw new UnsupportedOperationException(
+                "Touches queries are not supported in Mongo DB.");
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryCrosses(
+            final Geometry query, final StatementConstraints constraints) {
+        throw new UnsupportedOperationException(
+                "Crosses queries are not supported in Mongo DB.");
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryWithin(
+            final Geometry query, final StatementConstraints constraints) {
+        final DBObject queryObj = storageStrategy.getQuery(new GeoQuery(WITHIN, query));
+        return withConstraints(constraints, queryObj);
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryContains(
+            final Geometry query, final StatementConstraints constraints) {
+        throw new UnsupportedOperationException(
+                "Contains queries are not supported in Mongo DB.");
+    }
+
+    @Override
+    public CloseableIteration<Statement, QueryEvaluationException> queryOverlaps(
+            final Geometry query, final StatementConstraints constraints) {
+        throw new UnsupportedOperationException(
+                "Overlaps queries are not supported in Mongo DB.");
+    }
+
+    @Override
+    public String getCollectionName() {
+        return ConfigUtils.getTablePrefix(conf)  + COLLECTION_SUFFIX;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/7727b165/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/MongoGeoTupleSet.java
----------------------------------------------------------------------
diff --git a/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/MongoGeoTupleSet.java b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/MongoGeoTupleSet.java
new file mode 100644
index 0000000..35e679e
--- /dev/null
+++ b/extras/rya.geoindexing/src/main/java/mvm/rya/indexing/mongodb/geo/MongoGeoTupleSet.java
@@ -0,0 +1,360 @@
+package mvm.rya.indexing.mongodb.geo;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.hadoop.conf.Configuration;
+import org.openrdf.model.Statement;
+import org.openrdf.model.URI;
+import org.openrdf.query.BindingSet;
+import org.openrdf.query.QueryEvaluationException;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Maps;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.io.ParseException;
+import com.vividsolutions.jts.io.WKTReader;
+
+/*
+ * 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.
+ */
+
+
+import info.aduna.iteration.CloseableIteration;
+import mvm.rya.indexing.GeoConstants;
+import mvm.rya.indexing.GeoIndexer;
+import mvm.rya.indexing.IndexingExpr;
+import mvm.rya.indexing.IteratorFactory;
+import mvm.rya.indexing.SearchFunction;
+import mvm.rya.indexing.StatementConstraints;
+import mvm.rya.indexing.accumulo.geo.GeoTupleSet;
+import mvm.rya.indexing.external.tupleSet.ExternalTupleSet;
+
+public class MongoGeoTupleSet extends ExternalTupleSet {
+
+    private Configuration conf;
+    private GeoIndexer geoIndexer;
+    private IndexingExpr filterInfo;
+   
+
+    public MongoGeoTupleSet(IndexingExpr filterInfo, GeoIndexer geoIndexer) {
+        this.filterInfo = filterInfo;
+        this.geoIndexer = geoIndexer;
+        this.conf = geoIndexer.getConf();
+    }
+
+    @Override
+    public Set<String> getBindingNames() {
+        return filterInfo.getBindingNames();
+    }
+
+    public GeoTupleSet clone() {
+        return new GeoTupleSet(filterInfo, geoIndexer);
+    }
+
+    @Override
+    public double cardinality() {
+        return 0.0; // No idea how the estimate cardinality here.
+    }
+    
+   
+    @Override
+    public String getSignature() {
+        return "(GeoTuple Projection) " + "variables: " + Joiner.on(", ").join(this.getBindingNames()).replaceAll("\\s+", " ");
+    }
+    
+    
+    
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (!(other instanceof MongoGeoTupleSet)) {
+            return false;
+        }
+        MongoGeoTupleSet arg = (MongoGeoTupleSet) other;
+        return this.filterInfo.equals(arg.filterInfo);
+    }
+    
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31*result + filterInfo.hashCode();
+        
+        return result;
+    }
+    
+    
+
+    /**
+     * Returns an iterator over the result set of the contained IndexingExpr.
+     * <p>
+     * Should be thread-safe (concurrent invocation {@link OfflineIterable} this
+     * method can be expected with some query evaluators.
+     */
+    @Override
+    public CloseableIteration<BindingSet, QueryEvaluationException> evaluate(BindingSet bindings)
+            throws QueryEvaluationException {
+        
+      
+        URI funcURI = filterInfo.getFunction();
+        SearchFunction searchFunction = (new MongoGeoSearchFunctionFactory(conf)).getSearchFunction(funcURI);
+        if(filterInfo.getArguments().length > 1) {
+            throw new IllegalArgumentException("Index functions do not support more than two arguments.");
+        }
+        
+        String queryText = filterInfo.getArguments()[0].stringValue();
+        
+        return IteratorFactory.getIterator(filterInfo.getSpConstraint(), bindings, queryText, searchFunction);
+    }
+
+
+    
+    //returns appropriate search function for a given URI
+    //search functions used in GeoMesaGeoIndexer to access index
+    public class MongoGeoSearchFunctionFactory {
+        
+        Configuration conf;
+        
+        private final Map<URI, SearchFunction> SEARCH_FUNCTION_MAP = Maps.newHashMap();
+
+        public MongoGeoSearchFunctionFactory(Configuration conf) {
+            this.conf = conf;
+        }
+        
+
+        /**
+         * Get a {@link GeoSearchFunction} for a given URI.
+         * 
+         * @param searchFunction
+         * @return
+         */
+        public SearchFunction getSearchFunction(final URI searchFunction) {
+
+            SearchFunction geoFunc = null;
+
+            try {
+                geoFunc = getSearchFunctionInternal(searchFunction);
+            } catch (QueryEvaluationException e) {
+                e.printStackTrace();
+            }
+
+            return geoFunc;
+        }
+
+        private SearchFunction getSearchFunctionInternal(final URI searchFunction) throws QueryEvaluationException {
+            SearchFunction sf = SEARCH_FUNCTION_MAP.get(searchFunction);
+
+            if (sf != null) {
+                return sf;
+            } else {
+                throw new QueryEvaluationException("Unknown Search Function: " + searchFunction.stringValue());
+            }
+        }
+
+        private final SearchFunction GEO_EQUALS = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(String queryText,
+                    StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    WKTReader reader = new WKTReader();
+                    Geometry geometry = reader.read(queryText);
+                    CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_EQUALS";
+            };
+        };
+
+        private final SearchFunction GEO_DISJOINT = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(String queryText,
+                    StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    WKTReader reader = new WKTReader();
+                    Geometry geometry = reader.read(queryText);
+                    CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_DISJOINT";
+            };
+        };
+
+        private final SearchFunction GEO_INTERSECTS = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(String queryText,
+                    StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    WKTReader reader = new WKTReader();
+                    Geometry geometry = reader.read(queryText);
+                    CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_INTERSECTS";
+            };
+        };
+
+        private final SearchFunction GEO_TOUCHES = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(String queryText,
+                    StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    WKTReader reader = new WKTReader();
+                    Geometry geometry = reader.read(queryText);
+                    CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_TOUCHES";
+            };
+        };
+
+        private final SearchFunction GEO_CONTAINS = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(String queryText,
+                    StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    WKTReader reader = new WKTReader();
+                    Geometry geometry = reader.read(queryText);
+                    CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_CONTAINS";
+            };
+        };
+
+        private final SearchFunction GEO_OVERLAPS = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(String queryText,
+                    StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    WKTReader reader = new WKTReader();
+                    Geometry geometry = reader.read(queryText);
+                    CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_OVERLAPS";
+            };
+        };
+
+        private final SearchFunction GEO_CROSSES = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(String queryText,
+                    StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    WKTReader reader = new WKTReader();
+                    Geometry geometry = reader.read(queryText);
+                    CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_CROSSES";
+            };
+        };
+
+        private final SearchFunction GEO_WITHIN = new SearchFunction() {
+
+            @Override
+            public CloseableIteration<Statement, QueryEvaluationException> performSearch(String queryText,
+                    StatementConstraints contraints) throws QueryEvaluationException {
+                try {
+                    WKTReader reader = new WKTReader();
+                    Geometry geometry = reader.read(queryText);
+                    CloseableIteration<Statement, QueryEvaluationException> statements = geoIndexer.queryWithin(
+                            geometry, contraints);
+                    return statements;
+                } catch (ParseException e) {
+                    throw new QueryEvaluationException(e);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "GEO_WITHIN";
+            };
+        };
+
+        {
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_EQUALS, GEO_EQUALS);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_DISJOINT, GEO_DISJOINT);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_INTERSECTS, GEO_INTERSECTS);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_TOUCHES, GEO_TOUCHES);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_CONTAINS, GEO_CONTAINS);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_OVERLAPS, GEO_OVERLAPS);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_CROSSES, GEO_CROSSES);
+            SEARCH_FUNCTION_MAP.put(GeoConstants.GEO_SF_WITHIN, GEO_WITHIN);
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/7727b165/extras/rya.geoindexing/src/test/java/mvm/rya/indexing/accumulo/geo/GeoIndexerSfTest.java
----------------------------------------------------------------------
diff --git a/extras/rya.geoindexing/src/test/java/mvm/rya/indexing/accumulo/geo/GeoIndexerSfTest.java b/extras/rya.geoindexing/src/test/java/mvm/rya/indexing/accumulo/geo/GeoIndexerSfTest.java
new file mode 100644
index 0000000..69c3ce2
--- /dev/null
+++ b/extras/rya.geoindexing/src/test/java/mvm/rya/indexing/accumulo/geo/GeoIndexerSfTest.java
@@ -0,0 +1,514 @@
+package mvm.rya.indexing.accumulo.geo;
+
+/*
+ * 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.
+ */
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.accumulo.core.client.admin.TableOperations;
+import org.geotools.geometry.jts.Geometries;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.openrdf.model.Resource;
+import org.openrdf.model.Statement;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.model.impl.StatementImpl;
+import org.openrdf.model.impl.URIImpl;
+import org.openrdf.model.impl.ValueFactoryImpl;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.LinearRing;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.PrecisionModel;
+import com.vividsolutions.jts.geom.impl.PackedCoordinateSequence;
+import com.vividsolutions.jts.io.ParseException;
+import com.vividsolutions.jts.io.gml2.GMLWriter;
+
+import info.aduna.iteration.CloseableIteration;
+import mvm.rya.accumulo.AccumuloRdfConfiguration;
+import mvm.rya.api.domain.RyaStatement;
+import mvm.rya.api.resolver.RdfToRyaConversions;
+import mvm.rya.api.resolver.RyaToRdfConversions;
+import mvm.rya.indexing.GeoConstants;
+import mvm.rya.indexing.StatementConstraints;
+import mvm.rya.indexing.accumulo.ConfigUtils;
+
+/**
+ * Tests all of the "simple functions" of the geoindexer specific to GML.
+ * Parameterized so that each test is run for WKT and for GML.
+ */
+@RunWith(value = Parameterized.class)
+public class GeoIndexerSfTest {
+    private static AccumuloRdfConfiguration conf;
+    private static GeometryFactory gf = new GeometryFactory(new PrecisionModel(), 4326);
+    private static GeoMesaGeoIndexer g;
+
+    private static final StatementConstraints EMPTY_CONSTRAINTS = new StatementConstraints();
+
+    // Here is the landscape:
+    /**
+     * <pre>
+     * 	 2---+---+---+---+---+---+
+     * 	 |        F      |G      |
+     * 	 1  A    o(-1,1) o   C   |
+     * 	 |               |       |
+     * 	 0---+---+       +---+---+(3,0)
+     * 	 |       |    E  |    
+     * 	-1   B   +   .---+---+
+     * 	 |       |  /|   |   |
+     * 	-2---+---+-/-+---+   +
+     * 	 ^        /  |     D |
+     *  -3  -2  -1   0---1---2   3   4
+     * </pre>
+     **/
+    private static final Polygon A = poly(bbox(-3, -2, 1, 2));
+    private static final Polygon B = poly(bbox(-3, -2, -1, 0));
+    private static final Polygon C = poly(bbox(1, 0, 3, 2));
+    private static final Polygon D = poly(bbox(0, -3, 2, -1));
+
+    private static final Point F = point(-1, 1);
+    private static final Point G = point(1, 1);
+
+    private static final LineString E = line(-1, -3, 0, -1);
+
+    private static final Map<Geometry, String> names = Maps.newHashMap();
+    static {
+        names.put(A, "A");
+        names.put(B, "B");
+        names.put(C, "C");
+        names.put(D, "D");
+        names.put(E, "E");
+        names.put(F, "F");
+        names.put(G, "G");
+    }
+
+    /**
+     * JUnit 4 parameterized iterates thru this list and calls the constructor with each. 
+     * For each test, Call the constructor three times, for WKT and for GML encoding 1, and GML encoding 2 
+     * @return
+     */
+    final static URI useJtsLibEncoding = new URIImpl("uri:useLib") ;
+    final static URI useRoughEncoding = new URIImpl("uri:useRough") ;
+    
+    @Parameters
+    public static Collection<URI[]> constructorData() {
+        URI[][] data = new URI[][] { { GeoConstants.XMLSCHEMA_OGC_WKT,useJtsLibEncoding }, { GeoConstants.XMLSCHEMA_OGC_GML,useJtsLibEncoding } , { GeoConstants.XMLSCHEMA_OGC_GML,useRoughEncoding } };
+        return Arrays.asList(data);
+    }
+
+    private URI schemaToTest;
+    private URI encodeMethod;
+    /**
+     * Constructor required by JUnit parameterized runner.  See data() for constructor values.
+     */
+    public GeoIndexerSfTest(URI schemaToTest, URI encodeMethod) {
+        this.schemaToTest=schemaToTest;
+        this.encodeMethod = encodeMethod;
+    }
+    /**
+     * Run before each test method.
+     * @throws Exception
+     */
+    @Before
+    public void before() throws Exception {
+        conf = new AccumuloRdfConfiguration();
+        conf.setTablePrefix("triplestore_");
+        String tableName = GeoMesaGeoIndexer.getTableName(conf);
+        conf.setBoolean(ConfigUtils.USE_MOCK_INSTANCE, true);
+        conf.set(ConfigUtils.CLOUDBASE_USER, "USERNAME");
+        conf.set(ConfigUtils.CLOUDBASE_PASSWORD, "PASS");
+        conf.set(ConfigUtils.CLOUDBASE_AUTHS, "U");
+
+        TableOperations tops = ConfigUtils.getConnector(conf).tableOperations();
+        // get all of the table names with the prefix
+        Set<String> toDel = Sets.newHashSet();
+        for (String t : tops.list()) {
+            if (t.startsWith(tableName)) {
+                toDel.add(t);
+            }
+        }
+        for (String t : toDel) {
+            tops.delete(t);
+        }
+
+        g = new GeoMesaGeoIndexer();
+        g.setConf(conf);
+        // Convert the statements as schema WKT or GML, then GML has two methods to encode.
+        g.storeStatement(RyaStatement(A,schemaToTest, encodeMethod));
+        g.storeStatement(RyaStatement(B,schemaToTest, encodeMethod));
+        g.storeStatement(RyaStatement(C,schemaToTest, encodeMethod));
+        g.storeStatement(RyaStatement(D,schemaToTest, encodeMethod));
+        g.storeStatement(RyaStatement(F,schemaToTest, encodeMethod));
+        g.storeStatement(RyaStatement(E,schemaToTest, encodeMethod));
+        g.storeStatement(RyaStatement(G,schemaToTest, encodeMethod));
+    }
+
+    private static RyaStatement RyaStatement(Geometry geo, URI schema, URI encodingMethod) {
+        return RdfToRyaConversions.convertStatement(genericStatement(geo,schema,encodingMethod));
+    }
+    private static Statement genericStatement(Geometry geo, URI schema, URI encodingMethod) {
+        if (schema.equals(GeoConstants.XMLSCHEMA_OGC_WKT)) {
+            return genericStatementWkt(geo);
+        } else if (schema.equals(GeoConstants.XMLSCHEMA_OGC_GML)) {
+            return genericStatementGml(geo, encodingMethod);
+        }
+        throw new Error("schema unsupported: "+schema);
+    }
+    private static Statement genericStatementWkt(Geometry geo) {
+        ValueFactory vf = new ValueFactoryImpl();
+        Resource subject = vf.createURI("uri:" + names.get(geo));
+        URI predicate = GeoConstants.GEO_AS_WKT;
+        Value object = vf.createLiteral(geo.toString(), GeoConstants.XMLSCHEMA_OGC_WKT);
+        return new StatementImpl(subject, predicate, object);
+    }
+
+    private static Statement genericStatementGml(Geometry geo, URI encodingMethod) {
+        ValueFactory vf = new ValueFactoryImpl();
+        Resource subject = vf.createURI("uri:" + names.get(geo));
+        URI predicate = GeoConstants.GEO_AS_GML;
+        
+        final String gml ;
+        if (encodingMethod==useJtsLibEncoding) 
+            gml = geoToGmlUseJtsLib(geo);
+        else if (encodingMethod==useRoughEncoding)
+            gml = geoToGmlRough(geo);
+        else
+            throw new Error("invalid encoding method: "+encodingMethod);
+        //        System.out.println("===created GML====");
+        //        System.out.println(gml);
+        //        System.out.println("========== GML====");
+
+        Value object = vf.createLiteral(gml, GeoConstants.XMLSCHEMA_OGC_GML);
+        return new StatementImpl(subject, predicate, object);
+    }
+
+    /**
+     * JTS library conversion from geometry to GML.
+     * @param geo base Geometry gets delegated
+     * @return String gml encoding of the geomoetry
+     */
+    private static String geoToGmlUseJtsLib(Geometry geo) {
+        int srid = geo.getSRID();
+        GMLWriter gmlWriter = new GMLWriter();
+        gmlWriter.setNamespace(false);
+        gmlWriter.setPrefix(null);
+        
+        if (srid != -1 || srid != 0) {
+            gmlWriter.setSrsName("EPSG:" + geo.getSRID());
+        }
+        String gml = gmlWriter.write(geo);
+        // Hack to replace a gml 2.0 deprecated element in the Polygon.  
+        // It should tolerate this as it does other depreciated elements like <gml:coordinates>.
+        return gml.replace("outerBoundaryIs", "exterior");
+    }
+
+    /**
+     * Rough conversion from geometry to GML using a template.
+     * @param geo base Geometry gets delegated
+     * @return String gml encoding of the gemoetry
+     */
+        private static String geoToGmlRough(Geometry geo) {
+            final Geometries theType = org.geotools.geometry.jts.Geometries.get(geo);
+            switch (theType) {
+            case POINT:
+                return geoToGml((Point)geo);
+            case LINESTRING:
+                return geoToGml((LineString)geo);
+            case POLYGON:
+                return geoToGml((Polygon)geo);
+            case MULTIPOINT:
+            case MULTILINESTRING:
+            case MULTIPOLYGON:
+            default:
+                throw new Error("No code to convert to GML for this type: "+theType);
+            }
+        }
+
+    private static Point point(double x, double y) {
+        return gf.createPoint(new Coordinate(x, y));
+    }
+
+    private static String geoToGml(Point point) {
+        //CRS:84 long X,lat Y
+        //ESPG:4326 lat Y,long X
+        return "<Point"//
+        + " srsName='CRS:84'"// TODO: point.getSRID()  
+        + "><pos>"+point.getX()+" "+point.getY()+"</pos>  "// assumes  Y=lat  X=long 
+        + " </Point>";
+    }
+
+    private static LineString line(double x1, double y1, double x2, double y2) {
+        return new LineString(new PackedCoordinateSequence.Double(new double[] { x1, y1, x2, y2 }, 2), gf);
+    }
+    /** 
+     * convert a lineString geometry to GML
+     * @param line
+     * @return String that is XML that is a GMLLiteral of line
+     */
+    private static String geoToGml(LineString line) {
+        StringBuilder coordString = new StringBuilder() ;
+        for (Coordinate coor : line.getCoordinates()) {
+            coordString.append(" ").append(coor.x).append(" ").append(coor.y); //ESPG:4326 lat/long
+        }
+        return " <gml:LineString srsName=\"http://www.opengis.net/def/crs/EPSG/0/4326\" xmlns:gml='http://www.opengis.net/gml'>\n"  
+                + "<gml:posList srsDimension=\"2\">"//
+                + coordString //
+                + "</gml:posList></gml:LineString >";
+    }
+
+    private static Polygon poly(double[] arr) {
+        LinearRing r1 = gf.createLinearRing(new PackedCoordinateSequence.Double(arr, 2));
+        Polygon p1 = gf.createPolygon(r1, new LinearRing[] {});
+        return p1;
+    }
+    /** 
+     * convert a Polygon geometry to GML
+     * @param geometry
+     * @return String that is XML that is a GMLLiteral of line
+     */
+    private static String geoToGml(Polygon poly) {
+        StringBuilder coordString = new StringBuilder() ;
+        for (Coordinate coor : poly.getCoordinates()) {
+            coordString.append(" ").append(coor.x).append(" ").append(coor.y); //ESPG:4326 lat/long
+            //with commas:  coordString.append(" ").append(coor.x).append(",").append(coor.y); 
+        }
+        return "<gml:Polygon srsName=\"EPSG:4326\"  xmlns:gml='http://www.opengis.net/gml'>\r\n"//
+                + "<gml:exterior><gml:LinearRing>\r\n"//
+                + "<gml:posList srsDimension='2'>\r\n"
+                +  coordString
+                + "</gml:posList>\r\n"//
+                + "</gml:LinearRing></gml:exterior>\r\n</gml:Polygon>\r\n";
+    }
+
+    private static double[] bbox(double x1, double y1, double x2, double y2) {
+        return new double[] { x1, y1, x1, y2, x2, y2, x2, y1, x1, y1 };
+    }
+
+    public void compare(CloseableIteration<Statement, ?> actual, Geometry... expected) throws Exception {
+        Set<Statement> expectedSet = Sets.newHashSet();
+        for (Geometry geo : expected) {
+            expectedSet.add(RyaToRdfConversions.convertStatement(RyaStatement(geo,this.schemaToTest, encodeMethod)));
+        }
+
+        Assert.assertEquals(expectedSet, getSet(actual));
+    }
+
+    private static <X> Set<X> getSet(CloseableIteration<X, ?> iter) throws Exception {
+        Set<X> set = new HashSet<X>();
+        while (iter.hasNext()) {
+            set.add(iter.next());
+        }
+        return set;
+    }
+
+    private static Geometry[] EMPTY_RESULTS = {};
+
+    @Test
+    public void testParsePoly() throws Exception {
+        assertParseable(D);
+    }
+
+    @Test
+    public void testParseLine() throws Exception {
+        assertParseable(E);
+    }
+
+    @Test
+    public void testParsePoint() throws Exception {
+        assertParseable(F);
+    }
+
+    /**
+     * Convert Geometry to Wkt|GML (schemaToTest), parse to Geometry, and compare to original.
+     * @throws ParseException
+     */
+    public void assertParseable(Geometry originalGeom) throws ParseException {
+        Geometry parsedGeom = GeoParseUtils.getGeometry(genericStatement(originalGeom,schemaToTest, encodeMethod));
+        assertTrue("Parsed should equal original: "+originalGeom+" parsed: "+parsedGeom, originalGeom.equalsNorm(parsedGeom));
+        // assertEquals( originalGeom, parsedGeom ); //also passes
+        // assertTrue( originalGeom.equalsExact(parsedGeom) ); //also passes
+    }
+
+    @Test
+    public void testEquals() throws Exception {
+        // point
+        compare(g.queryEquals(F, EMPTY_CONSTRAINTS), F);
+        compare(g.queryEquals(point(-1, -1), EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+
+        // line
+        compare(g.queryEquals(E, EMPTY_CONSTRAINTS), E);
+        compare(g.queryEquals(line(-1, -1, 0, 0), EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+
+        // poly
+        compare(g.queryEquals(A, EMPTY_CONSTRAINTS), A);
+        compare(g.queryEquals(poly(bbox(-2, -2, 1, 2)), EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+
+    }
+
+    @Test
+    public void testDisjoint() throws Exception {
+        // point
+        compare(g.queryDisjoint(F, EMPTY_CONSTRAINTS), B, C, D, E, G);
+
+        // line
+        compare(g.queryDisjoint(E, EMPTY_CONSTRAINTS), B, C, F, G);
+
+        // poly
+        compare(g.queryDisjoint(A, EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+        compare(g.queryDisjoint(B, EMPTY_CONSTRAINTS), C, D, F, E, G);
+    }
+
+    @Test
+    @Ignore
+    public void testIntersectsPoint() throws Exception {
+        // This seems like a bug
+        //   scala.MatchError: POINT (2 4) (of class com.vividsolutions.jts.geom.Point)
+        //   at org.locationtech.geomesa.filter.FilterHelper$.updateToIDLSafeFilter(FilterHelper.scala:53)
+        // compare(g.queryIntersects(F, EMPTY_CONSTRAINTS), A, F);
+        // compare(g.queryIntersects(F, EMPTY_CONSTRAINTS), EMPTY_RESULTS);    
+    }
+
+    @Ignore
+    @Test
+    public void testIntersectsLine() throws Exception {
+        // This seems like a bug
+        // fails with: 
+        //     scala.MatchError: LINESTRING (2 0, 3 3) (of class com.vividsolutions.jts.geom.LineString)
+        //     at org.locationtech.geomesa.filter.FilterHelper$.updateToIDLSafeFilter(FilterHelper.scala:53)
+        //compare(g.queryIntersects(E, EMPTY_CONSTRAINTS), A, E, D);
+        //compare(g.queryIntersects(E, EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+    }
+
+    @Test
+    public void testIntersectsPoly() throws Exception {
+        compare(g.queryIntersects(A, EMPTY_CONSTRAINTS), A, B, C, D, F, E, G);
+    }
+
+    @Test
+    public void testTouchesPoint() throws Exception {
+        compare(g.queryTouches(F, EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+        compare(g.queryTouches(G, EMPTY_CONSTRAINTS), A, C);
+    }
+
+    @Test
+    public void testTouchesLine() throws Exception {
+        compare(g.queryTouches(E, EMPTY_CONSTRAINTS), D);
+    }
+
+    @Test
+    public void testTouchesPoly() throws Exception {
+        compare(g.queryTouches(A, EMPTY_CONSTRAINTS), C,G);
+    }
+
+    @Test
+    public void testCrossesPoint() throws Exception {
+        compare(g.queryCrosses(F, EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+        compare(g.queryCrosses(G, EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+        // bug? java.lang.IllegalStateException:  getX called on empty Point
+        //    compare(g.queryCrosses(point(2, 0), EMPTY_CONSTRAINTS), E);
+    }
+
+    @Ignore
+    @Test
+    public void testCrossesLine() throws Exception {
+        // fails with:
+        //     java.lang.IllegalStateException: getX called on empty Point
+        //      at com.vividsolutions.jts.geom.Point.getX(Point.java:124)
+        //      at org.locationtech.geomesa.utils.geohash.GeohashUtils$.considerCandidate$1(GeohashUtils.scala:1023)
+
+        // compare(g.queryCrosses(E, EMPTY_CONSTRAINTS), A);
+    }
+
+    @Test
+    public void testCrossesPoly() throws Exception {
+        compare(g.queryCrosses(A, EMPTY_CONSTRAINTS), E);
+        compare(g.queryCrosses(poly(bbox(-0.9, -2.9, -0.1, -1.1)), EMPTY_CONSTRAINTS), E);
+    }
+
+    @Test
+    public void testWithin() throws Exception {
+        // point
+        // geomesa bug? scala.MatchError: POINT (2 4) (of class com.vividsolutions.jts.geom.Point)
+        //    compare(g.queryWithin(F, EMPTY_CONSTRAINTS), F);
+
+        // line
+        // geomesa bug? scala.MatchError: LINESTRING (2 0, 3 2) (of class com.vividsolutions.jts.geom.LineString)
+        //    compare(g.queryWithin(E, EMPTY_CONSTRAINTS), E);
+
+        // poly
+        compare(g.queryWithin(A, EMPTY_CONSTRAINTS), A, B, F);
+    }
+
+    @Test
+    public void testContainsPoint() throws Exception {
+        compare(g.queryContains(F, EMPTY_CONSTRAINTS), A, F);
+    }
+
+    @Ignore
+    @Test
+    public void testContainsLine() throws Exception {
+        // compare(g.queryContains(E, EMPTY_CONSTRAINTS), E);
+    }
+
+    @Test
+    public void testContainsPoly() throws Exception {
+        compare(g.queryContains(A, EMPTY_CONSTRAINTS), A);
+        compare(g.queryContains(B, EMPTY_CONSTRAINTS), A, B);
+    }
+
+    @Ignore
+    @Test
+    public void testOverlapsPoint() throws Exception {
+        // compare(g.queryOverlaps(F, EMPTY_CONSTRAINTS), F);
+        // You cannot have overlapping points
+        // compare(g.queryOverlaps(F, EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+    }
+
+    @Ignore
+    @Test
+    public void testOverlapsLine() throws Exception {
+        // compare(g.queryOverlaps(E, EMPTY_CONSTRAINTS), A, E);
+        // You cannot have overlapping lines
+        // compare(g.queryOverlaps(E, EMPTY_CONSTRAINTS), EMPTY_RESULTS);
+    }
+
+    @Test
+    public void testOverlapsPoly() throws Exception {
+        compare(g.queryOverlaps(A, EMPTY_CONSTRAINTS), D);
+    }
+
+}