sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jso...@apache.org
Subject [sis] 01/01: SQL/MM : add all specification expressions
Date Wed, 27 Nov 2019 14:48:36 GMT
This is an automated email from the ASF dual-hosted git repository.

jsorel pushed a commit to branch feat/sqlmm
in repository https://gitbox.apache.org/repos/asf/sis.git

commit e735e9a0ef79000e3de950297c61a10c891d647d
Author: jsorel <johann.sorel@geomatys.com>
AuthorDate: Tue Nov 26 15:08:18 2019 +0100

    SQL/MM : add all specification expressions
---
 .../org/apache/sis/filter/SpatialFunction.java     | 220 ++----------
 .../sis/internal/feature/jts/JTSMapping.java       | 373 ++++++++++++++++++++
 .../sis/internal/filter/FilterGeometryUtils.java   | 281 +++++++++++++++
 .../sqlmm/AbstractAccessorSpatialFunction.java     |  64 ++++
 .../sqlmm/AbstractBinarySpatialFunction.java       |  85 +++++
 .../filter/sqlmm/AbstractGeomConstructor.java      |  78 +++++
 .../filter/sqlmm/AbstractSpatialFunction.java      | 113 ++++++
 .../apache/sis/internal/filter/sqlmm/SQLMM.java    | 185 +++++++++-
 .../apache/sis/internal/filter/sqlmm/ST_Area.java  |  69 ++++
 .../sis/internal/filter/sqlmm/ST_AsBinary.java     |  68 ++++
 .../apache/sis/internal/filter/sqlmm/ST_AsGML.java |  68 ++++
 .../sis/internal/filter/sqlmm/ST_AsText.java       |  70 ++++
 .../sis/internal/filter/sqlmm/ST_Boundary.java     |  72 ++++
 .../sis/internal/filter/sqlmm/ST_Buffer.java       |  11 +-
 .../sis/internal/filter/sqlmm/ST_Centroid.java     |  14 +-
 .../sis/internal/filter/sqlmm/ST_Contains.java     |  63 ++++
 .../sis/internal/filter/sqlmm/ST_ConvexHull.java   |  72 ++++
 .../sis/internal/filter/sqlmm/ST_CoordDim.java     |  72 ++++
 .../sis/internal/filter/sqlmm/ST_Crosses.java      |  63 ++++
 .../sis/internal/filter/sqlmm/ST_Difference.java   |  67 ++++
 .../sis/internal/filter/sqlmm/ST_Dimension.java    |  67 ++++
 .../sis/internal/filter/sqlmm/ST_Disjoint.java     |  63 ++++
 .../sis/internal/filter/sqlmm/ST_Distance.java     |  63 ++++
 .../sis/internal/filter/sqlmm/ST_EndPoint.java     |  73 ++++
 .../sis/internal/filter/sqlmm/ST_Envelope.java     |   6 +-
 .../sis/internal/filter/sqlmm/ST_Equals.java       |  63 ++++
 .../internal/filter/sqlmm/ST_ExplicitPoint.java    |  75 ++++
 .../sis/internal/filter/sqlmm/ST_ExteriorRing.java |  71 ++++
 .../internal/filter/sqlmm/ST_GeomCollection.java   |  52 +++
 .../sis/internal/filter/sqlmm/ST_GeometryN.java    |  83 +++++
 .../sis/internal/filter/sqlmm/ST_GeometryType.java |  94 +++++
 .../internal/filter/sqlmm/ST_InteriorRingN.java    |  72 ++++
 .../sis/internal/filter/sqlmm/ST_Intersection.java |  67 ++++
 .../sis/internal/filter/sqlmm/ST_Intersects.java   |  63 ++++
 .../apache/sis/internal/filter/sqlmm/ST_Is3D.java  |  72 ++++
 .../sis/internal/filter/sqlmm/ST_IsClosed.java     |  69 ++++
 .../sis/internal/filter/sqlmm/ST_IsEmpty.java      |  67 ++++
 .../sis/internal/filter/sqlmm/ST_IsRing.java       |  69 ++++
 .../sis/internal/filter/sqlmm/ST_IsSimple.java     |  68 ++++
 .../sis/internal/filter/sqlmm/ST_IsValid.java      |  67 ++++
 .../sis/internal/filter/sqlmm/ST_Length.java       |  69 ++++
 .../sis/internal/filter/sqlmm/ST_LineString.java   |  73 ++--
 .../internal/filter/sqlmm/ST_MultiLineString.java  |  52 +++
 .../sis/internal/filter/sqlmm/ST_MultiPoint.java   |  52 +++
 .../sis/internal/filter/sqlmm/ST_MultiPolygon.java |  52 +++
 .../internal/filter/sqlmm/ST_NumGeometries.java    |  67 ++++
 .../internal/filter/sqlmm/ST_NumInteriorRings.java |  69 ++++
 .../sis/internal/filter/sqlmm/ST_NumPoints.java    |  67 ++++
 .../sis/internal/filter/sqlmm/ST_Overlaps.java     |  63 ++++
 .../sis/internal/filter/sqlmm/ST_Perimeter.java    |  71 ++++
 .../apache/sis/internal/filter/sqlmm/ST_Point.java | 114 ++++--
 .../sis/internal/filter/sqlmm/ST_PointN.java       |  83 +++++
 .../internal/filter/sqlmm/ST_PointOnSurface.java   |  75 ++++
 .../sis/internal/filter/sqlmm/ST_Polygon.java      |  60 ++++
 .../sis/internal/filter/sqlmm/ST_Relate.java       |  74 ++++
 .../apache/sis/internal/filter/sqlmm/ST_SRID.java  |  67 ++++
 .../sis/internal/filter/sqlmm/ST_Simplify.java     |   2 +-
 .../filter/sqlmm/ST_SimplifyPreserveTopology.java  |   2 +-
 .../sis/internal/filter/sqlmm/ST_StartPoint.java   |  75 ++++
 .../internal/filter/sqlmm/ST_SymDifference.java    |  67 ++++
 .../sis/internal/filter/sqlmm/ST_ToGeomColl.java   |  71 ++++
 .../sis/internal/filter/sqlmm/ST_ToLineString.java |  71 ++++
 .../sis/internal/filter/sqlmm/ST_ToMultiLine.java  |  71 ++++
 .../sis/internal/filter/sqlmm/ST_ToMultiPoint.java |  71 ++++
 .../internal/filter/sqlmm/ST_ToMultiPolygon.java   |  71 ++++
 .../sis/internal/filter/sqlmm/ST_ToPoint.java      |  71 ++++
 .../sis/internal/filter/sqlmm/ST_ToPolygon.java    |  71 ++++
 .../sis/internal/filter/sqlmm/ST_Touches.java      |  63 ++++
 .../sis/internal/filter/sqlmm/ST_Transform.java    |  10 +-
 .../apache/sis/internal/filter/sqlmm/ST_Union.java |  67 ++++
 .../sis/internal/filter/sqlmm/ST_Within.java       |  63 ++++
 .../org/apache/sis/internal/filter/sqlmm/ST_X.java |  67 ++++
 .../sis/internal/filter/sqlmm/ST_XFromBinary.java  | 385 +++++++++++++++++++++
 .../sis/internal/filter/sqlmm/ST_XFromGML.java     | 298 ++++++++++++++++
 .../sis/internal/filter/sqlmm/ST_XFromText.java    | 385 +++++++++++++++++++++
 .../org/apache/sis/internal/filter/sqlmm/ST_Y.java |  68 ++++
 .../org/apache/sis/internal/filter/sqlmm/ST_Z.java |  68 ++++
 .../sis/internal/filter/{ => sqlmm}/SQLMMTest.java |  31 +-
 .../apache/sis/test/suite/FeatureTestSuite.java    |   2 +-
 79 files changed, 6413 insertions(+), 277 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/filter/SpatialFunction.java b/core/sis-feature/src/main/java/org/apache/sis/filter/SpatialFunction.java
index c24f096..f7226b6 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/filter/SpatialFunction.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/filter/SpatialFunction.java
@@ -20,31 +20,17 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import javax.measure.Unit;
 import javax.measure.UnitConverter;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
 
-import org.opengis.feature.Feature;
-import org.opengis.feature.PropertyNotFoundException;
 import org.opengis.filter.FilterVisitor;
 import org.opengis.filter.expression.Expression;
-import org.opengis.filter.expression.PropertyName;
 import org.opengis.filter.spatial.BinarySpatialOperator;
-import org.opengis.referencing.NoSuchAuthorityCodeException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
 
-import org.apache.sis.coverage.grid.GridCoverage;
-import org.apache.sis.internal.feature.AttributeConvention;
-import org.apache.sis.internal.feature.Geometries;
-import org.apache.sis.internal.feature.GeometryWrapper;
-import org.apache.sis.internal.feature.WrapResolution;
 import org.apache.sis.math.Fraction;
 import org.apache.sis.measure.Units;
-import org.apache.sis.referencing.CRS;
-import org.apache.sis.setup.GeometryLibrary;
-import org.apache.sis.util.ObjectConverters;
-import org.apache.sis.util.UnconvertibleObjectException;
-import org.apache.sis.util.Utilities;
 
 import org.locationtech.jts.geom.Geometry;
 
@@ -57,20 +43,6 @@ import org.locationtech.jts.geom.Geometry;
  */
 abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOperator {
 
-    private static CoordinateReferenceSystem MERCATOR;
-
-    /**
-     * TODO: We should generify this. But too much code already depends on JTS, so for now I hack this
-     */
-    private static final Geometries<Geometry> SIS_GEOMETRY_FACTORY = (Geometries<Geometry>) Geometries.implementation(GeometryLibrary.JTS);
-
-    private static CoordinateReferenceSystem getMercator() throws FactoryException {
-        if (MERCATOR == null) {
-            MERCATOR = CRS.forCode("EPSG:3395");
-        }
-        return MERCATOR;
-    }
-
     protected SpatialFunction(Expression expression1, Expression expression2) {
         super(expression1, expression2);
     }
@@ -100,136 +72,6 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
         throw new UnsupportedOperationException("Not supported.");
     }
 
-    private static Geometry toGeometry(final Object object, Expression exp) {
-        Object value = null;
-        if ((exp instanceof PropertyName) && object instanceof Feature && ((PropertyName) exp).getPropertyName().isEmpty()) {
-            //Search for a default geometry.
-            try {
-                value = ((Feature) object).getPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString());
-            } catch (PropertyNotFoundException ex) {
-                //no defined default geometry
-            }
-        } else {
-            value = exp.evaluate(object);
-        }
-
-        if (value instanceof GeometryWrapper) value = ((GeometryWrapper) value).geometry;
-
-        Geometry candidate;
-        if (value instanceof GridCoverage) {
-            //use the coverage envelope
-            final GridCoverage coverage = (GridCoverage) value;
-            candidate = SIS_GEOMETRY_FACTORY.tryConvertToGeometry(coverage.getGridGeometry().getEnvelope(), WrapResolution.SPLIT);
-        } else {
-            try {
-                candidate = ObjectConverters.convert(value, Geometry.class);
-            } catch (UnconvertibleObjectException ex) {
-                //cound not convert value to a Geometry
-                candidate = null;
-            }
-        }
-        return candidate;
-    }
-
-    /**
-     * Reproject geometries to the same CRS if needed and if possible.
-     */
-    private static Geometry[] toSameCRS(final Geometry leftGeom, final Geometry rightGeom)
-            throws FactoryException, TransformException {
-
-        final CoordinateReferenceSystem leftCRS = Geometries.getCoordinateReferenceSystem(leftGeom);
-        final CoordinateReferenceSystem rightCRS = Geometries.getCoordinateReferenceSystem(rightGeom);
-
-        final CRSMatching.Match match = CRSMatching
-                .left(leftCRS)
-                .right(rightCRS);
-
-        final CRSMatching.Transformer<Geometry> projectGeom = (g, op) -> SIS_GEOMETRY_FACTORY.tryTransform(g, op, null);
-        return new Geometry[]{
-                match.transformLeftToCommon(leftGeom, projectGeom),
-                match.transformRightToCommon(rightGeom, projectGeom)
-        };
-    }
-
-    /**
-     * Reproject one or both geometries to the same crs, the matching crs will
-     * be compatible with the requested unit. return Array[leftGeometry,
-     * rightGeometry, matchingCRS];
-     */
-    private static Object[] toSameCRS(final Geometry leftGeom, final Geometry rightGeom, final Unit unit)
-            throws NoSuchAuthorityCodeException, FactoryException, TransformException {
-
-        final CoordinateReferenceSystem leftCRS = Geometries.getCoordinateReferenceSystem(leftGeom);
-        final CoordinateReferenceSystem rightCRS = Geometries.getCoordinateReferenceSystem(rightGeom);
-
-        if (leftCRS == null && rightCRS == null) {
-            //bother geometries doesn't have a defined SRID, we assume that both
-            //are in the same CRS
-            return new Object[]{leftGeom, rightGeom, null};
-        } else if (leftCRS == null || rightCRS == null || Utilities.equalsIgnoreMetadata(leftCRS, rightCRS)) {
-            //both are in the same CRS
-
-            final CoordinateReferenceSystem geomCRS = (leftCRS == null) ? rightCRS : leftCRS;
-
-            if (geomCRS.getCoordinateSystem().getAxis(0).getUnit().isCompatible(unit)) {
-                //the geometries crs is compatible with the requested unit, nothing to reproject
-                return new Object[]{leftGeom, rightGeom, geomCRS};
-            } else {
-                //the crs unit is not compatible, we must reproject both geometries to a more appropriate crs
-                if (Units.METRE.isCompatible(unit)) {
-                    //in that case we reproject to mercator EPSG:3395
-                    final CoordinateReferenceSystem mercator = getMercator();
-                    final CoordinateOperation trs = CRS.findOperation(geomCRS, mercator, null);
-
-                    return new Object[]{
-                        Geometries.transform(leftGeom, trs),
-                        Geometries.transform(rightGeom, trs),
-                        mercator};
-
-                } else {
-                    //we can not find a matching projection in this case
-                    throw new TransformException("Could not find a matching CRS for both geometries for unit :" + unit);
-                }
-            }
-
-        } else {
-            //both have different CRS, try to find the most appropriate crs amoung both
-
-            final CoordinateReferenceSystem matchingCRS;
-            final Object leftMatch;
-            final Object rightMatch;
-
-            if (leftCRS.getCoordinateSystem().getAxis(0).getUnit().isCompatible(unit)) {
-                matchingCRS = leftCRS;
-                final CoordinateOperation trs = CRS.findOperation(rightCRS, matchingCRS, null);
-                rightMatch = Geometries.transform(rightGeom, trs);
-                leftMatch = leftGeom;
-            } else if (rightCRS.getCoordinateSystem().getAxis(0).getUnit().isCompatible(unit)) {
-                matchingCRS = rightCRS;
-                final CoordinateOperation trs = CRS.findOperation(leftCRS, matchingCRS, null);
-                leftMatch = Geometries.transform(leftGeom, trs);
-                rightMatch = rightGeom;
-            } else {
-                //the crs unit is not compatible, we must reproject both geometries to a more appropriate crs
-                if (Units.METRE.isCompatible(unit)) {
-                    //in that case we reproject to mercator EPSG:3395
-                    matchingCRS = getMercator();
-
-                    CoordinateOperation trs = CRS.findOperation(leftCRS, matchingCRS, null);
-                    leftMatch = Geometries.transform(leftGeom, trs);
-                    trs = CRS.findOperation(rightCRS, matchingCRS, null);
-                    rightMatch = Geometries.transform(rightGeom, trs);
-
-                } else {
-                    //we can not find a matching projection in this case
-                    throw new TransformException("Could not find a matching CRS for both geometries for unit :" + unit);
-                }
-            }
-
-            return new Object[]{leftMatch, rightMatch, matchingCRS};
-        }
-    }
-
     /**
      * The {@value #NAME} filter.
      */
@@ -268,15 +110,15 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            final Geometry leftGeom = toGeometry(object, expression1);
-            final Geometry rightGeom = toGeometry(object, expression2);
+            final Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            final Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
             }
 
             try {
-                final Object[] values = toSameCRS(leftGeom, rightGeom, unit);
+                final Object[] values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom, unit);
 
                 if (values[2] == null) {
                     //no matching crs was found, assume both have the same and valid unit
@@ -323,8 +165,8 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            Geometry leftGeom = toGeometry(object, expression1);
-            Geometry rightGeom = toGeometry(object, expression2);
+            Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
@@ -332,7 +174,7 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
             final Geometry[] values;
             try {
-                values = toSameCRS(leftGeom, rightGeom);
+                values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom);
             } catch (FactoryException | TransformException ex) {
                 warning(ex);
                 return false;
@@ -376,8 +218,8 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            Geometry leftGeom = toGeometry(object, expression1);
-            Geometry rightGeom = toGeometry(object, expression2);
+            Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
@@ -385,7 +227,7 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
             final Geometry[] values;
             try {
-                values = toSameCRS(leftGeom, rightGeom);
+                values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom);
             } catch (FactoryException | TransformException ex) {
                 warning(ex);
                 return false;
@@ -430,8 +272,8 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            Geometry leftGeom = toGeometry(object, expression1);
-            Geometry rightGeom = toGeometry(object, expression2);
+            Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
@@ -439,7 +281,7 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
             final Geometry[] values;
             try {
-                values = toSameCRS(leftGeom, rightGeom);
+                values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom);
             } catch (FactoryException | TransformException ex) {
                 warning(ex);
                 return false;
@@ -501,15 +343,15 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            final Geometry leftGeom = toGeometry(object, expression1);
-            final Geometry rightGeom = toGeometry(object, expression2);
+            final Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            final Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
             }
 
             try {
-                final Object[] values = toSameCRS(leftGeom, rightGeom, unit);
+                final Object[] values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom, unit);
 
                 if (values[2] == null) {
                     //no matching crs was found, assume both have the same and valid unit
@@ -556,8 +398,8 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            Geometry leftGeom = toGeometry(object, expression1);
-            Geometry rightGeom = toGeometry(object, expression2);
+            Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
@@ -565,7 +407,7 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
             final Geometry[] values;
             try {
-                values = toSameCRS(leftGeom, rightGeom);
+                values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom);
             } catch (FactoryException | TransformException ex) {
                 warning(ex);
                 return false;
@@ -603,8 +445,8 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            Geometry leftGeom = toGeometry(object, expression1);
-            Geometry rightGeom = toGeometry(object, expression2);
+            Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
@@ -612,7 +454,7 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
             final Geometry[] values;
             try {
-                values = toSameCRS(leftGeom, rightGeom);
+                values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom);
             } catch (FactoryException | TransformException ex) {
                 warning(ex);
                 return false;
@@ -656,8 +498,8 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            Geometry leftGeom = toGeometry(object, expression1);
-            Geometry rightGeom = toGeometry(object, expression2);
+            Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
@@ -665,7 +507,7 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
             final Geometry[] values;
             try {
-                values = toSameCRS(leftGeom, rightGeom);
+                values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom);
             } catch (FactoryException | TransformException ex) {
                 warning(ex);
                 return false;
@@ -709,8 +551,8 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            Geometry leftGeom = toGeometry(object, expression1);
-            Geometry rightGeom = toGeometry(object, expression2);
+            Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
@@ -718,7 +560,7 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
             final Geometry[] values;
             try {
-                values = toSameCRS(leftGeom, rightGeom);
+                values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom);
             } catch (FactoryException | TransformException ex) {
                 warning(ex);
                 return false;
@@ -755,8 +597,8 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
         @Override
         public boolean evaluate(Object object) {
-            Geometry leftGeom = toGeometry(object, expression1);
-            Geometry rightGeom = toGeometry(object, expression2);
+            Geometry leftGeom = FilterGeometryUtils.toGeometry(object, expression1);
+            Geometry rightGeom = FilterGeometryUtils.toGeometry(object, expression2);
 
             if (leftGeom == null || rightGeom == null) {
                 return false;
@@ -764,7 +606,7 @@ abstract class SpatialFunction extends BinaryFunction implements BinarySpatialOp
 
             final Geometry[] values;
             try {
-                values = toSameCRS(leftGeom, rightGeom);
+                values = FilterGeometryUtils.toSameCRS(leftGeom, rightGeom);
             } catch (FactoryException | TransformException ex) {
                 warning(ex);
                 return false;
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/JTSMapping.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/JTSMapping.java
new file mode 100644
index 0000000..0937ff6
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/JTSMapping.java
@@ -0,0 +1,373 @@
+/*
+ * 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.jts;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPoint;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Polygon;
+
+/**
+ * Set of Utility function and methods for mapping geometry types.
+ *
+ * @author Quentin Boileau (Geomatys)
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+public final class JTSMapping {
+
+    private static final GeometryFactory GF = new GeometryFactory();
+
+    private JTSMapping() {
+    }
+
+    /**
+     * Force geometry type. If the given geometry is not of given class it will
+     * be adapted.
+     */
+    public static <T extends Geometry> T convertType(final Geometry geom, final Class<T> targetClass) {
+        if (geom == null) {
+            return null;
+        }
+
+        if (targetClass.isInstance(geom)) {
+            return (T) geom;
+        }
+
+        Geometry result;
+        if (targetClass == Point.class) {
+            result = convertToPoint(geom);
+        } else if (targetClass == MultiPoint.class) {
+            result = convertToMultiPoint(geom);
+        } else if (targetClass == LineString.class) {
+            result = convertToLineString(geom);
+        } else if (targetClass == MultiLineString.class) {
+            result = convertToMultiLineString(geom);
+        } else if (targetClass == Polygon.class) {
+            result = convertToPolygon(geom);
+        } else if (targetClass == MultiPolygon.class) {
+            result = convertToMultiPolygon(geom);
+        } else if (targetClass == GeometryCollection.class) {
+            result = convertToGeometryCollection(geom);
+        } else {
+            result = null;
+        }
+
+        if (result != null) {
+            //copy srid and user data
+            result.setSRID(geom.getSRID());
+            result.setUserData(geom.getUserData());
+        }
+
+        return targetClass.cast(result);
+    }
+
+    // Convert to Point --------------------------------------------------------
+    private static Point convertToPoint(final Geometry geom) {
+        if (geom instanceof Point) {
+            return convertToPoint((Point) geom);
+        } else if (geom instanceof MultiPoint) {
+            return convertToPoint((MultiPoint) geom);
+        } else if (geom instanceof LineString) {
+            return convertToPoint((LineString) geom);
+        } else if (geom instanceof MultiLineString) {
+            return convertToPoint((MultiLineString) geom);
+        } else if (geom instanceof Polygon) {
+            return convertToPoint((Polygon) geom);
+        } else if (geom instanceof MultiPolygon) {
+            return convertToPoint((MultiPolygon) geom);
+        }
+        return null;
+    }
+
+    private static Point convertToPoint(final Point pt) {
+        return pt;
+    }
+
+    private static Point convertToPoint(final MultiPoint pt) {
+        return pt.getCentroid();
+    }
+
+    private static Point convertToPoint(final LineString pt) {
+        return pt.getCentroid();
+    }
+
+    private static Point convertToPoint(final MultiLineString pt) {
+        return pt.getCentroid();
+    }
+
+    private static Point convertToPoint(final Polygon pt) {
+        return pt.getCentroid();
+    }
+
+    private static Point convertToPoint(final MultiPolygon pt) {
+        return pt.getCentroid();
+    }
+
+    // Convert to MultiPoint ---------------------------------------------------
+    private static MultiPoint convertToMultiPoint(final Geometry geom) {
+        if (geom instanceof Point) {
+            return convertToMultiPoint((Point) geom);
+        } else if (geom instanceof MultiPoint) {
+            return convertToMultiPoint((MultiPoint) geom);
+        } else if (geom instanceof LineString) {
+            return convertToMultiPoint((LineString) geom);
+        } else if (geom instanceof MultiLineString) {
+            return convertToMultiPoint((MultiLineString) geom);
+        } else if (geom instanceof Polygon) {
+            return convertToMultiPoint((Polygon) geom);
+        } else if (geom instanceof MultiPolygon) {
+            return convertToMultiPoint((MultiPolygon) geom);
+        }
+        return null;
+    }
+
+    private static MultiPoint convertToMultiPoint(final Point pt) {
+        return GF.createMultiPoint(new Point[]{pt});
+    }
+
+    private static MultiPoint convertToMultiPoint(final MultiPoint pt) {
+        return pt;
+    }
+
+    private static MultiPoint convertToMultiPoint(final LineString pt) {
+        return GF.createMultiPoint(pt.getCoordinates());
+    }
+
+    private static MultiPoint convertToMultiPoint(final MultiLineString pt) {
+        return GF.createMultiPoint(pt.getCoordinates());
+    }
+
+    private static MultiPoint convertToMultiPoint(final Polygon pt) {
+        return GF.createMultiPoint(pt.getCoordinates());
+    }
+
+    private static MultiPoint convertToMultiPoint(final MultiPolygon pt) {
+        return GF.createMultiPoint(pt.getCoordinates());
+    }
+
+    // Convert to LineString ---------------------------------------------------
+    private static LineString convertToLineString(final Geometry geom) {
+        if (geom instanceof Point) {
+            return convertToLineString((Point) geom);
+        } else if (geom instanceof MultiPoint) {
+            return convertToLineString((MultiPoint) geom);
+        } else if (geom instanceof LineString) {
+            return convertToLineString((LineString) geom);
+        } else if (geom instanceof MultiLineString) {
+            return convertToLineString((MultiLineString) geom);
+        } else if (geom instanceof Polygon) {
+            return convertToLineString((Polygon) geom);
+        } else if (geom instanceof MultiPolygon) {
+            return convertToLineString((MultiPolygon) geom);
+        }
+        return null;
+    }
+
+    private static LineString convertToLineString(final Point pt) {
+        return GF.createLineString(new Coordinate[]{pt.getCoordinate(), pt.getCoordinate()});
+    }
+
+    private static LineString convertToLineString(final MultiPoint pt) {
+        final Coordinate[] coords = pt.getCoordinates();
+        if (coords.length == 1) {
+            return GF.createLineString(new Coordinate[]{coords[0], coords[0]});
+        } else {
+            return GF.createLineString(coords);
+        }
+    }
+
+    private static LineString convertToLineString(final LineString pt) {
+        return pt;
+    }
+
+    private static LineString convertToLineString(final MultiLineString pt) {
+        return GF.createLineString(pt.getCoordinates());
+    }
+
+    private static LineString convertToLineString(final Polygon pt) {
+        return GF.createLineString(pt.getCoordinates());
+    }
+
+    private static LineString convertToLineString(final MultiPolygon pt) {
+        return GF.createLineString(pt.getCoordinates());
+    }
+
+    // Convert to MultiLineString ----------------------------------------------
+    private static MultiLineString convertToMultiLineString(final Geometry geom) {
+        if (geom instanceof Point) {
+            return convertToMultiLineString((Point) geom);
+        } else if (geom instanceof MultiPoint) {
+            return convertToMultiLineString((MultiPoint) geom);
+        } else if (geom instanceof LineString) {
+            return convertToMultiLineString((LineString) geom);
+        } else if (geom instanceof MultiLineString) {
+            return convertToMultiLineString((MultiLineString) geom);
+        } else if (geom instanceof Polygon) {
+            return convertToMultiLineString((Polygon) geom);
+        } else if (geom instanceof MultiPolygon) {
+            return convertToMultiLineString((MultiPolygon) geom);
+        }
+        return null;
+    }
+
+    private static MultiLineString convertToMultiLineString(final Point pt) {
+        return convertToMultiLineString(convertToLineString(pt));
+    }
+
+    private static MultiLineString convertToMultiLineString(final MultiPoint pt) {
+        return convertToMultiLineString(convertToLineString(pt));
+    }
+
+    private static MultiLineString convertToMultiLineString(final LineString pt) {
+        return GF.createMultiLineString(new LineString[]{pt});
+    }
+
+    private static MultiLineString convertToMultiLineString(final MultiLineString pt) {
+        return pt;
+    }
+
+    private static MultiLineString convertToMultiLineString(final Polygon pt) {
+        return convertToMultiLineString(GF.createLineString(pt.getCoordinates()));
+    }
+
+    private static MultiLineString convertToMultiLineString(final MultiPolygon pt) {
+        final int n = pt.getNumGeometries();
+        final LineString[] geoms = new LineString[n];
+        for (int i = 0; i < n; i++) {
+            geoms[i] = convertToLineString(pt.getGeometryN(i));
+        }
+        return GF.createMultiLineString(geoms);
+    }
+
+    // Convert to Polygon ------------------------------------------------------
+    private static Polygon convertToPolygon(final Geometry geom) {
+        if (geom instanceof Point) {
+            return convertToPolygon((Point) geom);
+        } else if (geom instanceof MultiPoint) {
+            return convertToPolygon((MultiPoint) geom);
+        } else if (geom instanceof LineString) {
+            return convertToPolygon((LineString) geom);
+        } else if (geom instanceof MultiLineString) {
+            return convertToPolygon((MultiLineString) geom);
+        } else if (geom instanceof Polygon) {
+            return convertToPolygon((Polygon) geom);
+        } else if (geom instanceof MultiPolygon) {
+            return convertToPolygon((MultiPolygon) geom);
+        }
+        return null;
+    }
+
+    private static Polygon convertToPolygon(final Point pt) {
+        LinearRing ring = GF.createLinearRing(new Coordinate[]{pt.getCoordinate(), pt.getCoordinate(), pt.getCoordinate(), pt.getCoordinate()});
+        return GF.createPolygon(ring, new LinearRing[0]);
+    }
+
+    private static Polygon convertToPolygon(final MultiPoint pt) {
+        return convertToPolygon(convertToLineString(pt));
+    }
+
+    private static Polygon convertToPolygon(final LineString pt) {
+        return GF.createPolygon(GF.createLinearRing(pt.getCoordinates()), new LinearRing[0]);
+    }
+
+    private static Polygon convertToPolygon(final MultiLineString pt) {
+        return convertToPolygon(convertToLineString(pt));
+    }
+
+    private static Polygon convertToPolygon(final Polygon pt) {
+        return pt;
+    }
+
+    private static Polygon convertToPolygon(final MultiPolygon pt) {
+        final int nbGeom = pt.getNumGeometries();
+        if (nbGeom == 0) {
+            return GF.createPolygon();
+        } else if (nbGeom == 1) {
+            return (Polygon) pt.getGeometryN(0);
+        } else {
+            return convertToPolygon(pt.convexHull());
+        }
+    }
+
+    // Convert to MultiPolygon -------------------------------------------------
+    private static MultiPolygon convertToMultiPolygon(final Geometry geom) {
+        if (geom instanceof Point) {
+            return convertToMultiPolygon((Point) geom);
+        } else if (geom instanceof MultiPoint) {
+            return convertToMultiPolygon((MultiPoint) geom);
+        } else if (geom instanceof LineString) {
+            return convertToMultiPolygon((LineString) geom);
+        } else if (geom instanceof MultiLineString) {
+            return convertToMultiPolygon((MultiLineString) geom);
+        } else if (geom instanceof Polygon) {
+            return convertToMultiPolygon((Polygon) geom);
+        } else if (geom instanceof MultiPolygon) {
+            return convertToMultiPolygon((MultiPolygon) geom);
+        }
+        return null;
+    }
+
+    private static MultiPolygon convertToMultiPolygon(final Point pt) {
+        return convertToMultiPolygon(convertToPolygon(pt));
+    }
+
+    private static MultiPolygon convertToMultiPolygon(final MultiPoint pt) {
+        return convertToMultiPolygon(convertToPolygon(pt));
+    }
+
+    private static MultiPolygon convertToMultiPolygon(final LineString pt) {
+        return convertToMultiPolygon(convertToPolygon(pt));
+    }
+
+    private static MultiPolygon convertToMultiPolygon(final MultiLineString pt) {
+        return convertToMultiPolygon(convertToPolygon(pt));
+    }
+
+    private static MultiPolygon convertToMultiPolygon(final Polygon pt) {
+        return GF.createMultiPolygon(new Polygon[]{pt});
+    }
+
+    private static MultiPolygon convertToMultiPolygon(final MultiPolygon pt) {
+        return pt;
+    }
+
+    private static GeometryCollection convertToGeometryCollection(final Geometry geom) {
+        if (geom instanceof GeometryCollection) {
+            return (GeometryCollection) geom;
+        }
+
+        if (geom instanceof Point) {
+            return convertToMultiPoint(geom);
+        } else if (geom instanceof LineString) {
+            return convertToMultiLineString(geom);
+        } else if (geom instanceof Polygon) {
+            return convertToMultiPolygon(geom);
+        }
+        return null;
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/FilterGeometryUtils.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/FilterGeometryUtils.java
new file mode 100644
index 0000000..0577685
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/FilterGeometryUtils.java
@@ -0,0 +1,281 @@
+/*
+ * 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.filter;
+
+import javax.measure.Unit;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.filter.CRSMatching;
+import org.apache.sis.internal.feature.AttributeConvention;
+import org.apache.sis.internal.feature.Geometries;
+import org.apache.sis.internal.feature.GeometryWrapper;
+import org.apache.sis.internal.feature.WrapResolution;
+import org.apache.sis.measure.Units;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.setup.GeometryLibrary;
+import org.apache.sis.util.ObjectConverters;
+import org.apache.sis.util.UnconvertibleObjectException;
+import org.apache.sis.util.Utilities;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.io.ParseException;
+import org.locationtech.jts.io.WKBReader;
+import org.locationtech.jts.io.WKBWriter;
+import org.locationtech.jts.io.WKTReader;
+import org.locationtech.jts.io.WKTWriter;
+import org.opengis.feature.Feature;
+import org.opengis.feature.PropertyNotFoundException;
+import org.opengis.filter.expression.Expression;
+import org.opengis.filter.expression.PropertyName;
+import org.opengis.referencing.NoSuchAuthorityCodeException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class FilterGeometryUtils {
+
+    //store wkb/wkt readers/writers by thread since they are not concurrent.
+    public static final GeometryFactory GF = new GeometryFactory();
+    public static final ThreadLocal<WKBReader> WKB_READERS = new ThreadLocal<WKBReader>();
+    public static final ThreadLocal<WKTReader> WKT_READERS = new ThreadLocal<WKTReader>();
+    public static final ThreadLocal<WKBWriter> WKB_WRITERS = new ThreadLocal<WKBWriter>();
+    public static final ThreadLocal<WKTWriter> WKT_WRITERS = new ThreadLocal<WKTWriter>();
+
+    private FilterGeometryUtils() {
+    }
+
+    private static CoordinateReferenceSystem MERCATOR;
+
+    /**
+     * TODO: We should generify this. But too much code already depends on JTS,
+     * so for now I hack this
+     */
+    public static final Geometries<Geometry> SIS_GEOMETRY_FACTORY = (Geometries<Geometry>) Geometries.implementation(GeometryLibrary.JTS);
+
+    /**
+     * Created when first required, not thread safe.
+     *
+     * @return WKBReader
+     */
+    public static WKBReader getWKBReader() {
+        WKBReader wkbReader = WKB_READERS.get();
+        if (wkbReader == null) {
+            wkbReader = new WKBReader();
+            WKB_READERS.set(wkbReader);
+        }
+        return wkbReader;
+    }
+
+    /**
+     * Created when first requiered, not thread safe.
+     *
+     * @return WKTReader
+     */
+    public static WKTReader getWKTReader() {
+        WKTReader wktReader = WKT_READERS.get();
+        if (wktReader == null) {
+            wktReader = new WKTReader();
+            WKT_READERS.set(wktReader);
+        }
+        return wktReader;
+    }
+
+    /**
+     * Created when first requiered, not thread safe.
+     *
+     * @return WKBReader
+     */
+    public static WKBWriter getWKBWriter() {
+        WKBWriter candidate = FilterGeometryUtils.WKB_WRITERS.get();
+        if (candidate == null) {
+            candidate = new WKBWriter();
+            FilterGeometryUtils.WKB_WRITERS.set(candidate);
+        }
+        return candidate;
+    }
+
+    /**
+     * Created when first requiered, not thread safe.
+     *
+     * @return WKTReader
+     */
+    public static WKTWriter getWKTWriter() {
+        WKTWriter candidate = FilterGeometryUtils.WKT_WRITERS.get();
+        if (candidate == null) {
+            candidate = new WKTWriter();
+            FilterGeometryUtils.WKT_WRITERS.set(candidate);
+        }
+        return candidate;
+    }
+
+    public static CoordinateReferenceSystem getMercator() throws FactoryException {
+        if (MERCATOR == null) {
+            MERCATOR = CRS.forCode("EPSG:3395");
+        }
+        return MERCATOR;
+    }
+
+    public static Geometry toGeometry(final Object object, Expression exp) {
+        Object value = null;
+        if ((exp instanceof PropertyName) && object instanceof Feature && ((PropertyName) exp).getPropertyName().isEmpty()) {
+            //Search for a default geometry.
+            try {
+                value = ((Feature) object).getPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString());
+            } catch (PropertyNotFoundException ex) {
+                //no defined default geometry
+            }
+        } else {
+            value = exp.evaluate(object);
+        }
+
+        if (value instanceof GeometryWrapper) {
+            value = ((GeometryWrapper) value).geometry;
+        } else if (value instanceof String) {
+            try {
+                //try to convert from WKT
+                value = getWKTReader().read(value.toString());
+            } catch (ParseException ex) {
+                //we have try
+            }
+        } else if (value instanceof byte[]) {
+            try {
+                //try to convert from WKB
+                value = getWKBReader().read((byte[]) value);
+            } catch (ParseException ex) {
+                //we have try
+            }
+        }
+
+        Geometry candidate;
+        if (value instanceof GridCoverage) {
+            //use the coverage envelope
+            final GridCoverage coverage = (GridCoverage) value;
+            candidate = SIS_GEOMETRY_FACTORY.tryConvertToGeometry(coverage.getGridGeometry().getEnvelope(), WrapResolution.SPLIT);
+        } else {
+            try {
+                candidate = ObjectConverters.convert(value, Geometry.class);
+            } catch (UnconvertibleObjectException ex) {
+                //cound not convert value to a Geometry
+                candidate = null;
+            }
+        }
+        return candidate;
+    }
+
+    /**
+     * Reproject geometries to the same CRS if needed and if possible.
+     */
+    public static Geometry[] toSameCRS(final Geometry leftGeom, final Geometry rightGeom)
+            throws FactoryException, TransformException {
+
+        final CoordinateReferenceSystem leftCRS = Geometries.getCoordinateReferenceSystem(leftGeom);
+        final CoordinateReferenceSystem rightCRS = Geometries.getCoordinateReferenceSystem(rightGeom);
+
+        final CRSMatching.Match match = CRSMatching
+                .left(leftCRS)
+                .right(rightCRS);
+
+        final CRSMatching.Transformer<Geometry> projectGeom = (g, op) -> SIS_GEOMETRY_FACTORY.tryTransform(g, op, null);
+        return new Geometry[]{
+            match.transformLeftToCommon(leftGeom, projectGeom),
+            match.transformRightToCommon(rightGeom, projectGeom)
+        };
+    }
+
+    /**
+     * Reproject one or both geometries to the same crs, the matching crs will
+     * be compatible with the requested unit. return Array[leftGeometry,
+     * rightGeometry, matchingCRS];
+     */
+    public static Object[] toSameCRS(final Geometry leftGeom, final Geometry rightGeom, final Unit unit)
+            throws NoSuchAuthorityCodeException, FactoryException, TransformException {
+
+        final CoordinateReferenceSystem leftCRS = Geometries.getCoordinateReferenceSystem(leftGeom);
+        final CoordinateReferenceSystem rightCRS = Geometries.getCoordinateReferenceSystem(rightGeom);
+
+        if (leftCRS == null && rightCRS == null) {
+            //bother geometries doesn't have a defined SRID, we assume that both
+            //are in the same CRS
+            return new Object[]{leftGeom, rightGeom, null};
+        } else if (leftCRS == null || rightCRS == null || Utilities.equalsIgnoreMetadata(leftCRS, rightCRS)) {
+            //both are in the same CRS
+
+            final CoordinateReferenceSystem geomCRS = (leftCRS == null) ? rightCRS : leftCRS;
+
+            if (geomCRS.getCoordinateSystem().getAxis(0).getUnit().isCompatible(unit)) {
+                //the geometries crs is compatible with the requested unit, nothing to reproject
+                return new Object[]{leftGeom, rightGeom, geomCRS};
+            } else {
+                //the crs unit is not compatible, we must reproject both geometries to a more appropriate crs
+                if (Units.METRE.isCompatible(unit)) {
+                    //in that case we reproject to mercator EPSG:3395
+                    final CoordinateReferenceSystem mercator = getMercator();
+                    final CoordinateOperation trs = CRS.findOperation(geomCRS, mercator, null);
+
+                    return new Object[]{
+                        Geometries.transform(leftGeom, trs),
+                        Geometries.transform(rightGeom, trs),
+                        mercator};
+
+                } else {
+                    //we can not find a matching projection in this case
+                    throw new TransformException("Could not find a matching CRS for both geometries for unit :" + unit);
+                }
+            }
+
+        } else {
+            //both have different CRS, try to find the most appropriate crs amoung both
+
+            final CoordinateReferenceSystem matchingCRS;
+            final Object leftMatch;
+            final Object rightMatch;
+
+            if (leftCRS.getCoordinateSystem().getAxis(0).getUnit().isCompatible(unit)) {
+                matchingCRS = leftCRS;
+                final CoordinateOperation trs = CRS.findOperation(rightCRS, matchingCRS, null);
+                rightMatch = Geometries.transform(rightGeom, trs);
+                leftMatch = leftGeom;
+            } else if (rightCRS.getCoordinateSystem().getAxis(0).getUnit().isCompatible(unit)) {
+                matchingCRS = rightCRS;
+                final CoordinateOperation trs = CRS.findOperation(leftCRS, matchingCRS, null);
+                leftMatch = Geometries.transform(leftGeom, trs);
+                rightMatch = rightGeom;
+            } else {
+                //the crs unit is not compatible, we must reproject both geometries to a more appropriate crs
+                if (Units.METRE.isCompatible(unit)) {
+                    //in that case we reproject to mercator EPSG:3395
+                    matchingCRS = getMercator();
+
+                    CoordinateOperation trs = CRS.findOperation(leftCRS, matchingCRS, null);
+                    leftMatch = Geometries.transform(leftGeom, trs);
+                    trs = CRS.findOperation(rightCRS, matchingCRS, null);
+                    rightMatch = Geometries.transform(rightGeom, trs);
+
+                } else {
+                    //we can not find a matching projection in this case
+                    throw new TransformException("Could not find a matching CRS for both geometries for unit :" + unit);
+                }
+            }
+
+            return new Object[]{leftMatch, rightMatch, matchingCRS};
+        }
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractAccessorSpatialFunction.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractAccessorSpatialFunction.java
new file mode 100644
index 0000000..202f66d
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractAccessorSpatialFunction.java
@@ -0,0 +1,64 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * Spatial methods accessing properties of a geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since 2.0
+ * @module
+ */
+public abstract class AbstractAccessorSpatialFunction<T extends Geometry> extends AbstractSpatialFunction {
+
+    public AbstractAccessorSpatialFunction(Expression[] parameters) {
+        super(parameters);
+    }
+
+    protected abstract Class<T> getExpectedClass();
+
+    @Override
+    public Object evaluate(Object candidate) {
+        Geometry geometry = FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+
+        if (!getExpectedClass().isInstance(geometry)) {
+            return null;
+        }
+
+        final Object[] params = new Object[parameters.size()];
+        params[0] = geometry;
+        for (int i = 1, n = parameters.size(); i < n; i++) {
+            params[i] = parameters.get(i).evaluate(candidate);
+        }
+
+        try {
+            return execute((T) geometry, params);
+        } catch (ParseException ex) {
+            warning(ex);
+        }
+        return null;
+    }
+
+    public abstract Object execute(T geom, Object... params) throws ParseException;
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractBinarySpatialFunction.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractBinarySpatialFunction.java
new file mode 100644
index 0000000..6ee163e
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractBinarySpatialFunction.java
@@ -0,0 +1,85 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
+import org.opengis.filter.expression.Expression;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+/**
+ * Spatial methods involving two geometries.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since 2.0
+ * @module
+ */
+public abstract class AbstractBinarySpatialFunction extends AbstractSpatialFunction {
+
+    public AbstractBinarySpatialFunction(Expression[] parameters) {
+        super(parameters);
+    }
+
+    @Override
+    protected int getMinParams() {
+        return 2;
+    }
+
+    @Override
+    protected int getMaxParams() {
+        return 2;
+    }
+
+    @Override
+    public Object evaluate(Object candidate) {
+        Geometry left = FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+        Geometry right = FilterGeometryUtils.toGeometry(candidate, parameters.get(1));
+        try {
+            final Geometry[] geoms = FilterGeometryUtils.toSameCRS(left, right);
+            left = geoms[0];
+            right = geoms[1];
+        } catch (FactoryException ex) {
+            return null;
+        } catch (TransformException ex) {
+            return null;
+        }
+
+        if (left == null || right == null) {
+            return null;
+        }
+
+        final Object[] params = new Object[parameters.size()];
+        params[0] = left;
+        params[1] = right;
+        for (int i = 2, n = parameters.size(); i < n; i++) {
+            params[i] = parameters.get(i).evaluate(candidate);
+        }
+
+        try {
+            return execute(left, right);
+        } catch (ParseException ex) {
+            warning(ex);
+        }
+        return null;
+    }
+
+    public abstract Object execute(Geometry left, Geometry right, Object... params) throws ParseException;
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractGeomConstructor.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractGeomConstructor.java
new file mode 100644
index 0000000..dec365d
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractGeomConstructor.java
@@ -0,0 +1,78 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
+import org.locationtech.jts.geom.Geometry;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since 2.0
+ * @module
+ */
+public abstract class AbstractGeomConstructor extends AbstractSpatialFunction {
+
+    public AbstractGeomConstructor(Expression[] parameters) {
+        super(parameters);
+    }
+
+    @Override
+    protected int getMinParams() {
+        return 1;
+    }
+
+    @Override
+    protected int getMaxParams() {
+        return 2;
+    }
+
+    protected abstract Class getExpectedClass();
+
+    public String getSyntax() {
+        return getName() + "( wkt [,srid] ) or " + getName() + "( wkb [,srid] )";
+    }
+
+    @Override
+    public Object evaluate(Object candidate) {
+        final Geometry geom = FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+
+        if (parameters.size() == 2) {
+            geom.setSRID(((Number) parameters.get(1).evaluate(candidate)).intValue());
+        } else if (parameters.size() > 2) {
+            warning(new Exception("Unexpected number of arguments : " + parameters.size()));
+            return null;
+        }
+
+        if (!getExpectedClass().isInstance(geom)) {
+            warning(new Exception("Geometry is not of expected type : " + getExpectedClass().getSimpleName()));
+            return null;
+        }
+
+        return geom;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(getExpectedClass()).setName(getName());
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractSpatialFunction.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractSpatialFunction.java
new file mode 100644
index 0000000..daa3bad
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/AbstractSpatialFunction.java
@@ -0,0 +1,113 @@
+/*
+ * 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.filter.sqlmm;
+
+import java.util.Map;
+import org.apache.sis.feature.Features;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.internal.feature.AttributeConvention;
+import org.apache.sis.internal.feature.FeatureExpression;
+import org.apache.sis.internal.feature.jts.JTS;
+import org.apache.sis.internal.filter.NamedFunction;
+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.referencing.crs.CoordinateReferenceSystem;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since 2.0
+ * @module
+ */
+public abstract class AbstractSpatialFunction extends NamedFunction implements FeatureExpression {
+
+    public AbstractSpatialFunction(Expression[] parameters) {
+        super(parameters);
+    }
+
+    protected int getMinParams() {
+        return 1;
+    }
+
+    protected int getMaxParams() {
+        return 1;
+    }
+
+    public String getSyntax() {
+        final int minparams = getMinParams();
+        final int maxparams = getMaxParams();
+        final StringBuilder sb = new StringBuilder();
+        sb.append(getName());
+        sb.append('(');
+        for (int i = 0; i < minparams; i++) {
+            if (i != 0) {
+                sb.append(", ");
+            }
+            sb.append("param");
+            sb.append(i + 1);
+        }
+        if (minparams != maxparams) {
+            sb.append('[');
+            for (int i = minparams; i < maxparams; i++) {
+                if (i != 0) {
+                    sb.append(", ");
+                }
+                sb.append("param");
+                sb.append(i + 1);
+            }
+            sb.append(']');
+        }
+        sb.append(')');
+
+        return sb.toString();
+    }
+
+    protected static CoordinateReferenceSystem expectedCrs(FeatureType type, Expression exp) {
+
+        final PropertyType expressionType = FeatureExpression.expectedType(exp, type, new FeatureTypeBuilder()).build();
+        final AttributeType<?> attr = Features.castOrUnwrap(expressionType).orElse(null);
+        if (attr == null) {
+            return null;
+        }
+
+        final AttributeType<CoordinateReferenceSystem> crsCharacteristic = (AttributeType<CoordinateReferenceSystem>)
+                attr.characteristics().get(AttributeConvention.CRS_CHARACTERISTIC);
+        return crsCharacteristic == null ? null : crsCharacteristic.getDefaultValue();
+    }
+
+    protected static void copyCrs(Geometry source, Geometry target) {
+        if (source == null || target == null) return;
+
+        final Object userData = source.getUserData();
+        CoordinateReferenceSystem crs = null;
+        if (userData instanceof CoordinateReferenceSystem) {
+            crs = (CoordinateReferenceSystem) userData;
+        } else if (userData instanceof Map<?,?>) {
+            final Map<?,?> map = (Map<?,?>) userData;
+            final Object value = map.get(JTS.CRS_KEY);
+            if (value instanceof CoordinateReferenceSystem) {
+                crs = (CoordinateReferenceSystem) value;
+            }
+        }
+        target.setUserData(crs);
+        target.setSRID(source.getSRID());
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/SQLMM.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/SQLMM.java
index 171506c..ed21ced 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/SQLMM.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/SQLMM.java
@@ -24,7 +24,6 @@ import org.opengis.util.FactoryException;
 import org.apache.sis.internal.feature.FunctionRegister;
 import org.apache.sis.internal.feature.Resources;
 import org.apache.sis.util.ArgumentChecks;
-import org.locationtech.jts.geom.GeometryFactory;
 
 
 /**
@@ -42,11 +41,6 @@ import org.locationtech.jts.geom.GeometryFactory;
 public final class SQLMM implements FunctionRegister {
 
     /**
-     * JTS factory used by multiple functions.
-     */
-    static final GeometryFactory GF = new GeometryFactory();
-
-    /**
      * Creates the default register.
      */
     public SQLMM() {
@@ -66,14 +60,101 @@ public final class SQLMM implements FunctionRegister {
     @Override
     public Collection<String> getNames() {
         return Arrays.asList(
+                ST_Area.NAME,
+                ST_AsBinary.NAME,
+                ST_AsGML.NAME,
+                ST_AsText.NAME,
+                ST_Boundary.NAME,
                 ST_Buffer.NAME,
                 ST_Centroid.NAME,
+                ST_Contains.NAME,
+                ST_ConvexHull.NAME,
+                ST_CoordDim.NAME,
+                ST_Crosses.NAME,
+                ST_Difference.NAME,
+                ST_Dimension.NAME,
+                ST_Disjoint.NAME,
+                ST_Distance.NAME,
+                ST_EndPoint.NAME,
                 ST_Envelope.NAME,
-                ST_Point.NAME,
+                ST_Equals.NAME,
+                ST_ExplicitPoint.NAME,
+                ST_ExteriorRing.NAME,
+                ST_GeomCollection.NAME,
+                ST_GeometryN.NAME,
+                ST_GeometryType.NAME,
+                ST_InteriorRingN.NAME,
+                ST_Intersection.NAME,
+                ST_Intersects.NAME,
+                ST_Is3D.NAME,
+                ST_IsClosed.NAME,
+                ST_IsEmpty.NAME,
+                ST_IsRing.NAME,
+                ST_IsSimple.NAME,
+                ST_IsValid.NAME,
+                ST_Length.NAME,
                 ST_LineString.NAME,
+                ST_MultiLineString.NAME,
+                ST_MultiPoint.NAME,
+                ST_MultiPolygon.NAME,
+                ST_NumGeometries.NAME,
+                ST_NumInteriorRings.NAME,
+                ST_NumPoints.NAME,
+                ST_Overlaps.NAME,
+                ST_Perimeter.NAME,
+                ST_Point.NAME,
+                ST_PointN.NAME,
+                ST_PointOnSurface.NAME,
+                ST_Polygon.NAME,
+                ST_Relate.NAME,
                 ST_Simplify.NAME,
                 ST_SimplifyPreserveTopology.NAME,
-                ST_Transform.NAME);
+                ST_SRID.NAME,
+                ST_StartPoint.NAME,
+                ST_SymDifference.NAME,
+                ST_ToGeomColl.NAME,
+                ST_ToLineString.NAME,
+                ST_ToMultiLine.NAME,
+                ST_ToMultiPoint.NAME,
+                ST_ToMultiPolygon.NAME,
+                ST_ToPoint.NAME,
+                ST_ToPolygon.NAME,
+                ST_Touches.NAME,
+                ST_Transform.NAME,
+                ST_Union.NAME,
+                ST_Within.NAME,
+                ST_X.NAME,
+                ST_XFromBinary.BdMPoly.NAME,
+                ST_XFromBinary.BdPoly.NAME,
+                ST_XFromBinary.GeomColl.NAME,
+                ST_XFromBinary.Geom.NAME,
+                ST_XFromBinary.Line.NAME,
+                ST_XFromBinary.MLine.NAME,
+                ST_XFromBinary.MPoint.NAME,
+                ST_XFromBinary.MPoly.NAME,
+                ST_XFromBinary.Point.NAME,
+                ST_XFromBinary.Poly.NAME,
+                ST_XFromGML.GeomColl.NAME,
+                ST_XFromGML.Geom.NAME,
+                ST_XFromGML.Line.NAME,
+                ST_XFromGML.MLine.NAME,
+                ST_XFromGML.MPoint.NAME,
+                ST_XFromGML.MPoly.NAME,
+                ST_XFromGML.Point.NAME,
+                ST_XFromGML.Poly.NAME,
+                ST_XFromText.BdMPoly.NAME,
+                ST_XFromText.BdPoly.NAME,
+                ST_XFromText.GeomColl.NAME,
+                ST_XFromText.Geom.NAME,
+                ST_XFromText.Line.NAME,
+                ST_XFromText.MLine.NAME,
+                ST_XFromText.MPoint.NAME,
+                ST_XFromText.MPoly.NAME,
+                ST_XFromText.Point.NAME,
+                ST_XFromText.Poly.NAME,
+                ST_Y.NAME,
+                ST_Z.NAME
+        );
     }
 
     /**
@@ -94,14 +175,100 @@ public final class SQLMM implements FunctionRegister {
         }
         try {
             switch (name) {
+                case ST_Area.NAME:                      return new ST_Area(parameters);
+                case ST_AsBinary.NAME:                  return new ST_AsBinary(parameters);
+                case ST_AsGML.NAME:                     return new ST_AsGML(parameters);
+                case ST_AsText.NAME:                    return new ST_AsText(parameters);
+                case ST_Boundary.NAME:                  return new ST_Boundary(parameters);
                 case ST_Buffer.NAME:                    return new ST_Buffer(parameters);
                 case ST_Centroid.NAME:                  return new ST_Centroid(parameters);
+                case ST_Contains.NAME:                  return new ST_Contains(parameters);
+                case ST_ConvexHull.NAME:                return new ST_ConvexHull(parameters);
+                case ST_CoordDim.NAME:                  return new ST_CoordDim(parameters);
+                case ST_Crosses.NAME:                   return new ST_Crosses(parameters);
+                case ST_Difference.NAME:                return new ST_Difference(parameters);
+                case ST_Dimension.NAME:                 return new ST_Dimension(parameters);
+                case ST_Disjoint.NAME:                  return new ST_Disjoint(parameters);
+                case ST_Distance.NAME:                  return new ST_Distance(parameters);
+                case ST_EndPoint.NAME:                  return new ST_EndPoint(parameters);
                 case ST_Envelope.NAME:                  return new ST_Envelope(parameters);
-                case ST_Point.NAME:                     return new ST_Point(parameters);
+                case ST_Equals.NAME:                    return new ST_Equals(parameters);
+                case ST_ExplicitPoint.NAME:             return new ST_ExplicitPoint(parameters);
+                case ST_ExteriorRing.NAME:              return new ST_ExteriorRing(parameters);
+                case ST_GeomCollection.NAME:            return new ST_GeomCollection(parameters);
+                case ST_GeometryN.NAME:                 return new ST_GeometryN(parameters);
+                case ST_GeometryType.NAME:              return new ST_GeometryType(parameters);
+                case ST_InteriorRingN.NAME:             return new ST_InteriorRingN(parameters);
+                case ST_Intersection.NAME:              return new ST_Intersection(parameters);
+                case ST_Intersects.NAME:                return new ST_Intersects(parameters);
+                case ST_Is3D.NAME:                      return new ST_Is3D(parameters);
+                case ST_IsClosed.NAME:                  return new ST_IsClosed(parameters);
+                case ST_IsEmpty.NAME:                   return new ST_IsEmpty(parameters);
+                case ST_IsRing.NAME:                    return new ST_IsRing(parameters);
+                case ST_IsSimple.NAME:                  return new ST_IsSimple(parameters);
+                case ST_IsValid.NAME:                   return new ST_IsValid(parameters);
+                case ST_Length.NAME:                    return new ST_Length(parameters);
                 case ST_LineString.NAME:                return new ST_LineString(parameters);
+                case ST_MultiLineString.NAME:           return new ST_MultiLineString(parameters);
+                case ST_MultiPoint.NAME:                return new ST_MultiPoint(parameters);
+                case ST_MultiPolygon.NAME:              return new ST_MultiPolygon(parameters);
+                case ST_NumGeometries.NAME:             return new ST_NumGeometries(parameters);
+                case ST_NumInteriorRings.NAME:          return new ST_NumInteriorRings(parameters);
+                case ST_NumPoints.NAME:                 return new ST_NumPoints(parameters);
+                case ST_Overlaps.NAME:                  return new ST_Overlaps(parameters);
+                case ST_Perimeter.NAME:                 return new ST_Perimeter(parameters);
+                case ST_Point.NAME:                     return new ST_Point(parameters);
+                case ST_PointN.NAME:                    return new ST_PointN(parameters);
+                case ST_PointOnSurface.NAME:            return new ST_PointOnSurface(parameters);
+                case ST_Polygon.NAME:                   return new ST_Polygon(parameters);
+                case ST_Relate.NAME:                    return new ST_Relate(parameters);
                 case ST_Simplify.NAME:                  return new ST_Simplify(parameters);
                 case ST_SimplifyPreserveTopology.NAME:  return new ST_SimplifyPreserveTopology(parameters);
+                case ST_SRID.NAME:                      return new ST_SRID(parameters);
+                case ST_StartPoint.NAME:                return new ST_StartPoint(parameters);
+                case ST_SymDifference.NAME:             return new ST_SymDifference(parameters);
+                case ST_ToGeomColl.NAME:                return new ST_ToGeomColl(parameters);
+                case ST_ToLineString.NAME:              return new ST_ToLineString(parameters);
+                case ST_ToMultiLine.NAME:               return new ST_ToMultiLine(parameters);
+                case ST_ToMultiPoint.NAME:              return new ST_ToMultiPoint(parameters);
+                case ST_ToMultiPolygon.NAME:            return new ST_ToMultiPolygon(parameters);
+                case ST_ToPoint.NAME:                   return new ST_ToPoint(parameters);
+                case ST_ToPolygon.NAME:                 return new ST_ToPolygon(parameters);
+                case ST_Touches.NAME:                   return new ST_Touches(parameters);
                 case ST_Transform.NAME:                 return new ST_Transform(parameters);
+                case ST_Union.NAME:                     return new ST_Union(parameters);
+                case ST_Within.NAME:                    return new ST_Within(parameters);
+                case ST_X.NAME:                         return new ST_X(parameters);
+                case ST_XFromBinary.BdMPoly.NAME:       return new ST_XFromBinary.BdMPoly(parameters);
+                case ST_XFromBinary.BdPoly.NAME:        return new ST_XFromBinary.BdPoly(parameters);
+                case ST_XFromBinary.GeomColl.NAME:      return new ST_XFromBinary.GeomColl(parameters);
+                case ST_XFromBinary.Geom.NAME:          return new ST_XFromBinary.Geom(parameters);
+                case ST_XFromBinary.Line.NAME:          return new ST_XFromBinary.Line(parameters);
+                case ST_XFromBinary.MLine.NAME:         return new ST_XFromBinary.MLine(parameters);
+                case ST_XFromBinary.MPoint.NAME:        return new ST_XFromBinary.MPoint(parameters);
+                case ST_XFromBinary.MPoly.NAME:         return new ST_XFromBinary.MPoly(parameters);
+                case ST_XFromBinary.Point.NAME:         return new ST_XFromBinary.Point(parameters);
+                case ST_XFromBinary.Poly.NAME:          return new ST_XFromBinary.Poly(parameters);
+                case ST_XFromGML.GeomColl.NAME:         return new ST_XFromGML.GeomColl(parameters);
+                case ST_XFromGML.Geom.NAME:             return new ST_XFromGML.Geom(parameters);
+                case ST_XFromGML.Line.NAME:             return new ST_XFromGML.Line(parameters);
+                case ST_XFromGML.MLine.NAME:            return new ST_XFromGML.MLine(parameters);
+                case ST_XFromGML.MPoint.NAME:           return new ST_XFromGML.MPoint(parameters);
+                case ST_XFromGML.MPoly.NAME:            return new ST_XFromGML.MPoly(parameters);
+                case ST_XFromGML.Point.NAME:            return new ST_XFromGML.Point(parameters);
+                case ST_XFromGML.Poly.NAME:             return new ST_XFromGML.Poly(parameters);
+                case ST_XFromText.BdMPoly.NAME:         return new ST_XFromText.BdMPoly(parameters);
+                case ST_XFromText.BdPoly.NAME:          return new ST_XFromText.BdPoly(parameters);
+                case ST_XFromText.GeomColl.NAME:        return new ST_XFromText.GeomColl(parameters);
+                case ST_XFromText.Geom.NAME:            return new ST_XFromText.Geom(parameters);
+                case ST_XFromText.Line.NAME:            return new ST_XFromText.Line(parameters);
+                case ST_XFromText.MLine.NAME:           return new ST_XFromText.MLine(parameters);
+                case ST_XFromText.MPoint.NAME:          return new ST_XFromText.MPoint(parameters);
+                case ST_XFromText.MPoly.NAME:           return new ST_XFromText.MPoly(parameters);
+                case ST_XFromText.Point.NAME:           return new ST_XFromText.Point(parameters);
+                case ST_XFromText.Poly.NAME:            return new ST_XFromText.Poly(parameters);
+                case ST_Y.NAME:                         return new ST_Y(parameters);
+                case ST_Z.NAME:                         return new ST_Z(parameters);
                 default: throw new IllegalArgumentException(Resources.format(Resources.Keys.UnknownFunction_1, name));
             }
         } catch (FactoryException e) {
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Area.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Area.java
new file mode 100644
index 0000000..8ef34c9
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Area.java
@@ -0,0 +1,69 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Area. <br>
+ * Return the area measurement of an ST_Surface value, ignoring z and m coordinate values in the calculations.
+ *
+ * This class should be for all surface types, but JTS only has Polygon and MultiPolygon.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Area extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 8662819270100042027L;
+
+    public static final String NAME = "ST_Area";
+
+    public ST_Area(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return geom.getArea();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Double.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_AsBinary.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_AsBinary.java
new file mode 100644
index 0000000..b1aec8d
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_AsBinary.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.sis.internal.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_AsBinary. <br>
+ * Return the well-known binary representation of an ST_Geometry value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_AsBinary extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 543784193712423026L;
+
+    public static final String NAME = "ST_AsBinary";
+
+    public ST_AsBinary(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return FilterGeometryUtils.getWKBWriter().write(geom);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(byte[].class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_AsGML.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_AsGML.java
new file mode 100644
index 0000000..33ff181
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_AsGML.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.sis.internal.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_AsGML. <br>
+ * Return the GML representation of an ST_Geometry value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_AsGML extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -2338915326478992665L;
+
+    public static final String NAME = "ST_AsGML";
+
+    public ST_AsGML(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        //TODO marshall to GML 3.1.1
+        throw new ParseException("Not implemented", 0);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(String.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_AsText.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_AsText.java
new file mode 100644
index 0000000..de4279d
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_AsText.java
@@ -0,0 +1,70 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.io.WKTWriter;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_AsText. <br>
+ * Return the well-known text representation of an ST_Geometry value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_AsText extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 8629461252459501038L;
+
+    public static final String NAME = "ST_AsText";
+
+    public ST_AsText(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        final WKTWriter writer = FilterGeometryUtils.getWKTWriter();
+        return writer.write(geom);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(String.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Boundary.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Boundary.java
new file mode 100644
index 0000000..7e0951b
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Boundary.java
@@ -0,0 +1,72 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Boundary. <br>
+ * Return the boundary of the ST_Geometry value, ignoring z and m coordinate values in the calculations and
+ * not including them in the resultant geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Boundary extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 6907443570851622576L;
+
+    public static final String NAME = "ST_Boundary";
+
+    public ST_Boundary(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        final Geometry bound = geom.getBoundary();
+        copyCrs(geom, bound);
+        return bound;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Geometry.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Buffer.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Buffer.java
index b84c18b..ec6a1f5 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Buffer.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Buffer.java
@@ -28,8 +28,17 @@ import org.opengis.filter.expression.Expression;
 
 
 /**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Buffer. <br>
+ * <p>
+ * Return the ST_Geometry value that represents all points whose distance from any point of an ST_Geometry
+ * value is less than or equal to a specified distance, ignoring z and m coordinate values in the calculations and
+ * not including them in the resultant geometry.
+ * </p>
+ *
+ * <p>
  * An expression which computes a geometry buffer.
  * This expression expects two arguments:
+ * </p>
  *
  * <ol class="verbose">
  *   <li>An expression returning a geometry object. The evaluated value shall be an instance of
@@ -61,7 +70,7 @@ final class ST_Buffer extends NamedFunction implements FeatureExpression {
      *
      * @throws IllegalArgumentException if the number of arguments is not equal to 2.
      */
-    ST_Buffer(final Expression[] parameters) {
+    ST_Buffer(final Expression... parameters) {
         super(parameters);
         ArgumentChecks.ensureExpectedCount("parameters", 2, parameters.length);
     }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Centroid.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Centroid.java
index 525b04f..35d4419 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Centroid.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Centroid.java
@@ -28,8 +28,20 @@ import org.opengis.filter.expression.Expression;
 
 
 /**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Centroid. <br>
+ * <p>
+ * Return the 2D ST_Point value that is the mathematical centroid of the ST_Surface value, ignoring z and m
+ * coordinate values in the calculations and not including them in the resultant geometry.
+ * </p>
+ *
+ * <p>
+ * This class should be for all surface types, but JTS only has Polygon and MultiPolygon.
+ * </p>
+ *
+ * <p>
  * An expression which computes the centroid of a geometry.
  * This expression expects one argument:
+ * </p>
  *
  * <ol class="verbose">
  *   <li>An expression returning a geometry object. The evaluated value shall be an instance of
@@ -59,7 +71,7 @@ final class ST_Centroid extends NamedFunction implements FeatureExpression {
      *
      * @throws IllegalArgumentException if the number of arguments is not equal to 1.
      */
-    ST_Centroid(final Expression[] parameters) {
+    ST_Centroid(final Expression... parameters) {
         super(parameters);
         ArgumentChecks.ensureExpectedCount("parameters", 1, parameters.length);
     }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Contains.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Contains.java
new file mode 100644
index 0000000..c090ffa
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Contains.java
@@ -0,0 +1,63 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Contains. <br>
+ * Test if an ST_Geometry value spatially 2D contains another ST_Geometry value, ignoring z and m coordinate
+ * values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Contains extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -7225934838878061249L;
+
+    public static final String NAME = "ST_Contains";
+
+    public ST_Contains(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        return left.contains(right);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ConvexHull.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ConvexHull.java
new file mode 100644
index 0000000..c151271
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ConvexHull.java
@@ -0,0 +1,72 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ConvexHull. <br>
+ * Return the convex hull of the ST_Geometry value, ignoring z and m coordinate values in the calculations and
+ * not including them in the resultant geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ConvexHull extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -2523224117411417556L;
+
+    public static final String NAME = "ST_ConvexHull";
+
+    public ST_ConvexHull(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        final Geometry res = geom.convexHull();
+        copyCrs(geom, res);
+        return res;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Geometry.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_CoordDim.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_CoordDim.java
new file mode 100644
index 0000000..4f678ce
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_CoordDim.java
@@ -0,0 +1,72 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_CoordDim. <br>
+ * Return the coordinate dimension of the ST_Geometry value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since 2.0
+ * @module
+ */
+final class ST_CoordDim extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 4730101640446055320L;
+
+    public static final String NAME = "ST_CoordDim";
+
+    public ST_CoordDim(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        final Coordinate c = geom.getCoordinate();
+        if (c != null) {
+            return Double.isNaN(c.z) ? 2 : 3;
+        }
+        return null;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Integer.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Crosses.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Crosses.java
new file mode 100644
index 0000000..e289719
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Crosses.java
@@ -0,0 +1,63 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Crosses. <br>
+ * Test if an ST_Geometry value spatially 2D crosses another ST_Geometry value, ignoring z and m coordinate
+ * values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Crosses extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -3578826539931205291L;
+
+    public static final String NAME = "ST_Crosses";
+
+    public ST_Crosses(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        return left.crosses(right);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Difference.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Difference.java
new file mode 100644
index 0000000..f803801
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Difference.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Difference. <br>
+ * Return an ST_Geometry value that represents the point set difference of two ST_Geometry values, ignoring z
+ * and m coordinate values in the calculations and not including them in the resultant geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Difference extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -6949293332862547418L;
+
+    public static final String NAME = "ST_Difference";
+
+    public ST_Difference(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        final Geometry res = left.difference(right);
+        copyCrs(left, res);
+        return res;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Geometry.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Dimension.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Dimension.java
new file mode 100644
index 0000000..9213307
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Dimension.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Dimension. <br>
+ * Return the dimension of the ST_Geometry value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Dimension extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4084734402948897229L;
+
+    public static final String NAME = "ST_Dimension";
+
+    public ST_Dimension(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return geom.getDimension();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Integer.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Disjoint.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Disjoint.java
new file mode 100644
index 0000000..44c1e47
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Disjoint.java
@@ -0,0 +1,63 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Disjoint. <br>
+ * Test if an ST_Geometry value is spatially 2D disjoint from another ST_Geometry value, ignoring z and m
+ * coordinate values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+public final class ST_Disjoint extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -878251746247560050L;
+
+    public static final String NAME = "ST_Disjoint";
+
+    public ST_Disjoint(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        return left.disjoint(right);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Distance.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Distance.java
new file mode 100644
index 0000000..94de72c
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Distance.java
@@ -0,0 +1,63 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Distance. <br>
+ * Return the distance between two geometry values, ignoring z and m coordinate values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Distance extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 6484941686987546409L;
+
+    public static final String NAME = "ST_Distance";
+
+    public ST_Distance(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        final double d = left.distance(right);
+        return d;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Double.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_EndPoint.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_EndPoint.java
new file mode 100644
index 0000000..60d3127
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_EndPoint.java
@@ -0,0 +1,73 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.Point;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_EndPoint. <br>
+ * Return an ST_Point value that is the end point of an ST_Curve value including existing z and m coordinate
+ * values in the resultant geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_EndPoint extends AbstractAccessorSpatialFunction<LineString> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 3005045080078912918L;
+
+    public static final String NAME = "ST_EndPoint";
+
+    public ST_EndPoint(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<LineString> getExpectedClass() {
+        return LineString.class;
+    }
+
+    @Override
+    public Object execute(LineString geom, Object... params) throws ParseException {
+        final Point pt = geom.getEndPoint();
+        copyCrs(geom, pt);
+        return pt;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Point.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Envelope.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Envelope.java
index d8a34d6..d4d4e08 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Envelope.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Envelope.java
@@ -52,11 +52,15 @@ import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
  * @module
  */
 public class ST_Envelope extends NamedFunction implements FeatureExpression {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 677890922998031096L;
 
     public static final String NAME = "ST_Envelope";
 
     private final Worker worker;
-    public ST_Envelope(Expression[] parameters) {
+    public ST_Envelope(Expression... parameters) {
         super(parameters);
         if (parameters == null || parameters.length != 1) throw new MismatchedDimensionException(
                 String.format(
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Equals.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Equals.java
new file mode 100644
index 0000000..75f9c50
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Equals.java
@@ -0,0 +1,63 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Equals. <br>
+ * Test if an ST_Geometry value is spatially 2D equal to another ST_Geometry value, ignoring z and m
+ * coordinate values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Equals extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 7173751615288222316L;
+
+    public static final String NAME = "ST_Equals";
+
+    public ST_Equals(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        return left.equals(right);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ExplicitPoint.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ExplicitPoint.java
new file mode 100644
index 0000000..b18e9c8
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ExplicitPoint.java
@@ -0,0 +1,75 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Point;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ExplicitPoint. <br>
+ * Return the coordinate values as a DOUBLE PRECISION LIST value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ExplicitPoint extends AbstractAccessorSpatialFunction<Point> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -519090616041839273L;
+
+    public static final String NAME = "ST_ExplicitPoint";
+
+    public ST_ExplicitPoint(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Point> getExpectedClass() {
+        return Point.class;
+    }
+
+    @Override
+    public Object execute(Point geom, Object... params) throws ParseException {
+        final Coordinate coord = geom.getCoordinate();
+        final List<Double> values = new ArrayList<Double>(3);
+        values.add(coord.x);
+        values.add(coord.y);
+        values.add(coord.z);
+        return values;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Double.class).setMinimumOccurs(0).setMaximumOccurs(Integer.MAX_VALUE).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ExteriorRing.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ExteriorRing.java
new file mode 100644
index 0000000..d677f6c
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ExteriorRing.java
@@ -0,0 +1,71 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.Polygon;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ExteriorRing. <br>
+ * Observe the ST_PrivateExteriorRing attribute of an ST_CurvePolygon value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ExteriorRing extends AbstractAccessorSpatialFunction<Polygon> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4351063096217766472L;
+
+    public static final String NAME = "ST_ExteriorRing";
+
+    public ST_ExteriorRing(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Polygon> getExpectedClass() {
+        return Polygon.class;
+    }
+
+    @Override
+    public Object execute(Polygon geom, Object... params) throws ParseException {
+        final LineString ring = geom.getExteriorRing();
+        copyCrs(geom, ring);
+        return ring;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(LineString.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_GeomCollection.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_GeomCollection.java
new file mode 100644
index 0000000..0105f44
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_GeomCollection.java
@@ -0,0 +1,52 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.GeometryCollection;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_GeomCollection. <br>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since 2.0
+ * @module
+ */
+final class ST_GeomCollection extends AbstractGeomConstructor {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -6639181263539125735L;
+
+    public static final String NAME = "ST_GeomCollection";
+
+    public ST_GeomCollection(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<GeometryCollection> getExpectedClass() {
+        return GeometryCollection.class;
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_GeometryN.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_GeometryN.java
new file mode 100644
index 0000000..05639dd
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_GeometryN.java
@@ -0,0 +1,83 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_GeometryN. <br>
+ * Return the specified ST_Geometry value in the ST_PrivateGeometries attribute of an ST_GeomCollection
+ * value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_GeometryN extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4368199731276946357L;
+
+    public static final String NAME = "ST_GeometryN";
+
+    public ST_GeometryN(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected int getMinParams() {
+        return 2;
+    }
+
+    @Override
+    protected int getMaxParams() {
+        return 2;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        final int index = Integer.parseInt(params[1].toString());
+        final Geometry sub = geom.getGeometryN(index-1);
+        copyCrs(geom, sub);
+        return sub;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Geometry.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_GeometryType.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_GeometryType.java
new file mode 100644
index 0000000..6dfd0f1
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_GeometryType.java
@@ -0,0 +1,94 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPoint;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Polygon;
+import java.text.ParseException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_GeometryType. <br>
+ * Returns the geometry type name.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_GeometryType extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 3950622078110541672L;
+
+    public static final String NAME = "ST_GeometryType";
+
+    private static final Map<Class,String> TYPES = new LinkedHashMap<Class, String>();
+    static {
+        TYPES.put(Point.class,              "ST_Point");
+        TYPES.put(LineString.class,         "ST_LineString");
+        TYPES.put(Polygon.class,            "ST_Polygon");
+        TYPES.put(MultiPoint.class,         "ST_MultiPoint");
+        TYPES.put(MultiLineString.class,    "ST_MultiLineString");
+        TYPES.put(MultiPolygon.class,       "ST_MultiPolygon");
+        TYPES.put(GeometryCollection.class, "ST_GeomCollection");
+        TYPES.put(Geometry.class,           "ST_Geometry");
+    }
+
+    public ST_GeometryType(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        for (Entry<Class,String> entry : TYPES.entrySet()) {
+            if (entry.getKey().isInstance(geom)) {
+                return entry.getValue();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(String.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_InteriorRingN.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_InteriorRingN.java
new file mode 100644
index 0000000..a08ebb3
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_InteriorRingN.java
@@ -0,0 +1,72 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.Polygon;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_InteriorRingN. <br>
+ * Return the specified element in the ST_PrivateInteriorRings attribute of an ST_CurvePolygon value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_InteriorRingN extends AbstractAccessorSpatialFunction<Polygon> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -8020999740090022880L;
+
+    public static final String NAME = "ST_InteriorRingN";
+
+    public ST_InteriorRingN(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Polygon> getExpectedClass() {
+        return Polygon.class;
+    }
+
+    @Override
+    public Object execute(Polygon geom, Object... params) throws ParseException {
+        final int index = Integer.parseInt(params[1].toString());
+        final LineString ring = geom.getInteriorRingN(index-1);
+        copyCrs(geom, ring);
+        return ring;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(LineString.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Intersection.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Intersection.java
new file mode 100644
index 0000000..d68eb38
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Intersection.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Intersection. <br>
+ * Return an ST_Geometry value that represents the point set intersection of two ST_Geometry values, ignoring
+ * z and m coordinate values in the calculations and not including them in the resultant geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Intersection extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 2438604585868953457L;
+
+    public static final String NAME = "ST_Intersection";
+
+    public ST_Intersection(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        final Geometry res = left.intersection(right);
+        copyCrs(left, res);
+        return res;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Geometry.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Intersects.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Intersects.java
new file mode 100644
index 0000000..7ebc615
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Intersects.java
@@ -0,0 +1,63 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Intersects. <br>
+ * Test if an ST_Geometry value spatially 2D intersects another ST_Geometry value, ignoring z and m
+ * coordinate values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Intersects extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -8906412891862094166L;
+
+    public static final String NAME = "ST_Intersects";
+
+    public ST_Intersects(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Boolean execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        return left.intersects(right);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Is3D.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Is3D.java
new file mode 100644
index 0000000..658d785
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Is3D.java
@@ -0,0 +1,72 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Is3D. <br>
+ * Test if an ST_Geometry value has z coordinate values.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Is3D extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 1572271391650367796L;
+
+    public static final String NAME = "ST_Is3D";
+
+    public ST_Is3D(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        final Coordinate c = geom.getCoordinate();
+        if (c != null) {
+            return !Double.isNaN(c.z);
+        }
+        return null;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsClosed.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsClosed.java
new file mode 100644
index 0000000..a7e3ce2
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsClosed.java
@@ -0,0 +1,69 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.LineString;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_IsClosed. <br>
+ * Test if an ST_Curve value is closed, ignoring z and m coordinate values in the calculations.
+ *
+ * This class should be for all curve types, but JTS only has LineString.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_IsClosed extends AbstractAccessorSpatialFunction<LineString> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -3036161984738401938L;
+
+    public static final String NAME = "ST_IsClosed";
+
+    public ST_IsClosed(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<LineString> getExpectedClass() {
+        return LineString.class;
+    }
+
+    @Override
+    public Object execute(LineString geom, Object... params) throws ParseException {
+        return geom.isClosed();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsEmpty.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsEmpty.java
new file mode 100644
index 0000000..83d0744
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsEmpty.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_IsEmpty. <br>
+ * Test if an ST_Geometry value corresponds to the empty set.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_IsEmpty extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 2758867462786336526L;
+
+    public static final String NAME = "ST_IsEmpty";
+
+    public ST_IsEmpty(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return geom.isEmpty();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsRing.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsRing.java
new file mode 100644
index 0000000..4c76ec6
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsRing.java
@@ -0,0 +1,69 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.LineString;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_IsRing. <br>
+ * Test if an ST_Curve value is a ring, ignoring z and m coordinate values in the calculations.
+ *
+ * This class should be for all curve types, but JTS only has LineString.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_IsRing extends AbstractAccessorSpatialFunction<LineString> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 7067326611766377264L;
+
+    public static final String NAME = "ST_IsRing";
+
+    public ST_IsRing(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<LineString> getExpectedClass() {
+        return LineString.class;
+    }
+
+    @Override
+    public Object execute(LineString geom, Object... params) throws ParseException {
+        return geom.isRing();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsSimple.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsSimple.java
new file mode 100644
index 0000000..ddd7fee
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsSimple.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.sis.internal.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_IsSimple. <br>
+ * Test if an ST_Geometry value has no anomalous geometric points, such as self intersection, ignoring z
+ * coordinate values in the determination.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_IsSimple extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4153525354330682049L;
+
+    public static final String NAME = "ST_IsSimple";
+
+    public ST_IsSimple(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return geom.isSimple();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsValid.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsValid.java
new file mode 100644
index 0000000..52497a0
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_IsValid.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_IsValid. <br>
+ * Test if an ST_Geometry value is well formed.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_IsValid extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -7619290650312884723L;
+
+    public static final String NAME = "ST_IsValid";
+
+    public ST_IsValid(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return geom.isValid();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Length.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Length.java
new file mode 100644
index 0000000..8ebd793
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Length.java
@@ -0,0 +1,69 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Length. <br>
+ * Return the length measurement of an ST_Curve value, ignoring z and m coordinate values in the calculations.
+ *
+ * This class should be for all curve types, but JTS only has LineString, MultiLineString.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Length extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 3778625608653432659L;
+
+    public static final String NAME = "ST_Length";
+
+    public ST_Length(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return geom.getLength();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Double.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_LineString.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_LineString.java
index c05ab4b..c7bb0a1 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_LineString.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_LineString.java
@@ -26,10 +26,14 @@ import org.apache.sis.feature.builder.FeatureTypeBuilder;
 import org.apache.sis.feature.builder.PropertyTypeBuilder;
 import org.apache.sis.internal.filter.NamedFunction;
 import org.apache.sis.internal.feature.FeatureExpression;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
+import static org.apache.sis.internal.filter.FilterGeometryUtils.getWKBReader;
+import static org.apache.sis.internal.filter.FilterGeometryUtils.getWKTReader;
 import org.apache.sis.referencing.CRS;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.LineString;
 import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.io.ParseException;
 import org.opengis.feature.FeatureType;
 import org.opengis.filter.expression.Expression;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -53,6 +57,10 @@ import org.opengis.util.FactoryException;
  * @module
  */
 final class ST_LineString extends NamedFunction implements FeatureExpression {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 3654052127628404584L;
 
     /**
      * Name of this function as defined by SQL/MM standard.
@@ -67,7 +75,7 @@ final class ST_LineString extends NamedFunction implements FeatureExpression {
      *
      * @throws IllegalArgumentException if the number of arguments is less then one.
      */
-    ST_LineString(final Expression[] parameters) {
+    ST_LineString(final Expression... parameters) {
         super(parameters);
         if (parameters.length < 1) {
             throw new IllegalArgumentException("ST_LineString function expect 2 or more parameters");
@@ -94,30 +102,53 @@ final class ST_LineString extends NamedFunction implements FeatureExpression {
         }
 
         Object x = parameters.get(0).evaluate(object);
-        if (x == null) {
-          x = Collections.EMPTY_LIST;
-        } else if (x instanceof Point) {
-            x = Arrays.asList(x);
-        } else if (!(x instanceof Iterable)) {
-            warning(new Exception("ST_LineString called with an object which is not a iterable"));
-            return null;
-        }
+        LineString geometry = null;
+        if (x instanceof byte[]) {
+            //wkb
+            try {
+                //try to convert from WKB
+                geometry = (LineString) getWKBReader().read((byte[]) x);
+            } catch (ParseException | ClassCastException ex) {
+                //we have try
+                warning(ex);
+            }
+        } else if (x instanceof String) {
+            //wkt
+            //todo handle gml
+            try {
+                //try to convert from WKT
+                geometry = (LineString) getWKTReader().read(x.toString());
+            } catch (ParseException | ClassCastException ex) {
+                //we have try
+                warning(ex);
+            }
+        } else {
+            if (x == null) {
+              x = Collections.EMPTY_LIST;
+            } else if (x instanceof Point) {
+                x = Arrays.asList(x);
+            } else if (!(x instanceof Iterable)) {
+                warning(new Exception("ST_LineString called with an object which is not a iterable"));
+                return null;
+            }
 
-        Iterator i = ((Iterable) x).iterator();
-        final List<Coordinate> coords = new ArrayList<>();
-        while (i.hasNext()) {
-            Object cdt = i.next();
-            if (cdt instanceof Point) {
-                coords.add(((Point) cdt).getCoordinate());
-            } else if (cdt instanceof Coordinate) {
-                coords.add(((Coordinate) cdt));
-            } else {
-                //what should we do ?
+            Iterator i = ((Iterable) x).iterator();
+            final List<Coordinate> coords = new ArrayList<>();
+            while (i.hasNext()) {
+                Object cdt = i.next();
+                if (cdt instanceof Point) {
+                    coords.add(((Point) cdt).getCoordinate());
+                } else if (cdt instanceof Coordinate) {
+                    coords.add(((Coordinate) cdt));
+                } else {
+                    //what should we do ?
+                }
             }
+
+            geometry = FilterGeometryUtils.GF.createLineString(coords.toArray(new Coordinate[coords.size()]));
         }
 
-        final LineString geometry = SQLMM.GF.createLineString(coords.toArray(new Coordinate[coords.size()]));
-        geometry.setUserData(crs);
+        if (geometry != null) geometry.setUserData(crs);
         return geometry;
     }
 
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_MultiLineString.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_MultiLineString.java
new file mode 100644
index 0000000..03dd413
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_MultiLineString.java
@@ -0,0 +1,52 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.MultiLineString;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_MultiLineString. <br>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_MultiLineString extends AbstractGeomConstructor {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 1756693505934851018L;
+
+    public static final String NAME = "ST_MultiLineString";
+
+    public ST_MultiLineString(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<MultiLineString> getExpectedClass() {
+        return MultiLineString.class;
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_MultiPoint.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_MultiPoint.java
new file mode 100644
index 0000000..51da090
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_MultiPoint.java
@@ -0,0 +1,52 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.MultiPoint;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_MultiPoint. <br>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_MultiPoint extends AbstractGeomConstructor {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 787499914563380171L;
+
+    public static final String NAME = "ST_MultiPoint";
+
+    public ST_MultiPoint(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<MultiPoint> getExpectedClass() {
+        return MultiPoint.class;
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_MultiPolygon.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_MultiPolygon.java
new file mode 100644
index 0000000..959d49b
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_MultiPolygon.java
@@ -0,0 +1,52 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.MultiPolygon;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_MultiPolygon. <br>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_MultiPolygon extends AbstractGeomConstructor {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -847780070815971604L;
+
+    public static final String NAME = "ST_MultiPolygon";
+
+    public ST_MultiPolygon(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<MultiPolygon> getExpectedClass() {
+        return MultiPolygon.class;
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_NumGeometries.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_NumGeometries.java
new file mode 100644
index 0000000..90fa9e0
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_NumGeometries.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_NumGeometries. <br>
+ * Return the cardinality of the ST_PrivateGeometries attribute of an ST_GeomCollection value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_NumGeometries extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 3736987404066163773L;
+
+    public static final String NAME = "ST_NumGeometries";
+
+    public ST_NumGeometries(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return geom.getNumGeometries();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Integer.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_NumInteriorRings.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_NumInteriorRings.java
new file mode 100644
index 0000000..da743c5
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_NumInteriorRings.java
@@ -0,0 +1,69 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Polygon;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_NumInteriorRings. <br>
+ * Return the cardinality of the ST_PrivateInteriorRings attribute of an ST_CurvePolygon value.
+ *
+ * This class should be for all curvepolygon types, but JTS only has Polygon.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_NumInteriorRings extends AbstractAccessorSpatialFunction<Polygon> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -7278238582506203440L;
+
+    public static final String NAME = "ST_NumInteriorRings";
+
+    public ST_NumInteriorRings(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Polygon> getExpectedClass() {
+        return Polygon.class;
+    }
+
+    @Override
+    public Object execute(Polygon geom, Object... params) throws ParseException {
+        return geom.getNumInteriorRing();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Integer.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_NumPoints.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_NumPoints.java
new file mode 100644
index 0000000..cd7bd63
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_NumPoints.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.LineString;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_NumPoints. <br>
+ * Return the cardinality of the ST_PrivatePoints attribute of an ST_LineString value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_NumPoints extends AbstractAccessorSpatialFunction<LineString> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -5615941135387263521L;
+
+    public static final String NAME = "ST_NumPoints";
+
+    public ST_NumPoints(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<LineString> getExpectedClass() {
+        return LineString.class;
+    }
+
+    @Override
+    public Object execute(LineString geom, Object... params) throws ParseException {
+        return geom.getNumPoints();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Integer.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Overlaps.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Overlaps.java
new file mode 100644
index 0000000..805aa70
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Overlaps.java
@@ -0,0 +1,63 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Overlaps. <br>
+ * Test if an ST_Geometry value spatially 2D overlaps another ST_Geometry value, ignoring z and m coordinate
+ * values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Overlaps extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -3322606303065731156L;
+
+    public static final String NAME = "ST_Overlaps";
+
+    public ST_Overlaps(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        return left.overlaps(right);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Perimeter.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Perimeter.java
new file mode 100644
index 0000000..ef5901b
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Perimeter.java
@@ -0,0 +1,71 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Perimeter. <br>
+ * Return the length measurement of the boundary of an ST_Surface value, ignoring z and m coordinate values
+ * in the calculations.
+ *
+ * This class should be for all surface types, but JTS only has Polygon and MultiPolygon.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Perimeter extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -1312027255077461307L;
+
+    public static final String NAME = "ST_Perimeter";
+
+    public ST_Perimeter(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        //lenght is the perimeter for polygons
+        return geom.getLength();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Double.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Point.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Point.java
index bbdf568..30d7175 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Point.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Point.java
@@ -16,13 +16,16 @@
  */
 package org.apache.sis.internal.filter.sqlmm;
 
+import java.math.BigDecimal;
 import org.apache.sis.feature.builder.AttributeTypeBuilder;
 import org.apache.sis.feature.builder.FeatureTypeBuilder;
 import org.apache.sis.feature.builder.PropertyTypeBuilder;
 import org.apache.sis.internal.filter.NamedFunction;
 import org.apache.sis.internal.feature.FeatureExpression;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
 import org.apache.sis.referencing.CRS;
 import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.Point;
 import org.opengis.feature.FeatureType;
 import org.opengis.filter.expression.Expression;
@@ -47,6 +50,10 @@ import org.opengis.util.FactoryException;
  * @module
  */
 final class ST_Point extends NamedFunction implements FeatureExpression {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -6280773709322350835L;
 
     /**
      * Name of this function as defined by SQL/MM standard.
@@ -61,23 +68,25 @@ final class ST_Point extends NamedFunction implements FeatureExpression {
      *
      * @throws IllegalArgumentException if the number of arguments is less then two.
      */
-    ST_Point(final Expression[] parameters) {
+    ST_Point(final Expression... parameters) {
         super(parameters);
-        if (parameters.length < 2) {
-            throw new IllegalArgumentException("ST_Point function expect 2 or more parameters");
+        if (parameters.length < 1 || parameters.length > 4) {
+            throw new IllegalArgumentException("ST_Point function expect 1 to 4 parameters");
         }
 
-        if (this.parameters.size() > 2) {
-            Object cdt = this.parameters.get(2).evaluate(null);
-            if (cdt instanceof Number) {
-                try {
-                    constantCrs = CRS.forCode("EPSG:" + ((Number) cdt).intValue());
-                } catch (FactoryException ex) {
-                    warning(ex);
-                }
-            } else if (cdt instanceof CoordinateReferenceSystem) {
-                constantCrs = (CoordinateReferenceSystem) cdt;
-            }
+        int nbarg = this.parameters.size();
+        switch (nbarg) {
+            case 2:
+                constantCrs = toCrs(null, this.parameters.get(1));
+                break;
+            case 3:
+                constantCrs = toCrs(null, this.parameters.get(2));
+                break;
+            case 4:
+                constantCrs = toCrs(null, this.parameters.get(3));
+                break;
+            default:
+                break;
         }
     }
 
@@ -90,26 +99,59 @@ final class ST_Point extends NamedFunction implements FeatureExpression {
     }
 
     @Override
-    public Object evaluate(Object object) {
-        Number x = parameters.get(0).evaluate(object, Number.class);
-        Number y = parameters.get(1).evaluate(object, Number.class);
+    public Object evaluate(Object candidate) {
+
         CoordinateReferenceSystem crs = constantCrs;
-        if (crs == null && parameters.size() > 2) {
-            Object cdt = parameters.get(2).evaluate(object);
-            if (cdt instanceof Number) {
-                try {
-                    crs = CRS.forCode("EPSG:" + ((Number) cdt).intValue());
-                } catch (FactoryException ex) {
-                    warning(ex);
+        final int nbarg = parameters.size();
+        Geometry geom;
+        if (nbarg == 1) {
+            //WKB or WKT
+            geom = FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+
+        } else if (nbarg == 2) {
+            final Object arg0 = parameters.get(0).evaluate(candidate);
+            final Object arg1 = parameters.get(1).evaluate(candidate);
+            if (arg0 instanceof Number) {
+                // X,Y
+                final Number obj1 = (Number) arg0;
+                final Number obj2 = (Number) arg1;
+                geom = FilterGeometryUtils.GF.createPoint(new Coordinate(obj1.doubleValue(), obj2.doubleValue()));
+            } else {
+                //WKT/WKB + srid
+                geom = FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+                if (crs == null) {
+                    crs = toCrs(candidate, parameters.get(1));
                 }
-            } else if (cdt instanceof CoordinateReferenceSystem) {
-                crs = (CoordinateReferenceSystem) cdt;
             }
+        } else if (nbarg == 3) {
+            final Number obj1 = parameters.get(0).evaluate(candidate, Number.class);
+            final Number obj2 = parameters.get(1).evaluate(candidate, Number.class);
+            final Object obj3 = parameters.get(2).evaluate(candidate);
+            geom = FilterGeometryUtils.GF.createPoint(new Coordinate(obj1.doubleValue(), obj2.doubleValue()));
+            if (obj3 instanceof Float || obj3 instanceof Double || obj3 instanceof BigDecimal) {
+                // Z
+                geom.getCoordinate().z = ((Number) obj3).doubleValue();
+            } else {
+                // srid
+                if (crs == null) {
+                    crs = toCrs(candidate, parameters.get(2));
+                }
+            }
+        } else if (nbarg == 4) {
+            final Number obj1 = parameters.get(0).evaluate(candidate, Number.class);
+            final Number obj2 = parameters.get(1).evaluate(candidate, Number.class);
+            final Number obj3 = parameters.get(2).evaluate(candidate, Number.class);
+            geom = FilterGeometryUtils.GF.createPoint(new Coordinate(obj1.doubleValue(), obj2.doubleValue(), obj3.doubleValue()));
+            if (crs == null) {
+                crs = toCrs(candidate, parameters.get(3));
+            }
+        } else {
+            //should not happen,constructor prevents it
+            geom = null;
         }
 
-        final Point point = SQLMM.GF.createPoint(new Coordinate(x.doubleValue(), y.doubleValue()));
-        point.setUserData(crs);
-        return point;
+        if (geom != null) geom.setUserData(crs);
+        return geom;
     }
 
     @Override
@@ -124,4 +166,18 @@ final class ST_Point extends NamedFunction implements FeatureExpression {
         return atb;
     }
 
+    private CoordinateReferenceSystem toCrs(Object candidate, Expression exp) {
+        Object cdt = exp.evaluate(candidate);
+        if (cdt instanceof Number) {
+            try {
+                cdt = CRS.forCode("EPSG:" + ((Number) cdt).intValue());
+            } catch (FactoryException ex) {
+                warning(ex);
+            }
+        } else if (cdt instanceof CoordinateReferenceSystem) {
+            return (CoordinateReferenceSystem) cdt;
+        }
+        return null;
+    }
+
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_PointN.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_PointN.java
new file mode 100644
index 0000000..e5c7530
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_PointN.java
@@ -0,0 +1,83 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.Point;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_PointN. <br>
+ * Return the specified element in the ST_PrivatePoints attribute of an ST_LineString value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_PointN extends AbstractAccessorSpatialFunction<LineString> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 6209252271398378085L;
+
+    public static final String NAME = "ST_PointN";
+
+    public ST_PointN(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected int getMinParams() {
+        return 2;
+    }
+
+    @Override
+    protected int getMaxParams() {
+        return 2;
+    }
+
+    @Override
+    protected Class<LineString> getExpectedClass() {
+        return LineString.class;
+    }
+
+    @Override
+    public Object execute(LineString geom, Object... params) throws ParseException {
+        final int index = Integer.parseInt(params[1].toString());
+        final Point pt = geom.getPointN(index-1);
+        copyCrs(geom, pt);
+        return pt;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Point.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_PointOnSurface.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_PointOnSurface.java
new file mode 100644
index 0000000..20d0c16
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_PointOnSurface.java
@@ -0,0 +1,75 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.locationtech.jts.geom.Point;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_PointOnSurface. <br>
+ * Return an ST_Point value guaranteed to spatially intersect the ST_Surface value, ignoring z and m coordinate
+ * values in the calculations and not including them in the resultant geometry.
+ *
+ * This class should be for all surface types, but JTS only has Polygon and MultiPolygon.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_PointOnSurface extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 4583128428102106294L;
+
+    public static final String NAME = "ST_PointOnSurface";
+
+    public ST_PointOnSurface(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        final Geometry pt = geom.getInteriorPoint();
+        copyCrs(geom, pt);
+        return pt;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Point.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Polygon.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Polygon.java
new file mode 100644
index 0000000..c490649
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Polygon.java
@@ -0,0 +1,60 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.locationtech.jts.geom.Polygon;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Polygon. <br>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Polygon extends AbstractGeomConstructor {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 852966071102079242L;
+
+    public static final String NAME = "ST_Polygon";
+
+    public ST_Polygon(Expression... parameters){
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Polygon> getExpectedClass() {
+        return Polygon.class;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Polygon.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Relate.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Relate.java
new file mode 100644
index 0000000..0d28272
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Relate.java
@@ -0,0 +1,74 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Relate. <br>
+ * Test if an ST_Geometry value is spatially 2D related to another ST_Geometry value, ignoring z and m
+ * coordinate values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Relate extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 5219117092935978607L;
+
+    public static final String NAME = "ST_Relate";
+
+    public ST_Relate(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected int getMinParams() {
+        return 2;
+    }
+
+    @Override
+    protected int getMaxParams() {
+        return 2;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        final boolean res = left.relate(right, params[1].toString());
+        return res;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SRID.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SRID.java
new file mode 100644
index 0000000..11023b6
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SRID.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_SRID. <br>
+ * Observe the spatial reference system identifier of the ST_Geometry value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_SRID extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -9096441878310687365L;
+
+    public static final String NAME = "ST_SRID";
+
+    public ST_SRID(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return geom.getSRID();
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Integer.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Simplify.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Simplify.java
index 7cb09ea..9aa2402 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Simplify.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Simplify.java
@@ -67,7 +67,7 @@ final class ST_Simplify extends NamedFunction implements FeatureExpression {
      *
      * @throws IllegalArgumentException if the number of arguments is not equal to 2.
      */
-    ST_Simplify(final Expression[] parameters) {
+    ST_Simplify(final Expression... parameters) {
         super(parameters);
         ArgumentChecks.ensureExpectedCount("parameters", 2, parameters.length);
     }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SimplifyPreserveTopology.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SimplifyPreserveTopology.java
index e264326..42c65f9 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SimplifyPreserveTopology.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SimplifyPreserveTopology.java
@@ -67,7 +67,7 @@ final class ST_SimplifyPreserveTopology extends NamedFunction implements Feature
      *
      * @throws IllegalArgumentException if the number of arguments is not equal to 2.
      */
-    ST_SimplifyPreserveTopology(final Expression[] parameters) {
+    ST_SimplifyPreserveTopology(final Expression... parameters) {
         super(parameters);
         ArgumentChecks.ensureExpectedCount("parameters", 2, parameters.length);
     }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_StartPoint.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_StartPoint.java
new file mode 100644
index 0000000..d66c619
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_StartPoint.java
@@ -0,0 +1,75 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.Point;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_StartPoint. <br>
+ * Return an ST_Point value that is the start point of an ST_Curve value including existing z and m coordinate
+ * values in the resultant geometry.
+ *
+ * This class should be for all curve types, but JTS only has LineString.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_StartPoint extends AbstractAccessorSpatialFunction<LineString> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -3584681263405936434L;
+
+    public static final String NAME = "ST_StartPoint";
+
+    public ST_StartPoint(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<LineString> getExpectedClass() {
+        return LineString.class;
+    }
+
+    @Override
+    public Object execute(LineString geom, Object... params) throws ParseException {
+        final Point pt = geom.getStartPoint();
+        copyCrs(geom, pt);
+        return pt;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Point.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SymDifference.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SymDifference.java
new file mode 100644
index 0000000..e64bb18
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_SymDifference.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_SymDifference. <br>
+ * Return an ST_Geometry value that represents the point set symmetric difference of two ST_Geometry values,
+ * ignoring z and m coordinate values in the calculations and not including them in the resultant geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_SymDifference extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 8791170084255971187L;
+
+    public static final String NAME = "ST_SymDifference";
+
+    public ST_SymDifference(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        final Geometry res = left.symDifference(right);
+        copyCrs(left, res);
+        return res;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Geometry.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToGeomColl.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToGeomColl.java
new file mode 100644
index 0000000..a8768de
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToGeomColl.java
@@ -0,0 +1,71 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.feature.jts.JTSMapping;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ToGeomColl. <br>
+ * Cast an ST_Geometry value to a specific instantiable subtype of ST_Geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ToGeomColl extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4026503261668900371L;
+
+    public static final String NAME = "ST_ToGeomColl";
+
+    public ST_ToGeomColl(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return JTSMapping.convertType(geom, GeometryCollection.class);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Geometry.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToLineString.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToLineString.java
new file mode 100644
index 0000000..0c487cc
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToLineString.java
@@ -0,0 +1,71 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.LineString;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.feature.jts.JTSMapping;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ToLineString. <br>
+ * Cast an ST_Geometry value to a specific instantiable subtype of ST_Geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ToLineString extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 5061161379544404715L;
+
+    public static final String NAME = "ST_ToLineString";
+
+    public ST_ToLineString(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return JTSMapping.convertType(geom, LineString.class);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Geometry.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToMultiLine.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToMultiLine.java
new file mode 100644
index 0000000..ed665a1
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToMultiLine.java
@@ -0,0 +1,71 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.MultiLineString;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.feature.jts.JTSMapping;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ToMultiLine. <br>
+ * Cast an ST_Geometry value to a specific instantiable subtype of ST_Geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ToMultiLine extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 4516671077052073740L;
+
+    public static final String NAME = "ST_ToMultiLine";
+
+    public ST_ToMultiLine(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return JTSMapping.convertType(geom, MultiLineString.class);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(MultiLineString.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToMultiPoint.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToMultiPoint.java
new file mode 100644
index 0000000..ec680ce
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToMultiPoint.java
@@ -0,0 +1,71 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.MultiPoint;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.feature.jts.JTSMapping;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ToMultiPoint. <br>
+ * Cast an ST_Geometry value to a specific instantiable subtype of ST_Geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ToMultiPoint extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -2683987052895075949L;
+
+    public static final String NAME = "ST_ToMultiPoint";
+
+    public ST_ToMultiPoint(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return JTSMapping.convertType(geom, MultiPoint.class);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(MultiPoint.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToMultiPolygon.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToMultiPolygon.java
new file mode 100644
index 0000000..ce712a3
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToMultiPolygon.java
@@ -0,0 +1,71 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.MultiPolygon;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.feature.jts.JTSMapping;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ToMultiPolygon. <br>
+ * Cast an ST_Geometry value to a specific instantiable subtype of ST_Geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ToMultiPolygon extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 4190767609852261562L;
+
+    public static final String NAME = "ST_ToMultiPolygon";
+
+    public ST_ToMultiPolygon(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return JTSMapping.convertType(geom, MultiPolygon.class);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(MultiPolygon.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToPoint.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToPoint.java
new file mode 100644
index 0000000..397b3c0
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToPoint.java
@@ -0,0 +1,71 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.Point;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.feature.jts.JTSMapping;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ToPoint. <br>
+ * Cast an ST_Geometry value to a specific instantiable subtype of ST_Geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ToPoint extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -2553595500105961498L;
+
+    public static final String NAME = "ST_ToPoint";
+
+    public ST_ToPoint(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return JTSMapping.convertType(geom, Point.class);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Point.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToPolygon.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToPolygon.java
new file mode 100644
index 0000000..eb610f0
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_ToPolygon.java
@@ -0,0 +1,71 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.Polygon;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.feature.jts.JTSMapping;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_ToPolygon. <br>
+ * Cast an ST_Geometry value to a specific instantiable subtype of ST_Geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_ToPolygon extends AbstractAccessorSpatialFunction<Geometry> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -6316093689932320348L;
+
+    public static final String NAME = "ST_ToPolygon";
+
+    public ST_ToPolygon(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Geometry> getExpectedClass() {
+        return Geometry.class;
+    }
+
+    @Override
+    public Object execute(Geometry geom, Object... params) throws ParseException {
+        return JTSMapping.convertType(geom, Polygon.class);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Polygon.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Touches.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Touches.java
new file mode 100644
index 0000000..6b2cac5
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Touches.java
@@ -0,0 +1,63 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_touches. <br>
+ * Test if an ST_Geometry value spatially 2D touches another ST_Geometry value, ignoring z and m coordinate
+ * values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Touches extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 35544493787096847L;
+
+    public static final String NAME = "ST_Touches";
+
+    public ST_Touches(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        return left.touches(right);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Transform.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Transform.java
index 986a6e9..e55bc8b 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Transform.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Transform.java
@@ -39,8 +39,16 @@ import org.apache.sis.util.resources.Errors;
 
 
 /**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Transform. <br>
+ * <p>
+ * Return an ST_Geometry value transformed to the specified spatial reference system, considering z and m
+ * coordinate values in the calculations and including them in the resultant geometry.
+ * </p>
+ *
+ * <p>
  * An expression which transforms a geometry from one CRS to another CRS.
  * This expression expects two arguments:
+ * </p>
  *
  * <ol class="verbose">
  *   <li>An expression returning a geometry object. The evaluated value shall be an instance of
@@ -99,7 +107,7 @@ final class ST_Transform extends NamedFunction implements FeatureExpression {
      * @throws IllegalArgumentException if the number of arguments is not equal to 2.
      * @throws FactoryException if CRS can not be constructed from the second expression.
      */
-    ST_Transform(final Expression[] parameters) throws FactoryException {
+    ST_Transform(final Expression... parameters) throws FactoryException {
         super(parameters);
         ArgumentChecks.ensureExpectedCount("parameters", 2, parameters.length);
         final Expression crs = parameters[1];
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Union.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Union.java
new file mode 100644
index 0000000..e23caa0
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Union.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Union. <br>
+ * Return an ST_Geometry value that represents the point set union of two ST_Geometry values, ignoring z and
+ * m coordinate values in the calculations and not including them in the resultant geometry.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Union extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 1984636364819136687L;
+
+    public static final String NAME = "ST_Union";
+
+    public ST_Union(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        final Geometry res = left.union(right);
+        copyCrs(left, res);
+        return res;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Geometry.class)
+                .setCRS(expectedCrs(valueType, parameters.get(0)))
+                .setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Within.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Within.java
new file mode 100644
index 0000000..849b76b
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Within.java
@@ -0,0 +1,63 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Geometry;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Within. <br>
+ * Test if an ST_Geometry value is spatially 2D within another ST_Geometry value, ignoring z and m coordinate
+ * values in the calculations.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Within extends AbstractBinarySpatialFunction {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4474212706955109548L;
+
+    public static final String NAME = "ST_Within";
+
+    public ST_Within(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public Object execute(Geometry left, Geometry right, Object... params) throws ParseException {
+        return left.within(right);
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Boolean.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_X.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_X.java
new file mode 100644
index 0000000..0f894e6
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_X.java
@@ -0,0 +1,67 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.locationtech.jts.geom.Point;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_X. <br>
+ * Observe the x coordinate value of an ST_Point value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_X extends AbstractAccessorSpatialFunction<Point> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 143569120333779562L;
+
+    public static final String NAME = "ST_X";
+
+    public ST_X(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Point> getExpectedClass() {
+        return Point.class;
+    }
+
+    @Override
+    public Object execute(Point geom, Object... params) throws ParseException {
+        return geom.getCoordinate().x;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Double.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_XFromBinary.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_XFromBinary.java
new file mode 100644
index 0000000..0e7e2c7
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_XFromBinary.java
@@ -0,0 +1,385 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPoint;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Polygon;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since 2.0
+ * @module
+ */
+abstract class ST_XFromBinary extends AbstractSpatialFunction {
+
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 8111450596023441499L;
+
+    public ST_XFromBinary(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    protected int getMinParams() {
+        return 1;
+    }
+
+    @Override
+    protected int getMaxParams() {
+        return 2;
+    }
+
+    protected abstract Class getExpectedClass();
+
+    public String getSyntax() {
+        return getName() + " ( <wkb> [,<srid>] )";
+    }
+
+    @Override
+    public Geometry evaluate(Object candidate) {
+
+        Geometry geom = FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+
+        if (!getExpectedClass().isInstance(geom)) {
+            warning(new Exception("WKB is not of expected type : " + getExpectedClass().getSimpleName()));
+            return null;
+        }
+
+        if (parameters.size() > 1) {
+            //srid
+            geom.setSRID(((Number) parameters.get(1).evaluate(candidate)).intValue());
+        }
+
+        return geom;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(getExpectedClass()).setName(getName());
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_BdMPolyFromWKB. <br>
+     * Return an ST_MultiPolygon value which is transformed from a BINARY LARGE
+     * OBJECT value that represents the well-known binary representation of an
+     * ST_MultiLineString value.
+     */
+    public static final class BdMPoly extends ST_XFromBinary {
+
+        public static final String NAME = "ST_BdMPolyFromWKB";
+        private static final long serialVersionUID = -5763967468534517956L;
+
+        public BdMPoly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiLineString> getExpectedClass() {
+            return MultiLineString.class;
+        }
+
+        @Override
+        public Geometry evaluate(Object candidate) {
+            final MultiLineString mls = (MultiLineString) FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+
+            final LinearRing exterior;
+            final LinearRing[] interiors = new LinearRing[mls.getNumGeometries() - 1];
+
+            exterior = FilterGeometryUtils.GF.createLinearRing(mls.getGeometryN(0).getCoordinates());
+            for (int i = 0; i < interiors.length; i++) {
+                interiors[i] = FilterGeometryUtils.GF.createLinearRing(mls.getGeometryN(i + 1).getCoordinates());
+            }
+
+            final Polygon poly = FilterGeometryUtils.GF.createPolygon(exterior, interiors);
+            final MultiPolygon mpoly = FilterGeometryUtils.GF.createMultiPolygon(new Polygon[]{poly});
+            mpoly.setSRID(mls.getSRID());
+            return mpoly;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_BdPolyFromWKB. <br>
+     * Return an ST_Polygon value which is transformed from a BINARY LARGE
+     * OBJECT value that represents the well-known binary representation of an
+     * ST_Polygon value.
+     */
+    public static final class BdPoly extends ST_XFromBinary {
+
+        public static final String NAME = "ST_BdPolyFromWKB";
+        private static final long serialVersionUID = 6661949194256216330L;
+
+        public BdPoly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiLineString> getExpectedClass() {
+            return MultiLineString.class;
+        }
+
+        @Override
+        public Geometry evaluate(Object candidate) {
+            final MultiLineString mls = (MultiLineString) FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+
+            final LinearRing exterior;
+            final LinearRing[] interiors = new LinearRing[mls.getNumGeometries() - 1];
+
+            exterior = FilterGeometryUtils.GF.createLinearRing(mls.getGeometryN(0).getCoordinates());
+            for (int i = 0; i < interiors.length; i++) {
+                interiors[i] = FilterGeometryUtils.GF.createLinearRing(mls.getGeometryN(i + 1).getCoordinates());
+            }
+
+            final Polygon poly = FilterGeometryUtils.GF.createPolygon(exterior, interiors);
+            poly.setSRID(mls.getSRID());
+            return poly;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_GeomCollFromWKB. <br>
+     * Return an ST_GeomCollection value which is transformed from a BINARY
+     * LARGE OBJECT value that represents the well-known binary representation
+     * of an ST_GeomCollection value.
+     */
+    public static final class GeomColl extends ST_XFromBinary {
+
+        public static final String NAME = "ST_GeomCollFromWKB";
+        private static final long serialVersionUID = -1229981535753136679L;
+
+        public GeomColl(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<GeometryCollection> getExpectedClass() {
+            return GeometryCollection.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_GeomFromWKB. <br>
+     * Return an ST_Geometry value which is transformed from a BINARY LARGE
+     * OBJECT value that represents the well-known binary representation of an
+     * ST_Geometry value.
+     */
+    public static final class Geom extends ST_XFromBinary {
+
+        public static final String NAME = "ST_GeomFromWKB";
+        private static final long serialVersionUID = -1428672188136390056L;
+
+        public Geom(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<Geometry> getExpectedClass() {
+            return Geometry.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_LineFromWKB. <br>
+     * Return an ST_LineString value which is transformed from a BINARY LARGE
+     * OBJECT value that represents the well-known binary representation of an
+     * ST_LineString value.
+     */
+    public static final class Line extends ST_XFromBinary {
+
+        public static final String NAME = "ST_LineFromWKB";
+        private static final long serialVersionUID = -4525486088596088088L;
+
+        public Line(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<LineString> getExpectedClass() {
+            return LineString.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_MPointFromWKB. <br>
+     * Return an ST_MultiLineString value which is transformed from a BINARY
+     * LARGE OBJECT value that represents the well-known binary representation
+     * of an ST_MultiLineString value.
+     */
+    public static final class MLine extends ST_XFromBinary {
+
+        public static final String NAME = "ST_MLineFromWKB";
+        private static final long serialVersionUID = 1733897936536015735L;
+
+        public MLine(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiLineString> getExpectedClass() {
+            return MultiLineString.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_MPointFromWKB. <br>
+     * Return an ST_MultiPoint value which is transformed from a BINARY LARGE
+     * OBJECT value that represents the well-known binary representation of an
+     * ST_MultiPoint value.
+     */
+    public static final class MPoint extends ST_XFromBinary {
+
+        public static final String NAME = "ST_MPointFromWKB";
+        private static final long serialVersionUID = 1725448922596814788L;
+
+        public MPoint(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiPoint> getExpectedClass() {
+            return MultiPoint.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_MPolyFromWKB. <br>
+     * Return an ST_MultiPolygon value which is transformed from a BINARY LARGE
+     * OBJECT value that represents the well-known binary representation of an
+     * ST_MultiPolygon value.
+     */
+    public static final class MPoly extends ST_XFromBinary {
+
+        public static final String NAME = "ST_MPolyFromWKB";
+        private static final long serialVersionUID = 8180295093331098160L;
+
+        public MPoly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiPolygon> getExpectedClass() {
+            return MultiPolygon.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_PointFromWKB. <br>
+     * Return an ST_Point value which is transformed from a BINARY LARGE OBJECT
+     * value that represents the well-known binary representation of an ST_Point
+     * value.
+     */
+    public static final class Point extends ST_XFromBinary {
+
+        public static final String NAME = "ST_PointFromWKB";
+        private static final long serialVersionUID = 6307946926253919188L;
+
+        public Point(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<org.locationtech.jts.geom.Point> getExpectedClass() {
+            return org.locationtech.jts.geom.Point.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_PolyFromWKB. <br>
+     * Return an ST_Polygon value which is transformed from a BINARY LARGE
+     * OBJECT value that represents the well-known binary representation of an
+     * ST_Polygon value.
+     */
+    public static final class Poly extends ST_XFromBinary {
+
+        public static final String NAME = "ST_PolyFromWKB";
+        private static final long serialVersionUID = 3729779304169979534L;
+
+        public Poly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<Polygon> getExpectedClass() {
+            return Polygon.class;
+        }
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_XFromGML.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_XFromGML.java
new file mode 100644
index 0000000..f79e3c3
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_XFromGML.java
@@ -0,0 +1,298 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPoint;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Polygon;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since 2.0
+ * @module
+ */
+abstract class ST_XFromGML extends AbstractSpatialFunction {
+
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 6113283783777989678L;
+
+    public ST_XFromGML(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    protected int getMinParams() {
+        return 1;
+    }
+
+    @Override
+    protected int getMaxParams() {
+        return 2;
+    }
+
+    protected abstract Class getExpectedClass();
+
+    @Override
+    public String getSyntax() {
+        return getName() + " ( <gml> [,<srid>] )";
+    }
+
+    @Override
+    public Geometry evaluate(Object candidate) {
+
+        final Object obj = parameters.get(0).evaluate(candidate);
+
+        final Geometry geom;
+        if (true) throw new RuntimeException("GML not supported yet");
+
+        if (!getExpectedClass().isInstance(geom)) {
+            warning(new Exception("GML is not of expected type : " + getExpectedClass().getSimpleName()));
+            return null;
+        }
+
+        if (parameters.size() > 1) {
+            //srid
+            geom.setSRID(((Number) parameters.get(1).evaluate(candidate)).intValue());
+        }
+
+        return geom;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(getExpectedClass()).setName(getName());
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_GeomCollFromGML. <br>
+     * Return an ST_GeomCollection value which is transformed from a CHARACTER
+     * LARGE OBJECT value that represents the GML representation of an
+     * ST_GeomCollection value.
+     */
+    public static final class GeomColl extends ST_XFromGML {
+
+        public static final String NAME = "ST_GeomCollFromGML";
+        private static final long serialVersionUID = 2729025080212643888L;
+
+        public GeomColl(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<GeometryCollection> getExpectedClass() {
+            return GeometryCollection.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_GeomFromGML. <br>
+     * Return an ST_Geometry value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the GML representation of an ST_Geometry.     *
+     */
+    public static final class Geom extends ST_XFromGML {
+
+        public static final String NAME = "ST_GeomFromGML";
+        private static final long serialVersionUID = -2967969861402168905L;
+
+        public Geom(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<Geometry> getExpectedClass() {
+            return Geometry.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_LineFromGML. <br>
+     * Return an ST_LineString value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the GML representation of an ST_LineString
+     * value.
+     */
+    public static final class Line extends ST_XFromGML {
+
+        public static final String NAME = "ST_LineFromGML";
+        private static final long serialVersionUID = 2939373456076304217L;
+
+        public Line(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<LineString> getExpectedClass() {
+            return LineString.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_MPointFromGML. <br>
+     * Return an ST_MultiLineString value which is transformed from a CHARACTER
+     * LARGE OBJECT value that represents the GML representation of an
+     * ST_MultiLineString value.
+     */
+    public static final class MLine extends ST_XFromGML {
+
+        public static final String NAME = "ST_MLineFromGML";
+        private static final long serialVersionUID = -6449667109261980974L;
+
+        public MLine(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiLineString> getExpectedClass() {
+            return MultiLineString.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_MPointFromGML. <br>
+     * Return an ST_MultiPoint value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the GML representation of an ST_MultiPoint
+     * value.
+     */
+    public static final class MPoint extends ST_XFromGML {
+
+        public static final String NAME = "ST_MPointFromGML";
+        private static final long serialVersionUID = 268946482386292257L;
+
+        public MPoint(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiPoint> getExpectedClass() {
+            return MultiPoint.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_MPolyFromGML. <br>
+     * Return an ST_MultiPolygon value which is transformed from a CHARACTER
+     * LARGE OBJECT value that represents the GML representation of an
+     * ST_MultiPolygon value.
+     */
+    public static final class MPoly extends ST_XFromGML {
+
+        public static final String NAME = "ST_MPolyFromGML";
+        private static final long serialVersionUID = 3505318747927007728L;
+
+        public MPoly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiPolygon> getExpectedClass() {
+            return MultiPolygon.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_PointFromGML. <br>
+     * Return an ST_Point value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the GML representation of an ST_Point.
+     */
+    public static final class Point extends ST_XFromGML {
+
+        public static final String NAME = "ST_PointFromGML";
+        private static final long serialVersionUID = -1032773543624102657L;
+
+        public Point(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<org.locationtech.jts.geom.Point> getExpectedClass() {
+            return org.locationtech.jts.geom.Point.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_PolyFromGML. <br>
+     * Return an ST_Polygon value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the GML representation of an ST_Polygon
+     * value.
+     */
+    public static final class Poly extends ST_XFromGML {
+
+        public static final String NAME = "ST_PolyFromGML";
+        private static final long serialVersionUID = 7305807862301697831L;
+
+        public Poly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<Polygon> getExpectedClass() {
+            return Polygon.class;
+        }
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_XFromText.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_XFromText.java
new file mode 100644
index 0000000..2aede56
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_XFromText.java
@@ -0,0 +1,385 @@
+/*
+ * 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.filter.sqlmm;
+
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.apache.sis.internal.filter.FilterGeometryUtils;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPoint;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Polygon;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since 2.0
+ * @module
+ */
+abstract class ST_XFromText extends AbstractSpatialFunction {
+
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -9046918207193451904L;
+
+    public ST_XFromText(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    protected int getMinParams() {
+        return 1;
+    }
+
+    @Override
+    protected int getMaxParams() {
+        return 2;
+    }
+
+    protected abstract Class getExpectedClass();
+
+    @Override
+    public String getSyntax() {
+        return getName() + " ( <text> [,<srid>] )";
+    }
+
+    @Override
+    public Geometry evaluate(Object candidate) {
+
+        final Geometry geom = FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+
+        if (!getExpectedClass().isInstance(geom)) {
+            warning(new Exception("WKT is not of expected type : " + getExpectedClass().getSimpleName()));
+            return null;
+        }
+
+        if (parameters.size() > 1) {
+            //srid
+            geom.setSRID(((Number) parameters.get(1).evaluate(candidate)).intValue());
+        }
+        return geom;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(getExpectedClass()).setName(getName());
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_BdMPolyFromText. <br>
+     * Return an ST_MultiPolygon value which is transformed from a CHARACTER
+     * LARGE OBJECT value that represents the well-known text representation of
+     * an ST_MultiLineString value.
+     */
+    public static final class BdMPoly extends ST_XFromText {
+
+        public static final String NAME = "ST_BdMPolyFromText";
+        private static final long serialVersionUID = 9014831950657428233L;
+
+        public BdMPoly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiLineString> getExpectedClass() {
+            return MultiLineString.class;
+        }
+
+        @Override
+        public Geometry evaluate(Object candidate) {
+            final MultiLineString mls = (MultiLineString) FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+
+            final LinearRing exterior;
+            final LinearRing[] interiors = new LinearRing[mls.getNumGeometries() - 1];
+
+            exterior = FilterGeometryUtils.GF.createLinearRing(mls.getGeometryN(0).getCoordinates());
+            for (int i = 0; i < interiors.length; i++) {
+                interiors[i] = FilterGeometryUtils.GF.createLinearRing(mls.getGeometryN(i + 1).getCoordinates());
+            }
+
+            final Polygon poly = FilterGeometryUtils.GF.createPolygon(exterior, interiors);
+            final MultiPolygon mpoly = FilterGeometryUtils.GF.createMultiPolygon(new Polygon[]{poly});
+            mpoly.setSRID(mls.getSRID());
+            return mpoly;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_BdPolyFromText. <br>
+     * Return an ST_Polygon value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the well-known text representation of an
+     * ST_MultiLineString value.
+     */
+    public static final class BdPoly extends ST_XFromText {
+
+        public static final String NAME = "ST_BdPolyFromText";
+        private static final long serialVersionUID = -5743188370723944952L;
+
+        public BdPoly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiLineString> getExpectedClass() {
+            return MultiLineString.class;
+        }
+
+        @Override
+        public Geometry evaluate(Object candidate) {
+            final MultiLineString mls = (MultiLineString) FilterGeometryUtils.toGeometry(candidate, parameters.get(0));
+
+            final LinearRing exterior;
+            final LinearRing[] interiors = new LinearRing[mls.getNumGeometries() - 1];
+
+            exterior = FilterGeometryUtils.GF.createLinearRing(mls.getGeometryN(0).getCoordinates());
+            for (int i = 0; i < interiors.length; i++) {
+                interiors[i] = FilterGeometryUtils.GF.createLinearRing(mls.getGeometryN(i + 1).getCoordinates());
+            }
+
+            final Polygon poly = FilterGeometryUtils.GF.createPolygon(exterior, interiors);
+            poly.setSRID(mls.getSRID());
+            return poly;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_GeomCollFromText. <br>
+     * Return an ST_GeomCollection value which is transformed from a CHARACTER
+     * LARGE OBJECT value that represents the well-known text representation of
+     * an ST_GeomCollection value.
+     */
+    public static final class GeomColl extends ST_XFromText {
+
+        public static final String NAME = "ST_GeomCollFromText";
+        private static final long serialVersionUID = 8816234645129212298L;
+
+        public GeomColl(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<GeometryCollection> getExpectedClass() {
+            return GeometryCollection.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_GeomFromText. <br>
+     * Return an ST_Geometry value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the well-known text representation of an
+     * ST_Geometry value.
+     */
+    public static final class Geom extends ST_XFromText {
+
+        public static final String NAME = "ST_GeomFromText";
+        private static final long serialVersionUID = -1627156125717985980L;
+
+        public Geom(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<Geometry> getExpectedClass() {
+            return Geometry.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_LineFromText. <br>
+     * Return an ST_LineString value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the well-known text representation of an
+     * ST_LineString value.
+     */
+    public static final class Line extends ST_XFromText {
+
+        public static final String NAME = "ST_LineFromText";
+        private static final long serialVersionUID = -6384733422127389790L;
+
+        public Line(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<LineString> getExpectedClass() {
+            return LineString.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_MLineFromText. <br>
+     * Return an ST_MultiLineString value which is transformed from a CHARACTER
+     * LARGE OBJECT value that represents the well-known text representation of
+     * an ST_MultiLineString value.
+     */
+    public static final class MLine extends ST_XFromText {
+
+        public static final String NAME = "ST_MLineFromText";
+        private static final long serialVersionUID = -8797802130317419443L;
+
+        public MLine(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiLineString> getExpectedClass() {
+            return MultiLineString.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_MPointFromText. <br>
+     * Return an ST_MultiPoint value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the well-known text representation of an
+     * ST_MultiPoint value.
+     */
+    public static final class MPoint extends ST_XFromText {
+
+        public static final String NAME = "ST_MPointFromText";
+        private static final long serialVersionUID = 5037254027906574270L;
+
+        public MPoint(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiPoint> getExpectedClass() {
+            return MultiPoint.class;
+        }
+
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_MPolyFromText. <br>
+     * Return an ST_MultiPolygon value which is transformed from a CHARACTER
+     * LARGE OBJECT value that represents the well-known text representation of
+     * an ST_MultiPolygon value.
+     */
+    public static final class MPoly extends ST_XFromText {
+
+        public static final String NAME = "ST_MPolyFromText";
+        private static final long serialVersionUID = 5914267165283411013L;
+
+        public MPoly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<MultiPolygon> getExpectedClass() {
+            return MultiPolygon.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_PointFromText. <br>
+     * Return an ST_Point value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the well-known text representation of an
+     * ST_Point value.
+     */
+    public static final class Point extends ST_XFromText {
+
+        public static final String NAME = "ST_PointFromText";
+        private static final long serialVersionUID = -5265959153677337532L;
+
+        public Point(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<org.locationtech.jts.geom.Point> getExpectedClass() {
+            return org.locationtech.jts.geom.Point.class;
+        }
+    }
+
+    /**
+     * SQL/MM, ISO/IEC 13249-3:2011, ST_PolyFromText. <br>
+     * Return an ST_Polygon value which is transformed from a CHARACTER LARGE
+     * OBJECT value that represents the well-known text representation of an
+     * ST_Polygon value.
+     */
+    public static final class Poly extends ST_XFromText {
+
+        public static final String NAME = "ST_PolyFromText";
+        private static final long serialVersionUID = -2502891009213433036L;
+
+        public Poly(Expression[] parameters) {
+            super(parameters);
+        }
+
+        @Override
+        public String getName() {
+            return NAME;
+        }
+
+        @Override
+        protected Class<Polygon> getExpectedClass() {
+            return Polygon.class;
+        }
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Y.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Y.java
new file mode 100644
index 0000000..e7e9e5b
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Y.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.sis.internal.filter.sqlmm;
+
+import org.locationtech.jts.geom.Point;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Y. <br>
+ * Observe the y coordinate value of an ST_Point value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Y extends AbstractAccessorSpatialFunction<Point> {
+
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -6063954169922560196L;
+
+    public static final String NAME = "ST_Y";
+
+    public ST_Y(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Point> getExpectedClass() {
+        return Point.class;
+    }
+
+    @Override
+    public Object execute(Point geom, Object... params) throws ParseException {
+        return geom.getCoordinate().y;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Double.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Z.java b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Z.java
new file mode 100644
index 0000000..c65eeb7
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/filter/sqlmm/ST_Z.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.sis.internal.filter.sqlmm;
+
+import org.locationtech.jts.geom.Point;
+import java.text.ParseException;
+import org.apache.sis.feature.builder.FeatureTypeBuilder;
+import org.apache.sis.feature.builder.PropertyTypeBuilder;
+import org.opengis.feature.FeatureType;
+import org.opengis.filter.expression.Expression;
+
+/**
+ * SQL/MM, ISO/IEC 13249-3:2011, ST_Z. <br>
+ * Observe the z coordinate value of an ST_Point value.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class ST_Z extends AbstractAccessorSpatialFunction<Point> {
+
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 8600571279426234278L;
+
+    public static final String NAME = "ST_Z";
+
+    public ST_Z(Expression... parameters) {
+        super(parameters);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    protected Class<Point> getExpectedClass() {
+        return Point.class;
+    }
+
+    @Override
+    public Object execute(Point geom, Object... params) throws ParseException {
+        return geom.getCoordinate().z;
+    }
+
+    @Override
+    public PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo) {
+        return addTo.addAttribute(Double.class).setName(NAME);
+    }
+
+}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/SQLMMTest.java b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/SQLMMTest.java
similarity index 94%
rename from core/sis-feature/src/test/java/org/apache/sis/internal/filter/SQLMMTest.java
rename to core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/SQLMMTest.java
index c437032..ffd1f10 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/internal/filter/SQLMMTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/internal/filter/sqlmm/SQLMMTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.filter;
+package org.apache.sis.internal.filter.sqlmm;
 
 import java.util.Arrays;
 import org.opengis.feature.Feature;
@@ -259,19 +259,6 @@ public final strictfp class SQLMMTest extends TestCase {
 
     @Test
     public void ST_Intersects() throws Exception {
-        try {
-            new ST_Intersects(null, null);
-            fail("ST_Intersects operator should not accept null parameters");
-        } catch (NullArgumentException e) {
-            // expected behavior
-        }
-
-        try {
-            new ST_Intersects(null, factory.literal(new GeneralEnvelope(2)));
-            fail("ST_Intersects operator should not accept null parameters");
-        } catch (NullArgumentException e) {
-            // expected behavior
-        }
 
         final Coordinate start = new Coordinate(0, 0.1);
         final Coordinate second = new Coordinate(1.2, 0.2);
@@ -282,8 +269,8 @@ public final strictfp class SQLMMTest extends TestCase {
         final Literal lring = factory.literal(ring);
         ST_Intersects st = new ST_Intersects(factory.literal(geometryFactory.createPoint(new Coordinate(2, 4))), lring);
         // Ensure argument nullity does not modify behavior
-        assertFalse("Unexpected intersection", st.evaluate(null));
-        assertFalse("Unexpected intersection", st.evaluate(new Object()));
+        assertFalse("Unexpected intersection", (Boolean) st.evaluate(null));
+        assertFalse("Unexpected intersection", (Boolean) st.evaluate(new Object()));
 
         // Border should intersect
         final Feature f = mock();
@@ -291,10 +278,10 @@ public final strictfp class SQLMMTest extends TestCase {
         f.setPropertyValue(P_NAME, point);
         final PropertyName geomName = factory.property(P_NAME);
         st = new ST_Intersects(geomName, lring);
-        assertTrue("Border point should intersect triangle", st.evaluate(f));
+        assertTrue("Border point should intersect triangle", (Boolean) st.evaluate(f));
         // Ensure inverting expression does not modify behavior.
         st = new ST_Intersects(lring, geomName);
-        assertTrue("Border point should intersect triangle", st.evaluate(f));
+        assertTrue("Border point should intersect triangle", (Boolean) st.evaluate(f));
 
         // Ensure CRS conversion works as expected (see package-info).
         // Missing
@@ -311,8 +298,8 @@ public final strictfp class SQLMMTest extends TestCase {
         final Geometry nadPoint = JTS.transform(point, CRS.findOperation(crs84, nadUtm, null));
         f.setPropertyValue(P_NAME, nadPoint);
         ring.setUserData(crs84);
-        assertTrue("Intersection should be found when CRS are compatible", new ST_Intersects(geomName, lring).evaluate(f));
-        assertTrue("Intersection should be found when CRS are compatible", new ST_Intersects(factory.literal(nadPoint), lring).evaluate(null));
+        assertTrue("Intersection should be found when CRS are compatible", (Boolean) new ST_Intersects(geomName, lring).evaluate(f));
+        assertTrue("Intersection should be found when CRS are compatible", (Boolean) new ST_Intersects(factory.literal(nadPoint), lring).evaluate(null));
 
         // Common base CRS
         final ProjectedCRS utm00 = CommonCRS.WGS84.universal(0, 0);
@@ -320,8 +307,8 @@ public final strictfp class SQLMMTest extends TestCase {
         final ProjectedCRS utm78 = CommonCRS.WGS84.universal(7, 8);
         final Geometry pointUtm78 = JTS.transform(ring, CRS.findOperation(crs84, utm78, null));
         f.setPropertyValue(P_NAME, pointUtm78);
-        assertTrue("Intersection should be found when CRS are compatible", new ST_Intersects(geomName, factory.literal(ringUtm00)).evaluate(f));
-        assertTrue("Intersection should be found when CRS are compatible", new ST_Intersects(factory.literal(pointUtm78), factory.literal(ringUtm00)).evaluate(null));
+        assertTrue("Intersection should be found when CRS are compatible", (Boolean) new ST_Intersects(geomName, factory.literal(ringUtm00)).evaluate(f));
+        assertTrue("Intersection should be found when CRS are compatible", (Boolean) new ST_Intersects(factory.literal(pointUtm78), factory.literal(ringUtm00)).evaluate(null));
     }
 
     /**
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 6ccacf2..3058a84 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
@@ -61,7 +61,7 @@ import org.junit.runners.Suite;
     org.apache.sis.filter.LikeFunctionTest.class,
     org.apache.sis.filter.SpatialFunctionTest.class,
     org.apache.sis.filter.TemporalFunctionTest.class,
-    org.apache.sis.internal.filter.SQLMMTest.class,
+    org.apache.sis.internal.filter.sqlmm.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