Repository: incubator-rya Updated Branches: refs/heads/master a60208625 -> 3917471b5
http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3917471b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/java/org/apache/rya/indexing/pcj/functions/geo/UnionRdf4J.java ---------------------------------------------------------------------- diff --git a/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/java/org/apache/rya/indexing/pcj/functions/geo/UnionRdf4J.java b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/java/org/apache/rya/indexing/pcj/functions/geo/UnionRdf4J.java new file mode 100644 index 0000000..e86eb02 --- /dev/null +++ b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/java/org/apache/rya/indexing/pcj/functions/geo/UnionRdf4J.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.rya.indexing.pcj.functions.geo; + +public class UnionRdf4J extends FunctionAdapter { + public UnionRdf4J() { + super(new org.eclipse.rdf4j.query.algebra.evaluation.function.geosparql.Union()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3917471b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/geosparql/SpatialSupportInitializer.java ---------------------------------------------------------------------- diff --git a/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/geosparql/SpatialSupportInitializer.java b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/geosparql/SpatialSupportInitializer.java new file mode 100644 index 0000000..ff385ba --- /dev/null +++ b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/geosparql/SpatialSupportInitializer.java @@ -0,0 +1,240 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.eclipse.rdf4j.query.algebra.evaluation.function.geosparql; + +import java.io.IOException; + +import com.spatial4j.core.context.SpatialContext; +import com.spatial4j.core.context.jts.JtsSpatialContext; +import com.spatial4j.core.shape.Shape; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.io.WKTWriter; + +/** + * See https://bitbucket.org/pulquero/sesame-geosparql-jts + */ +public class SpatialSupportInitializer extends SpatialSupport { + + @Override + protected SpatialContext createSpatialContext() { + return JtsSpatialContext.GEO; + } + + @Override + protected SpatialAlgebra createSpatialAlgebra() { + return new JtsSpatialAlgebra(JtsSpatialContext.GEO); + } + + @Override + protected WktWriter createWktWriter() { + return new JtsWktWriter(JtsSpatialContext.GEO); + } + + static class JtsSpatialAlgebra implements SpatialAlgebra { + private final JtsSpatialContext context; + + public JtsSpatialAlgebra(JtsSpatialContext context) { + this.context = context; + } + + @Override + public Shape convexHull(Shape s) { + return context.makeShape(context.getGeometryFrom(s).convexHull()); + } + + @Override + public Shape boundary(Shape s) { + return context.makeShape(context.getGeometryFrom(s).getBoundary()); + } + + @Override + public Shape envelope(Shape s) { + return context.makeShape(context.getGeometryFrom(s).getEnvelope()); + } + + @Override + public Shape union(Shape s1, Shape s2) { + return context.makeShape(context.getGeometryFrom(s1).union(context.getGeometryFrom(s2))); + } + + @Override + public Shape intersection(Shape s1, Shape s2) { + return context.makeShape(context.getGeometryFrom(s1).intersection(context.getGeometryFrom(s2))); + } + + @Override + public Shape symDifference(Shape s1, Shape s2) { + return context.makeShape(context.getGeometryFrom(s1).symDifference(context.getGeometryFrom(s2))); + } + + @Override + public Shape difference(Shape s1, Shape s2) { + return context.makeShape(context.getGeometryFrom(s1).difference(context.getGeometryFrom(s2))); + } + + @Override + public boolean relate(Shape s1, Shape s2, String intersectionPattern) { + return context.getGeometryFrom(s1).relate(context.getGeometryFrom(s2), intersectionPattern); + } + + @Override + public boolean equals(Shape s1, Shape s2) { + return context.getGeometryFrom(s1).equalsNorm(context.getGeometryFrom(s2)); + } + + @Override + public boolean sfDisjoint(Shape s1, Shape s2) { + return relate(s1, s2, "FF*FF****"); + } + + @Override + public boolean sfIntersects(Shape s1, Shape s2) { + return relate(s1, s2, "T********") || relate(s1, s2, "*T*******") || relate(s1, s2, "***T*****") || relate(s1, s2, "****T****"); + } + + @Override + public boolean sfTouches(Shape s1, Shape s2) { + return relate(s1, s2, "FT*******") || relate(s1, s2, "F**T*****") || relate(s1, s2, "F***T****"); + } + + @Override + public boolean sfCrosses(Shape s1, Shape s2) { + Geometry g1 = context.getGeometryFrom(s1); + Geometry g2 = context.getGeometryFrom(s2); + int d1 = g1.getDimension(); + int d2 = g2.getDimension(); + if ((d1 == 0 && d2 == 1) || (d1 == 0 && d2 == 2) || (d1 == 1 && d2 == 2)) { + return g1.relate(g2, "T*T***T**"); + } else if (d1 == 1 && d2 == 1) { + return g1.relate(g2, "0*T***T**"); + } else { + return false; + } + } + + @Override + public boolean sfWithin(Shape s1, Shape s2) { + return relate(s1, s2, "T*F**F***"); + } + + @Override + public boolean sfContains(Shape s1, Shape s2) { + return relate(s1, s2, "T*****FF*"); + } + + @Override + public boolean sfOverlaps(Shape s1, Shape s2) { + Geometry g1 = context.getGeometryFrom(s1); + Geometry g2 = context.getGeometryFrom(s2); + int d1 = g1.getDimension(); + int d2 = g2.getDimension(); + if ((d1 == 2 && d2 == 2) || (d1 == 0 && d2 == 0)) { + return g1.relate(g2, "T*T***T**"); + } else if (d1 == 1 && d2 == 1) { + return g1.relate(g2, "1*T***T**"); + } else { + return false; + } + } + + @Override + public boolean ehDisjoint(Shape s1, Shape s2) { + return relate(s1, s2, "FF*FF****"); + } + + @Override + public boolean ehMeet(Shape s1, Shape s2) { + return relate(s1, s2, "FT*******") || relate(s1, s2, "F**T*****") || relate(s1, s2, "F***T****"); + } + + @Override + public boolean ehOverlap(Shape s1, Shape s2) { + return relate(s1, s2, "T*T***T**"); + } + + @Override + public boolean ehCovers(Shape s1, Shape s2) { + return relate(s1, s2, "T*TFT*FF*"); + } + + @Override + public boolean ehCoveredBy(Shape s1, Shape s2) { + return relate(s1, s2, "TFF*TFT**"); + } + + @Override + public boolean ehInside(Shape s1, Shape s2) { + return relate(s1, s2, "TFF*FFT**"); + } + + @Override + public boolean ehContains(Shape s1, Shape s2) { + return relate(s1, s2, "T*TFF*FF*"); + } + + @Override + public boolean rcc8dc(Shape s1, Shape s2) { + return relate(s1, s2, "FFTFFTTTT"); + } + + @Override + public boolean rcc8ec(Shape s1, Shape s2) { + return relate(s1, s2, "FFTFTTTTT"); + } + + @Override + public boolean rcc8po(Shape s1, Shape s2) { + return relate(s1, s2, "TTTTTTTTT"); + } + + @Override + public boolean rcc8tppi(Shape s1, Shape s2) { + return relate(s1, s2, "TTTFTTFFT"); + } + + @Override + public boolean rcc8tpp(Shape s1, Shape s2) { + return relate(s1, s2, "TFFTTFTTT"); + } + + @Override + public boolean rcc8ntpp(Shape s1, Shape s2) { + return relate(s1, s2, "TFFTFFTTT"); + } + + @Override + public boolean rcc8ntppi(Shape s1, Shape s2) { + return relate(s1, s2, "TTTFFTFFT"); + } + + } + + static class JtsWktWriter implements WktWriter { + private final JtsSpatialContext context; + + public JtsWktWriter(JtsSpatialContext context) { + this.context = context; + } + + @Override + public String toWkt(Shape s) throws IOException { + return new WKTWriter().write(context.getGeometryFrom(s)); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3917471b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/resources/META-INF/services/org.openrdf.query.algebra.evaluation.function.Function ---------------------------------------------------------------------- diff --git a/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/resources/META-INF/services/org.openrdf.query.algebra.evaluation.function.Function b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/resources/META-INF/services/org.openrdf.query.algebra.evaluation.function.Function new file mode 100644 index 0000000..ddfb09b --- /dev/null +++ b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/main/resources/META-INF/services/org.openrdf.query.algebra.evaluation.function.Function @@ -0,0 +1,30 @@ +org.apache.rya.indexing.pcj.functions.geo.DistanceRdf4J +org.apache.rya.indexing.pcj.functions.geo.ConvexHullRdf4J +org.apache.rya.indexing.pcj.functions.geo.BoundaryRdf4J +org.apache.rya.indexing.pcj.functions.geo.EnvelopeRdf4J +org.apache.rya.indexing.pcj.functions.geo.UnionRdf4J +org.apache.rya.indexing.pcj.functions.geo.IntersectionRdf4J +org.apache.rya.indexing.pcj.functions.geo.SymmetricDifferenceRdf4J +org.apache.rya.indexing.pcj.functions.geo.DifferenceRdf4J +org.apache.rya.indexing.pcj.functions.geo.RelateRdf4J +org.apache.rya.indexing.pcj.functions.geo.SfDisjointRdf4J +org.apache.rya.indexing.pcj.functions.geo.SfIntersectsRdf4J +org.apache.rya.indexing.pcj.functions.geo.SfTouchesRdf4J +org.apache.rya.indexing.pcj.functions.geo.SfCrossesRdf4J +org.apache.rya.indexing.pcj.functions.geo.SfWithinRdf4J +org.apache.rya.indexing.pcj.functions.geo.SfContainsRdf4J +org.apache.rya.indexing.pcj.functions.geo.SfOverlapsRdf4J +org.apache.rya.indexing.pcj.functions.geo.EhDisjointRdf4J +org.apache.rya.indexing.pcj.functions.geo.EhMeetRdf4J +org.apache.rya.indexing.pcj.functions.geo.EhOverlapRdf4J +org.apache.rya.indexing.pcj.functions.geo.EhCoversRdf4J +org.apache.rya.indexing.pcj.functions.geo.EhCoveredByRdf4J +org.apache.rya.indexing.pcj.functions.geo.EhInsideRdf4J +org.apache.rya.indexing.pcj.functions.geo.EhContainsRdf4J +org.apache.rya.indexing.pcj.functions.geo.RCC8DCRdf4J +org.apache.rya.indexing.pcj.functions.geo.RCC8ECRdf4J +org.apache.rya.indexing.pcj.functions.geo.RCC8PORdf4J +org.apache.rya.indexing.pcj.functions.geo.RCC8TPPIRdf4J +org.apache.rya.indexing.pcj.functions.geo.RCC8TPPRdf4J +org.apache.rya.indexing.pcj.functions.geo.RCC8NTPPRdf4J +org.apache.rya.indexing.pcj.functions.geo.RCC8NTPPIRdf4J http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3917471b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/test/java/org/apache/rya/indexing/pcj/functions/geo/GeoFunctionsIT.java ---------------------------------------------------------------------- diff --git a/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/test/java/org/apache/rya/indexing/pcj/functions/geo/GeoFunctionsIT.java b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/test/java/org/apache/rya/indexing/pcj/functions/geo/GeoFunctionsIT.java new file mode 100644 index 0000000..97873bf --- /dev/null +++ b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/test/java/org/apache/rya/indexing/pcj/functions/geo/GeoFunctionsIT.java @@ -0,0 +1,369 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.rya.indexing.pcj.functions.geo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.rya.api.domain.RyaStatement; +import org.apache.rya.api.domain.RyaType; +import org.apache.rya.indexing.pcj.fluo.ITBase; +import org.apache.rya.indexing.pcj.fluo.api.CreatePcj; +import org.apache.rya.indexing.pcj.fluo.api.InsertTriples; +import org.apache.rya.indexing.pcj.storage.PrecomputedJoinStorage; +import org.apache.rya.indexing.pcj.storage.accumulo.AccumuloPcjStorage; +import org.junit.Test; +import org.openrdf.model.Value; +import org.openrdf.model.ValueFactory; +import org.openrdf.model.impl.LiteralImpl; +import org.openrdf.model.impl.URIImpl; +import org.openrdf.query.BindingSet; +import org.openrdf.query.algebra.evaluation.ValueExprEvaluationException; +import org.openrdf.query.algebra.evaluation.function.Function; +import org.openrdf.query.algebra.evaluation.function.FunctionRegistry; +import org.openrdf.query.impl.BindingImpl; + +import com.google.common.base.Optional; +import com.google.common.collect.Sets; + +/** + * Performs integration tests PCJ Geospatial functions in SPARQL. + * Each test starts a Accumulo/Rya/Fluo single node stack and runs a continuous query, checking results. + */ +public class GeoFunctionsIT extends ITBase { + + @Test + public void verifySpiLoadedGeoFunctions() { + final String functions[] = { "distance", // + "convexHull", "boundary", "envelope", "union", "intersection", "symDifference", "difference", // + "relate", /* "equals", */ "sfDisjoint", "sfIntersects", "sfTouches", "sfCrosses", // + "sfWithin", "sfContains", "sfOverlaps", "ehDisjoint", "ehMeet", "ehOverlap", // + "ehCovers", "ehCoveredBy", "ehInside", "ehContains", "rcc8dc", "rcc8ec", // + "rcc8po", "rcc8tppi", "rcc8tpp", "rcc8ntpp", "rcc8ntppi" }; // + HashSet<String> functionsCheckList = new HashSet<String>(); + functionsCheckList.addAll(Arrays.asList(functions)); + for (String f : FunctionRegistry.getInstance().getKeys()) { + String functionShortName = f.replaceFirst("^.*/geosparql/(.*)", "$1"); + // System.out.println("Registered function: " + f + " shortname: " + functionShortName); + functionsCheckList.remove(functionShortName); + } + assertTrue("Missed loading these functions via SPI: " + functionsCheckList, functionsCheckList.isEmpty()); + } + + @Test + public void withGeoFilters() throws Exception { + final String geoWithinSelect = "PREFIX geo: <http://www.opengis.net/ont/geosparql#> "// + + "PREFIX ryageo: <tag:rya.apache.org,2017:function/geo#> "// + + "PREFIX geof: <http://www.opengis.net/def/function/geosparql/> "// + + "SELECT ?feature ?point ?wkt " // + + "{" // + + " ?feature a geo:Feature . "// + + " ?feature geo:hasGeometry ?point . "// + + " ?point a geo:Point . "// + + " ?point geo:asWKT ?wkt . "// + + " FILTER(ryageo:ehContains(?wkt, \"POLYGON((-77 39, -76 39, -76 38, -77 38, -77 39))\"^^geo:wktLiteral)) " // + + "}";// + final Set<RyaStatement> streamedTriples = Sets.newHashSet(makeRyaStatement("tag:rya.apache.org,2017:ex#feature", "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", "http://www.opengis.net/ont/geosparql#Feature"), // + makeRyaStatement("tag:rya.apache.org,2017:ex#feature", "http://www.opengis.net/ont/geosparql#hasGeometry", "tag:rya.apache.org,2017:ex#test_point"), // + makeRyaStatement("tag:rya.apache.org,2017:ex#test_point", "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", "http://www.opengis.net/ont/geosparql#Point"), // + makeRyaStatement("tag:rya.apache.org,2017:ex#test_point", "http://www.opengis.net/ont/geosparql#asWKT", new RyaType(new URIImpl("http://www.opengis.net/ont/geosparql#wktLiteral"), "Point(-77.03524 38.889468)")) // + ); + + Function fooFunction = new Function() { + @Override + public String getURI() { + return "tag:rya.apache.org,2017:function/geo#ehContains"; + } + + @Override + public Value evaluate(ValueFactory valueFactory, Value... args) throws ValueExprEvaluationException { + + if (args.length != 2) { + throw new ValueExprEvaluationException(getURI() + " requires exactly 3 arguments, got " + args.length); + } + // SpatialContext spatialContext = (new SpatialContextFactory()).newSpatialContext(); + // Shape shape1 = org.eclipse.rdf4j.query.algebra.evaluation.function.geosparql.FunctionArguments() .getShape(this, args[0], spatialContext); + // Shape shape2 = FunctionArguments.getShape(this, args[1], spatialContext); + // //https://github.com/eclipse/rdf4j/blob/master/core/queryalgebra/geosparql/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/function/geosparql/SpatialSupport.java + // boolean result = SpatialSupport.getSpatialAlgebra().ehContains(shape1, shape2); + // return valueFactory.createLiteral(result); + return valueFactory.createLiteral(true); + } + }; + + // Add our new function to the registry + FunctionRegistry.getInstance().add(fooFunction); + + // The expected results of the SPARQL query once the PCJ has been computed. + final Set<BindingSet> expected = new HashSet<>(); + expected.add(makeBindingSet(// + new BindingImpl("wkt", new LiteralImpl("Point(-77.03524 38.889468)", new URIImpl("http://www.opengis.net/ont/geosparql#wktLiteral"))), // + new BindingImpl("feature", new URIImpl("tag:rya.apache.org,2017:ex#feature")), // + new BindingImpl("point", new URIImpl("tag:rya.apache.org,2017:ex#test_point")))); + + // Create the PCJ table. + final PrecomputedJoinStorage pcjStorage = new AccumuloPcjStorage(accumuloConn, RYA_INSTANCE_NAME); + final String pcjId = pcjStorage.createPcj(geoWithinSelect); + + // Tell the Fluo app to maintain the PCJ. + new CreatePcj().withRyaIntegration(pcjId, pcjStorage, fluoClient, accumuloConn, RYA_INSTANCE_NAME); + + // Stream the data into Fluo. + new InsertTriples().insert(fluoClient, streamedTriples, Optional.<String> absent()); + + // Verify the end results of the query match the expected results. + fluo.waitForObservers(); + final Set<BindingSet> results = getQueryBindingSetValues(fluoClient, geoWithinSelect); + assertEquals(expected, results); + } + + @Test + public void GeoDistance() throws Exception { + String geoCitySelect = "PREFIX geo: <http://www.opengis.net/ont/geosparql#> " // + + "PREFIX geof: <http://www.opengis.net/def/function/geosparql/> " // + + "PREFIX uom: <http://www.opengis.net/def/uom/OGC/1.0/> " // + + "SELECT ?cityA ?cityB " // + // + "SELECT ?cityA ?cityB ?dist " // + + "WHERE { ?cityA geo:asWKT ?coord1 . " // + + " ?cityB geo:asWKT ?coord2 . " // + // + " BIND( (geof:distance(?coord1, ?coord2, uom:metre) / 1000) as ?dist) . " // currently not supported + + " FILTER ( 500000 > geof:distance(?coord1, ?coord2, uom:metre) ) . " // from brussels 173km to amsterdam + + " FILTER ( !sameTerm (?cityA, ?cityB) ) }"; // + + final URIImpl wktTypeUri = new URIImpl("http://www.opengis.net/ont/geosparql#wktLiteral"); + final String asWKT = "http://www.opengis.net/ont/geosparql#asWKT"; + final Set<RyaStatement> streamedTriples = Sets.newHashSet(// + makeRyaStatement("tag:rya.apache.org,2017:ex#dakar", asWKT, new RyaType(wktTypeUri, "Point(-17.45 14.69)")), // + makeRyaStatement("tag:rya.apache.org,2017:ex#dakar2", asWKT, new RyaType(wktTypeUri, "Point(-17.45 14.69)")), // + makeRyaStatement("tag:rya.apache.org,2017:ex#canberra", asWKT, new RyaType(wktTypeUri, "Point(149.12 -35.31)")), // + makeRyaStatement("tag:rya.apache.org,2017:ex#brussels", asWKT, new RyaType(wktTypeUri, "Point(4.35 50.85)")), // + makeRyaStatement("tag:rya.apache.org,2017:ex#amsterdam", asWKT, new RyaType(wktTypeUri, "Point(4.9 52.37)")) // + ); + + // The expected results of the SPARQL query once the PCJ has been computed. + final Set<BindingSet> expected = new HashSet<>(); + expected.add(makeBindingSet(new BindingImpl("cityA", new URIImpl("tag:rya.apache.org,2017:ex#dakar")), new BindingImpl("cityB", new URIImpl("tag:rya.apache.org,2017:ex#dakar2")))); + expected.add(makeBindingSet(new BindingImpl("cityA", new URIImpl("tag:rya.apache.org,2017:ex#dakar2")), new BindingImpl("cityB", new URIImpl("tag:rya.apache.org,2017:ex#dakar")))); + expected.add(makeBindingSet(new BindingImpl("cityA", new URIImpl("tag:rya.apache.org,2017:ex#brussels")), new BindingImpl("cityB", new URIImpl("tag:rya.apache.org,2017:ex#amsterdam")))); + expected.add(makeBindingSet(new BindingImpl("cityA", new URIImpl("tag:rya.apache.org,2017:ex#amsterdam")), new BindingImpl("cityB", new URIImpl("tag:rya.apache.org,2017:ex#brussels")))); + + // Create the PCJ table. + final PrecomputedJoinStorage pcjStorage = new AccumuloPcjStorage(accumuloConn, RYA_INSTANCE_NAME); + final String pcjId = pcjStorage.createPcj(geoCitySelect); + + // Tell the Fluo app to maintain the PCJ. + new CreatePcj().withRyaIntegration(pcjId, pcjStorage, fluoClient, accumuloConn, RYA_INSTANCE_NAME); + + // Stream the data into Fluo. + new InsertTriples().insert(fluoClient, streamedTriples, Optional.<String> absent()); + + // Verify the end results of the query match the expected results. + fluo.waitForObservers(); + final Set<BindingSet> results = getQueryBindingSetValues(fluoClient, geoCitySelect); + + results.forEach(res -> { + // System.out.println(res.getValue("cityA").stringValue() + " - " + res.getValue("cityB").stringValue() + " : " + // /* + res.getValue("dist").stringValue() + "km" */ + // ); + }); + + assertEquals(expected, results); + } + + /** + * sfwithin function test. This requires full blown JTS. + * If you see this error: "Unknown Shape definition [POLYGON" ... + * Then: + * (from Solr docs:) the field definition needs the attribute + * spatialContextFactory="com.spatial4j.core.context.jts.JtsSpatialContextFactory" + * or (this works) this system property must be set to : + * SpatialContextFactory=com.spatial4j.core.context.jts.JtsSpatialContextFactory + * If you see: + * java.lang.UnsupportedOperationException: Not supported due to licensing issues. Feel free to provide your own implementation by using something like JTS. + * Then add a bit of code to replace the default one that comes with RDF4J: + * SpatialSupportInitializer.java + * Here is one: https://bitbucket.org/pulquero/sesame-geosparql-jts + * + * @throws Exception + */ + // @Ignore("needs JTS initializer, see comments.") + @Test + public void withGeoSpatialSupportInitializer() throws Exception { + final String geoWithinSelect = "PREFIX geo: <http://www.opengis.net/ont/geosparql#> "// + + "PREFIX ryageo: <tag:rya.apache.org,2017:function/geo#> "// + + "PREFIX geof: <http://www.opengis.net/def/function/geosparql/> "// + + "SELECT ?feature ?point ?wkt " // + + "{" // + + " ?feature a geo:Feature . "// + + " ?feature geo:hasGeometry ?point . "// + + " ?point a geo:Point . "// + + " ?point geo:asWKT ?wkt . "// + + " FILTER(geof:sfWithin(?wkt, \"POLYGON((-78 39, -76 39, -76 38, -78 38, -78 39))\"^^geo:wktLiteral)) " // + + "}";// + final Set<RyaStatement> streamedTriples = Sets.newHashSet(// + makeRyaStatement("tag:rya.apache.org,2017:ex#feature", "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", "http://www.opengis.net/ont/geosparql#Feature"), // + makeRyaStatement("tag:rya.apache.org,2017:ex#feature", "http://www.opengis.net/ont/geosparql#hasGeometry", "tag:rya.apache.org,2017:ex#test_point"), // + makeRyaStatement("tag:rya.apache.org,2017:ex#test_point", "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", "http://www.opengis.net/ont/geosparql#Point"), // + makeRyaStatement("tag:rya.apache.org,2017:ex#test_point", "http://www.opengis.net/ont/geosparql#asWKT", new RyaType(new URIImpl("http://www.opengis.net/ont/geosparql#wktLiteral") // + , "Point(-77.03524 38.889468)")), // + makeRyaStatement("tag:rya.apache.org,2017:ex#skip_point", "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", "http://www.opengis.net/ont/geosparql#Point"), // + makeRyaStatement("tag:rya.apache.org,2017:ex#skip_point", "http://www.opengis.net/ont/geosparql#asWKT", new RyaType(new URIImpl("http://www.opengis.net/ont/geosparql#wktLiteral")// + , "Point(-10 10)")) // + ); + // Register geo functions from RDF4J is done automatically via SPI. + // The expected results of the SPARQL query once the PCJ has been computed. + final Set<BindingSet> expected = new HashSet<>(); + expected.add(makeBindingSet(new BindingImpl("wkt", (new LiteralImpl("Point(-77.03524 38.889468)", new URIImpl("http://www.opengis.net/ont/geosparql#wktLiteral")))), new BindingImpl("feature", new URIImpl("tag:rya.apache.org,2017:ex#feature")), new BindingImpl("point", new URIImpl("tag:rya.apache.org,2017:ex#test_point")))); + // expected.add(makeBindingSet(new BindingImpl("wkt", (new LiteralImpl("Point(-77.03524 38.889468)", new URIImpl("http://www.opengis.net/ont/geosparql#wktLiteral")))))); + // expected.add(makeBindingSet(new BindingImpl("wkt", new URIImpl("\"Point(-77.03524 38.889468)\"^^<http://www.opengis.net/ont/geosparql#wktLiteral>")), new BindingImpl("feature", new + // URIImpl("tag:rya.apache.org,2017:ex#feature")), new BindingImpl("point", new URIImpl("tag:rya.apache.org,2017:ex#test_point")))); + + // Create the PCJ table. + final PrecomputedJoinStorage pcjStorage = new AccumuloPcjStorage(accumuloConn, RYA_INSTANCE_NAME); + final String pcjId = pcjStorage.createPcj(geoWithinSelect); + + // Tell the Fluo app to maintain the PCJ. + new CreatePcj().withRyaIntegration(pcjId, pcjStorage, fluoClient, accumuloConn, RYA_INSTANCE_NAME); + + // Stream the data into Fluo. + new InsertTriples().insert(fluoClient, streamedTriples, Optional.<String> absent()); + + // Verify the end results of the query match the expected results. + fluo.waitForObservers(); + final Set<BindingSet> results = getQueryBindingSetValues(fluoClient, geoWithinSelect); + assertEquals(expected, results); + } + + /** + * This test does not rely on geoTools. The default implementation in RDF4J handles point intersections. + * + * @throws Exception + */ + @Test + public void withGeoIntersectsPoint() throws Exception { + String geoCitySelect = "PREFIX geo: <http://www.opengis.net/ont/geosparql#> " // + + "PREFIX geof: <http://www.opengis.net/def/function/geosparql/> " // + + "PREFIX uom: <http://www.opengis.net/def/uom/OGC/1.0/> " // + + "SELECT ?cityA ?cityB " // + + "WHERE { ?cityA geo:asWKT ?coord1 . " // + + " ?cityB geo:asWKT ?coord2 . " // + + " FILTER ( geof:sfIntersects(?coord1, ?coord2) ) " // + + " FILTER ( !sameTerm (?cityA, ?cityB) ) }"; // + + final URIImpl wktTypeUri = new URIImpl("http://www.opengis.net/ont/geosparql#wktLiteral"); + final String asWKT = "http://www.opengis.net/ont/geosparql#asWKT"; + final Set<RyaStatement> streamedTriples = Sets.newHashSet(// + makeRyaStatement("tag:rya.apache.org,2017:ex#dakar", asWKT, new RyaType(wktTypeUri, "Point(-17.45 14.69)")), // + makeRyaStatement("tag:rya.apache.org,2017:ex#canberra", asWKT, new RyaType(wktTypeUri, "Point(149.12 -35.31)")), // + makeRyaStatement("tag:rya.apache.org,2017:ex#brussels", asWKT, new RyaType(wktTypeUri, "Point(4.35 50.85)")), // + makeRyaStatement("tag:rya.apache.org,2017:ex#amsterdam", asWKT, new RyaType(wktTypeUri, "Point(4.9 52.37)")), // + makeRyaStatement("tag:rya.apache.org,2017:ex#amsterdam2", asWKT, new RyaType(wktTypeUri, "Point(4.9 52.37)")) // + ); + + // The expected results of the SPARQL query once the PCJ has been computed. + final Set<BindingSet> expected = new HashSet<>(); + expected.add(makeBindingSet(new BindingImpl("cityA", new URIImpl("tag:rya.apache.org,2017:ex#amsterdam")), new BindingImpl("cityB", new URIImpl("tag:rya.apache.org,2017:ex#amsterdam2")))); + expected.add(makeBindingSet(new BindingImpl("cityA", new URIImpl("tag:rya.apache.org,2017:ex#amsterdam2")), new BindingImpl("cityB", new URIImpl("tag:rya.apache.org,2017:ex#amsterdam")))); + + // Register geo functions from RDF4J is done automatically via SPI. + // Create the PCJ table. + final PrecomputedJoinStorage pcjStorage = new AccumuloPcjStorage(accumuloConn, RYA_INSTANCE_NAME); + final String pcjId = pcjStorage.createPcj(geoCitySelect); + + // Tell the Fluo app to maintain the PCJ. + new CreatePcj().withRyaIntegration(pcjId, pcjStorage, fluoClient, accumuloConn, RYA_INSTANCE_NAME); + + // Stream the data into Fluo. + new InsertTriples().insert(fluoClient, streamedTriples, Optional.<String> absent()); + + // Verify the end results of the query match the expected results. + fluo.waitForObservers(); + final Set<BindingSet> results = getQueryBindingSetValues(fluoClient, geoCitySelect); + + results.forEach(res -> { + // System.out.println(res.getValue("cityA").stringValue() + " - " + res.getValue("cityB").stringValue() + " : " + // /* + res.getValue("dist").stringValue() + "km" */ + // ); + }); + + assertEquals(expected, results); + } + + @Test + public void withTemporal() throws Exception { + final String dtPredUri = "http://www.w3.org/2006/time#inXSDDateTime"; + final String xmlDateTime = "http://www.w3.org/2001/XMLSchema#dateTime"; + // Find all stored dates. + String selectQuery = "PREFIX time: <http://www.w3.org/2006/time#> \n"// + + "PREFIX xml: <http://www.w3.org/2001/XMLSchema#> \n" // + + "PREFIX tempo: <tag:rya-rdf.org,2015:temporal#> \n"// + + "SELECT ?event ?time \n" // + + "WHERE { \n" // + + " ?event time:inXSDDateTime ?time . \n"// + // + " FILTER(?time > '2000-01-01T01:00:00Z'^^xml:dateTime) \n"// all + // + " FILTER(?time < '2007-01-01T01:01:03-08:00'^^xml:dateTime) \n"// after 2007 + + " FILTER(?time > '2001-01-01T01:01:03-08:00'^^xml:dateTime) \n"// after 3 seconds + + " FILTER('2007-01-01T01:01:01+09:00'^^xml:dateTime > ?time ) \n"// 2006/12/31 include 2006, not 2007,8 + + "}";// + + // create some resources and literals to make statements out of + String eventz = "<http://eventz>"; + final Set<RyaStatement> streamedTriples = Sets.newHashSet(// + makeRyaStatement(eventz, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", "<http://www.w3.org/2006/time#Instant>"), // + makeRyaStatement(eventz, dtPredUri, new RyaType(new URIImpl(xmlDateTime), "2001-01-01T01:01:01-08:00")), // one second + makeRyaStatement(eventz, dtPredUri, new RyaType(new URIImpl(xmlDateTime), "2001-01-01T04:01:02.000-05:00")), // 2 seconds + makeRyaStatement(eventz, dtPredUri, new RyaType(new URIImpl(xmlDateTime), "2001-01-01T01:01:03-08:00")), // 3 seconds + makeRyaStatement(eventz, dtPredUri, new RyaType(new URIImpl(xmlDateTime), "2001-01-01T01:01:03.999-08:00")), // 4 seconds + makeRyaStatement(eventz, dtPredUri, new RyaType(new URIImpl(xmlDateTime), "2001-01-01T09:01:05Z")), // 5 seconds + makeRyaStatement(eventz, dtPredUri, new RyaType(new URIImpl(xmlDateTime), "2006-01-01TZ")), // + makeRyaStatement(eventz, dtPredUri, new RyaType(new URIImpl(xmlDateTime), "2007-01-01TZ")), // + makeRyaStatement(eventz, dtPredUri, new RyaType(new URIImpl(xmlDateTime), "2008-01-01TZ"))); + + // The expected results of the SPARQL query once the PCJ has been computed. + final Set<BindingSet> expected = new HashSet<>(); + expected.add(makeBindingSet(new BindingImpl("time", new LiteralImpl("2001-01-01T09:01:03.999Z", new URIImpl(xmlDateTime))), new BindingImpl("event", new URIImpl(eventz)))); // + expected.add(makeBindingSet(new BindingImpl("time", new LiteralImpl("2001-01-01T09:01:05.000Z", new URIImpl(xmlDateTime))), new BindingImpl("event", new URIImpl(eventz)))); // + expected.add(makeBindingSet(new BindingImpl("time", new LiteralImpl("2006-01-01T00:00:00.000Z", new URIImpl(xmlDateTime))), new BindingImpl("event", new URIImpl(eventz)))); // + expected.add(makeBindingSet(new BindingImpl("time", new LiteralImpl("2006-01-01T00:00:00.000Z", new URIImpl(xmlDateTime))), new BindingImpl("event", new URIImpl(eventz)))); // + // expected.add(makeBindingSet(new BindingImpl("event", new URIImpl(eventz)), new BindingImpl("time", new LiteralImpl("2007-01-01T05:00:00.000Z", new URIImpl(xmlDateTime))))); // + // expected.add(makeBindingSet(new BindingImpl("event", new URIImpl(eventz)), new BindingImpl("time", new LiteralImpl("2008-01-01T05:00:00.000Z", new URIImpl(xmlDateTime))))); + + // Register geo functions from RDF4J is done automatically via SPI. + // Create the PCJ table. + final PrecomputedJoinStorage pcjStorage = new AccumuloPcjStorage(accumuloConn, RYA_INSTANCE_NAME); + final String pcjId = pcjStorage.createPcj(selectQuery); + + // Tell the Fluo app to maintain the PCJ. + new CreatePcj().withRyaIntegration(pcjId, pcjStorage, fluoClient, accumuloConn, RYA_INSTANCE_NAME); + + // Stream the data into Fluo. + new InsertTriples().insert(fluoClient, streamedTriples, Optional.<String> absent()); + + // Verify the end results of the query match the expected results. + fluo.waitForObservers(); + final Set<BindingSet> results = getQueryBindingSetValues(fluoClient, selectQuery); + assertEquals(expected, results); + } + +} + http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3917471b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/test/java/org/apache/rya/indexing/pcj/functions/geo/GeoFunctionsTest.java ---------------------------------------------------------------------- diff --git a/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/test/java/org/apache/rya/indexing/pcj/functions/geo/GeoFunctionsTest.java b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/test/java/org/apache/rya/indexing/pcj/functions/geo/GeoFunctionsTest.java new file mode 100644 index 0000000..f73fa8f --- /dev/null +++ b/extras/rya.pcj.fluo/rya.pcj.functions.geo/src/test/java/org/apache/rya/indexing/pcj/functions/geo/GeoFunctionsTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.rya.indexing.pcj.functions.geo; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.zookeeper.ClientCnxn; +import org.junit.Before; +import org.junit.Test; +import org.openrdf.query.algebra.evaluation.function.FunctionRegistry; + +/** + * Verifies that the geoFunctions are registered via SPI. + * Also see the more detailed integration test. + */ +public class GeoFunctionsTest { + @Before + public void before() { + org.apache.log4j.BasicConfigurator.configure(); + Logger.getRootLogger().setLevel(Level.ERROR); + Logger.getLogger(ClientCnxn.class).setLevel(Level.OFF); + } + + /** + * Thirty-some functions are registered via SPI. Make sure they are registered. + * This file lists functions to load: + * src/main/resources/META-INF/services/org.openrdf.query.algebra.evaluation.function.Function + */ + @Test + public void verifySpiLoadedGeoFunctions() { + final String functions[] = { "distance", // + "convexHull", "boundary", "envelope", "union", "intersection", "symDifference", "difference", // + "relate", /* "equals", */ "sfDisjoint", "sfIntersects", "sfTouches", "sfCrosses", // + "sfWithin", "sfContains", "sfOverlaps", "ehDisjoint", "ehMeet", "ehOverlap", // + "ehCovers", "ehCoveredBy", "ehInside", "ehContains", "rcc8dc", "rcc8ec", // + "rcc8po", "rcc8tppi", "rcc8tpp", "rcc8ntpp", "rcc8ntppi" }; // + HashSet<String> functionsCheckList = new HashSet<String>(); + functionsCheckList.addAll(Arrays.asList(functions)); + for (String f : FunctionRegistry.getInstance().getKeys()) { + String functionShortName = f.replaceFirst("^.*/geosparql/(.*)", "$1"); + // System.out.println("Registered function: " + f + " shortname: " + functionShortName); + functionsCheckList.remove(functionShortName); + } + assertTrue("Missed loading these functions via SPI: " + functionsCheckList, functionsCheckList.isEmpty()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3917471b/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 7524736..e635e25 100644 --- a/pom.xml +++ b/pom.xml @@ -221,6 +221,11 @@ under the License. <version>${project.version}</version> </dependency> <dependency> + <groupId>org.apache.rya</groupId> + <artifactId>rya.pcj.fluo.integration</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>org.apache.accumulo</groupId> <artifactId>accumulo-core</artifactId> <version>${accumulo.version}</version>
