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 <[email protected]>
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,