sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jso...@apache.org
Subject [sis] branch geoapi-4.0 updated: Filter : add function register api, prepare SQL/MM function register
Date Thu, 11 Jul 2019 13:18:03 GMT
This is an automated email from the ASF dual-hosted git repository.

jsorel pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 4579037  Filter : add function register api, prepare SQL/MM function register
4579037 is described below

commit 457903718f00a37ab05e12570c7b4cb49ad3b554
Author: jsorel <johann.sorel@geomatys.com>
AuthorDate: Thu Jul 11 15:17:43 2019 +0200

    Filter : add function register api, prepare SQL/MM function register
---
 .../org/apache/sis/filter/AbstractFunction.java    | 141 +++++++++++++++++++++
 .../apache/sis/filter/DefaultFilterFactory.java    |  28 +++-
 .../src/main/java/org/apache/sis/filter/SQLMM.java |  58 +++++++++
 .../java/org/apache/sis/filter/ST_Transform.java   |  97 ++++++++++++++
 .../sis/internal/feature/FunctionRegister.java     |  54 ++++++++
 ...rg.apache.sis.internal.feature.FunctionRegister |   1 +
 .../test/java/org/apache/sis/filter/SQLMMTest.java |  84 ++++++++++++
 .../apache/sis/test/suite/FeatureTestSuite.java    |   3 +-
 8 files changed, 463 insertions(+), 3 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/AbstractFunction.java b/core/sis-feature/src/main/java/org/apache/sis/filter/AbstractFunction.java
new file mode 100644
index 0000000..219b76d
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/AbstractFunction.java
@@ -0,0 +1,141 @@
+/*
+ * 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.sis.filter;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
+import static org.apache.sis.util.ArgumentChecks.*;
+import org.apache.sis.util.ObjectConverters;
+import org.apache.sis.util.UnconvertibleObjectException;
+import org.opengis.filter.expression.Expression;
+import org.opengis.filter.expression.ExpressionVisitor;
+import org.opengis.filter.expression.Function;
+import org.opengis.filter.expression.Literal;
+
+/**
+ * Abstract function.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+abstract class AbstractFunction extends Node implements Function {
+
+    protected final String name;
+    protected final List<Expression> parameters;
+    protected final Literal fallback;
+
+    public AbstractFunction(final String name, final Expression[] parameters, final Literal
fallback) {
+        ensureNonNull("name", name);
+        this.name = name;
+        this.parameters = UnmodifiableArrayList.wrap(parameters);
+        this.fallback = fallback;
+    }
+
+    @Override
+    protected String name() {
+        return name;
+    }
+
+    @Override
+    protected Collection<?> getChildren() {
+        return parameters;
+    }
+
+    /**
+     * {@inheritDoc }
+     */
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * {@inheritDoc }
+     */
+    @Override
+    public List<Expression> getParameters() {
+        return parameters;
+    }
+
+    /**
+     * {@inheritDoc }
+     */
+    @Override
+    public Literal getFallbackValue() {
+        return fallback;
+    }
+
+    /**
+     * {@inheritDoc }
+     * Use the converters utility class to convert the default result object
+     * to the wished class.
+     */
+    @Override
+    public <T> T evaluate(final Object candidate, final Class<T> target) {
+        final Object value = evaluate(candidate);
+        if (target == null) {
+            return (T) value; // TODO - unsafe cast!!!!
+        }
+        try {
+            return ObjectConverters.convert(value, target);
+        } catch (UnconvertibleObjectException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * {@inheritDoc }
+     */
+    @Override
+    public Object accept(final ExpressionVisitor visitor, final Object extraData) {
+        return visitor.visit(this, extraData);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
+        hash = 89 * hash + (this.parameters != null ? this.parameters.hashCode() : 0);
+        hash = 89 * hash + (this.fallback != null ? this.fallback.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AbstractFunction other = (AbstractFunction) obj;
+        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
+            return false;
+        }
+        if (this.parameters != other.parameters && (this.parameters == null || !this.parameters.equals(other.parameters)))
{
+            return false;
+        }
+        if (this.fallback != other.fallback && (this.fallback == null || !this.fallback.equals(other.fallback)))
{
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java
b/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java
index 0f3ec0f..e74cef6 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/DefaultFilterFactory.java
@@ -17,16 +17,21 @@
 package org.apache.sis.filter;
 
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
 import java.util.Set;
+import org.apache.sis.internal.feature.FunctionRegister;
 import org.opengis.filter.*;
 import org.opengis.filter.capability.*;
+import org.opengis.filter.capability.SpatialOperator;
 import org.opengis.filter.expression.*;
 import org.opengis.filter.identity.*;
 import org.opengis.filter.sort.*;
 import org.opengis.filter.spatial.*;
 import org.opengis.filter.temporal.*;
-import org.opengis.filter.capability.SpatialOperator;       // Resolve ambiguity with org.opengis.filter.spatial.
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.Geometry;
 import org.opengis.util.GenericName;
@@ -46,6 +51,21 @@ import org.opengis.util.GenericName;
  * @module
  */
 public class DefaultFilterFactory implements FilterFactory2 {
+
+    //TODO we should use a class like FeatureNaming to store and find registers.
+    //but it is not accessible here, should we move it ?
+    private static Map<String,FunctionRegister> FUNCTION_REGISTERS = new HashMap<>();
+    static {
+        final Iterator<FunctionRegister> iterator = ServiceLoader.load(FunctionRegister.class).iterator();
+        while (iterator.hasNext()) {
+            FunctionRegister register = iterator.next();
+            for (String name : register.getNames()) {
+                FUNCTION_REGISTERS.put(name, register);
+            }
+        }
+    }
+
+
     /**
      * Creates a new factory.
      */
@@ -690,7 +710,11 @@ public class DefaultFilterFactory implements FilterFactory2 {
      */
     @Override
     public Function function(final String name, final Expression... parameters) {
-        throw new UnsupportedOperationException("Not supported yet.");
+        final FunctionRegister register = FUNCTION_REGISTERS.get(name);
+        if (register == null) {
+            throw new IllegalArgumentException("Unknown function "+name);
+        }
+        return register.create(name, parameters);
     }
 
     /**
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/SQLMM.java b/core/sis-feature/src/main/java/org/apache/sis/filter/SQLMM.java
new file mode 100644
index 0000000..6899095
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/SQLMM.java
@@ -0,0 +1,58 @@
+/*
+ * 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.sis.filter;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.sis.internal.feature.FunctionRegister;
+import org.opengis.filter.expression.Expression;
+import org.opengis.filter.expression.Function;
+
+/**
+ * SQL/MM function register.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class SQLMM implements FunctionRegister {
+
+    private static final Set<String> NAMES;
+    static {
+        Set<String> names = new HashSet<>();
+        names.add(ST_Transform.NAME);
+        NAMES = Collections.unmodifiableSet(names);
+    }
+
+    @Override
+    public String getIdentifier() {
+        return "SQL/MM";
+    }
+
+    @Override
+    public Set<String> getNames() {
+        return NAMES;
+    }
+
+    @Override
+    public Function create(String name, Expression... parameters) {
+        switch (name) {
+            case ST_Transform.NAME : return new ST_Transform(parameters);
+        }
+        throw new IllegalArgumentException("Unknown function "+name);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Transform.java b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Transform.java
new file mode 100644
index 0000000..89995e9
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/ST_Transform.java
@@ -0,0 +1,97 @@
+/*
+ * 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.sis.filter;
+
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.internal.feature.FeatureExpression;
+import org.apache.sis.internal.feature.jts.JTS;
+import org.apache.sis.referencing.CRS;
+import org.locationtech.jts.geom.Geometry;
+import org.opengis.feature.AttributeType;
+import org.opengis.feature.FeatureType;
+import org.opengis.feature.PropertyType;
+import org.opengis.filter.expression.Expression;
+import org.opengis.filter.expression.Literal;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+/**
+ * TODO implement all SQL/MM specification functions.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+final class ST_Transform extends AbstractFunction implements FeatureExpression {
+
+    public static final String NAME = "ST_Transform";
+
+    private final CoordinateReferenceSystem outCrs;
+
+    public ST_Transform(Expression[] parameters) {
+        super(NAME, parameters, null);
+        if (parameters.length != 2) throw new IllegalArgumentException("Reproject function
expect 2 parameters, an expression for the geometry and a literal for the target CRS.");
+        if (!(parameters[0] instanceof FeatureExpression)) {
+            throw new IllegalArgumentException("First expression must be a FeatureExpression");
+        }
+        if (!(parameters[1] instanceof Literal)) {
+            throw new IllegalArgumentException("Second expression must be a Literal");
+        }
+        final String crsCode = parameters[1].evaluate(null, String.class);
+        try {
+            this.outCrs = CRS.forCode(crsCode);
+        } catch (FactoryException ex) {
+            throw new IllegalArgumentException("Requested CRS" + crsCode + "is undefined.\n"+ex.getMessage(),
ex);
+        }
+
+    }
+
+    @Override
+    public Object evaluate(Object object) {
+        final Expression inGeometry = parameters.get(0);
+        Geometry geometry = inGeometry.evaluate(object, Geometry.class);
+        if (geometry == null) return null;
+
+        try {
+            return JTS.transform(geometry, outCrs);
+        } catch (TransformException ex) {
+            warning(ex);
+        } catch (FactoryException ex) {
+            warning(ex);
+        }
+        return null;
+    }
+
+    @Override
+    public PropertyType expectedType(FeatureType type) {
+        final FeatureExpression inGeometry = (FeatureExpression) parameters.get(0);
+
+        PropertyType expectedType = inGeometry.expectedType(type);
+        if (!(expectedType instanceof AttributeType)) {
+            throw new IllegalArgumentException("First expression must result in a Geometric
attribute");
+        }
+        AttributeType att = (AttributeType) expectedType;
+        if (!Geometry.class.isAssignableFrom(att.getValueClass())) {
+            throw new IllegalArgumentException("First expression must result in a Geometric
attribute");
+        }
+        return new FeatureTypeBuilder().addAttribute(att).setCRS(outCrs).build();
+    }
+
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FunctionRegister.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FunctionRegister.java
new file mode 100644
index 0000000..d96d61a
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FunctionRegister.java
@@ -0,0 +1,54 @@
+/*
+ * 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.sis.internal.feature;
+
+import java.util.Set;
+import org.opengis.filter.expression.Expression;
+import org.opengis.filter.expression.Function;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public interface FunctionRegister {
+
+    /**
+     *
+     * Note : should it be the starting element of all function names to avoid any conflict
?
+     *
+     * @return factory unique name.
+     */
+    String getIdentifier();
+
+    /**
+     * Note : should we use GenericName ?
+     *
+     * @return set of supported function names.
+     */
+    Set<String> getNames();
+
+    /**
+     * Create a new function with given parameters.
+     *
+     * @param name function name, not null
+     * @param parameters function parameters
+     * @return
+     * @throws IllegalArgumentException if function name is unknown or parameters do not
match
+     */
+    Function create(String name, Expression... parameters) throws IllegalArgumentException;
+
+}
diff --git a/core/sis-feature/src/main/resources/META-INF/services/org.apache.sis.internal.feature.FunctionRegister
b/core/sis-feature/src/main/resources/META-INF/services/org.apache.sis.internal.feature.FunctionRegister
new file mode 100644
index 0000000..669fe2e
--- /dev/null
+++ b/core/sis-feature/src/main/resources/META-INF/services/org.apache.sis.internal.feature.FunctionRegister
@@ -0,0 +1 @@
+org.apache.sis.filter.SQLMM
\ No newline at end of file
diff --git a/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java b/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java
new file mode 100644
index 0000000..5f27152
--- /dev/null
+++ b/core/sis-feature/src/test/java/org/apache/sis/filter/SQLMMTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.sis.filter;
+
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.test.TestCase;
+import org.junit.Assert;
+import org.junit.Test;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Point;
+import org.opengis.feature.Feature;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.FilterFactory2;
+import org.opengis.filter.expression.Function;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public class SQLMMTest extends TestCase {
+    /**
+     * The factory to use for creating the objects to test.
+     */
+    private final FilterFactory2 factory = new DefaultFilterFactory();
+    private final GeometryFactory gf = new GeometryFactory();
+
+    /**
+     * Test SQL/MM ST_Transform function.
+     */
+    @Test
+    public void ST_TransformTest() {
+
+        CoordinateReferenceSystem inCrs = CommonCRS.WGS84.normalizedGeographic();
+        CoordinateReferenceSystem outCrs = CommonCRS.WGS84.geographic();
+
+        //test invalid
+        try {
+            factory.function("ST_Transform");
+        } catch (IllegalArgumentException ex) {
+            //ok
+        }
+
+        //create a test feature
+        final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
+        ftb.setName("test");
+        ftb.addAttribute(Point.class).setName("geom").setCRS(inCrs);
+        final FeatureType type = ftb.build();
+        final Point geometry = gf.createPoint(new Coordinate(10, 30));
+        geometry.setUserData(inCrs);
+        final Feature feature = type.newInstance();
+        feature.setPropertyValue("geom", geometry);
+
+        //transform function
+        final Function fct = factory.function("ST_Transform", factory.property("geom"), factory.literal("EPSG:4326"));
+
+        //check result
+        final Object newGeom = fct.evaluate(feature);
+        Assert.assertTrue(newGeom instanceof Point);
+        final Point trs = (Point) newGeom;
+        Assert.assertEquals(outCrs, trs.getUserData());
+        Assert.assertEquals(30.0, trs.getX(), 0.0);
+        Assert.assertEquals(10.0, trs.getY(), 0.0);
+
+
+    }
+
+}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
index 9682455..bbe93a9 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
@@ -17,8 +17,8 @@
 package org.apache.sis.test.suite;
 
 import org.apache.sis.test.TestSuite;
-import org.junit.runners.Suite;
 import org.junit.BeforeClass;
+import org.junit.runners.Suite;
 
 
 /**
@@ -55,6 +55,7 @@ import org.junit.BeforeClass;
     org.apache.sis.filter.DefaultObjectIdTest.class,
     org.apache.sis.filter.FilterByIdentifierTest.class,
     org.apache.sis.filter.ArithmeticFunctionTest.class,
+    org.apache.sis.filter.SQLMMTest.class,
     org.apache.sis.internal.feature.AttributeConventionTest.class,
     org.apache.sis.internal.feature.j2d.ShapePropertiesTest.class,
     org.apache.sis.internal.feature.Java2DTest.class,


Mime
View raw message