You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2026/01/26 16:24:40 UTC
(sis) branch geoapi-4.0 updated (9f766a14c9 -> 4065171388)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
from 9f766a14c9 Consolidation of the conversion of temporal objects before comparison. It fixes a `NullPointerException` when there is no converter available.
new 28334378d5 Convert literal values in advance when possible.
new 4065171388 Add a note about a work to do in `PassThroughTransform`.
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../org/apache/sis/filter/ComparisonFilter.java | 11 ++
.../main/org/apache/sis/filter/Optimization.java | 116 +++++++++++++++------
.../main/org/apache/sis/filter/base/Node.java | 10 ++
.../operation/transform/PassThroughTransform.java | 10 +-
.../operation/transform/TransformJoiner.java | 17 ++-
.../operation/transform/WraparoundTransform.java | 5 +-
6 files changed, 122 insertions(+), 47 deletions(-)
(sis) 02/02: Add a note about a work to do in `PassThroughTransform`.
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 4065171388b647f26914d5fc7d34d21a221954b3
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Jan 26 16:55:07 2026 +0100
Add a note about a work to do in `PassThroughTransform`.
---
.../operation/transform/PassThroughTransform.java | 10 +++-------
.../operation/transform/TransformJoiner.java | 17 ++++++++---------
.../operation/transform/WraparoundTransform.java | 5 +++--
3 files changed, 14 insertions(+), 18 deletions(-)
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/PassThroughTransform.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
index c775ff5db7..fd1e6c7d78 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/PassThroughTransform.java
@@ -825,13 +825,6 @@ public class PassThroughTransform extends AbstractMathTransform implements Seria
* which above loop was executed.
*/
if (m == null) {
- /*
- * Do not invoke `super.tryConcatenate(context)`. We do not want to test if this transform
- * is the inverse of the `other` transform because this check is costly and unnecessary.
- * If the two transforms were the inverse of each other, then the concatenation of
- * `this.subTransform` with `other.subTransform` done at the beginning of this method
- * would have produced the identity transform already.
- */
return;
}
/*
@@ -852,6 +845,9 @@ public class PassThroughTransform extends AbstractMathTransform implements Seria
}
}
/*
+ * TODO: replace the remaining code by a call to `TransformJoiner.reduceDimension(…)`.
+ * But first, we need to verify if this is really equivalent.
+ *
* Verify if matrix discards the sub-transform. If it does not, then we need to keep all the sub-transform
* dimensions (discarding them is a "all or nothing" operation). Other dimensions (leading and trailing)
* can be keep or discarded on a case-by-case basis.
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/TransformJoiner.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/TransformJoiner.java
index 42ab864fca..b4562242a4 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/TransformJoiner.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/TransformJoiner.java
@@ -22,7 +22,6 @@ import java.util.List;
import java.util.Map;
import java.util.Iterator;
import java.util.Optional;
-import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.IntFunction;
import java.util.function.UnaryOperator;
@@ -785,14 +784,14 @@ valid: if (i >= 0 && i < steps.size()) {
* (depending on the second argument) equals to {@link BitSet#cardinality()}, or {@code null}
* if the reducer cannot build the transform.
*
- * @param inputDimensions dimensions of coordinates used by the main transform.
- * @param outputDimensions dimensions of coordinates with a value modified by the transform.
+ * @param inputDimensions dimensions of input coordinates of the main transform that are not pass-through.
+ * @param outputDimensions dimensions of output coordinates of the main transform that are not pass-through.
* @param reducer a constructor accepting selected dimension and returning the new transform.
* @return whether the transform chain has been modified as a result of this method call.
* @throws FactoryException if the concatenation is not a valid replacement.
*/
- final boolean reduceDimension(final Set<Integer> inputDimensions,
- final Set<Integer> outputDimensions,
+ final boolean reduceDimension(final BitSet inputDimensions,
+ final BitSet outputDimensions,
final BiFunction<BitSet, Integer, MathTransform> reducer)
throws FactoryException
{
@@ -820,7 +819,7 @@ prepare: if (relativeIndex < 0) {
* We will try to move `matrix` after the main transform for making that transform smaller.
* We only need to keep the rows which modify the input coordinates of the main transform.
*/
- inputDimensions.forEach(rowsToKeep::set);
+ rowsToKeep.or(inputDimensions);
if (rowsToKeep.length() >= numCol) continue;
} else {
/*
@@ -828,13 +827,13 @@ prepare: if (relativeIndex < 0) {
* We will try to move `matrix` before the main transform for making that transform smaller.
* But we cannot move the rows which depend on the output coordinates of the main transform.
*/
- for (int i : outputDimensions) {
+ outputDimensions.stream().forEach((i) -> {
for (int j=0; j<numRow; j++) {
if (move.getElement(j, i) != 0) {
rowsToKeep.set(j);
}
}
- }
+ });
if (rowsToKeep.isEmpty()) {
if (replace(0, factory.createAffineTransform(createDiagonalMatrix(0)))) {
return true;
@@ -865,7 +864,7 @@ prepare: if (relativeIndex < 0) {
* the coordinates modified by `move` will not impact or will not be impacted (depending on
* whether `move` will be moved to the right or to the left) by the main transform.
*/
- (relativeIndex < 0 ? outputDimensions : inputDimensions).forEach(rowsToKeep::set);
+ rowsToKeep.or(relativeIndex < 0 ? outputDimensions : inputDimensions);
// Check input coordinates (columns) because `move` is still a step after `keep`.
for (int i=0; (i = rowsToKeep.nextSetBit(i)) >= 0; i++) {
for (int j=0; j<numRow; j++) {
diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/WraparoundTransform.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/WraparoundTransform.java
index ac1100232f..55167755e4 100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/WraparoundTransform.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/WraparoundTransform.java
@@ -16,7 +16,7 @@
*/
package org.apache.sis.referencing.operation.transform;
-import java.util.Set;
+import java.util.BitSet;
import java.util.HashMap;
import java.util.Objects;
import java.util.function.Function;
@@ -495,7 +495,8 @@ public class WraparoundTransform extends AbstractMathTransform implements Serial
return;
}
if (getClass() == WraparoundTransform.class) { // Because we did not defined an overrideable `redimension` method yet.
- final var workOn = Set.of(wraparoundDimension);
+ final var workOn = new BitSet();
+ workOn.set(wraparoundDimension);
if (context.reduceDimension(workOn, workOn,
(selected, ignored) -> new WraparoundTransform(
selected.cardinality(),
(sis) 01/02: Convert literal values in advance when possible.
Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 28334378d5da45e67777d03ec785bad409e6eb46
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Jan 26 16:34:21 2026 +0100
Convert literal values in advance when possible.
---
.../org/apache/sis/filter/ComparisonFilter.java | 11 ++
.../main/org/apache/sis/filter/Optimization.java | 116 +++++++++++++++------
.../main/org/apache/sis/filter/base/Node.java | 10 ++
3 files changed, 108 insertions(+), 29 deletions(-)
diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/ComparisonFilter.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/ComparisonFilter.java
index db3a7cf787..4025972525 100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/ComparisonFilter.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/ComparisonFilter.java
@@ -154,6 +154,17 @@ abstract class ComparisonFilter<R> extends BinaryFunctionWidening<R, Object, Obj
return false;
}
+ /**
+ * Whether to convert literals to the same type as non-literal parameters during the optimization phase.
+ * This is invoked by {@link Optimization} for deciding whether to attempt such replacement.
+ *
+ * @return whether it is okay to convert literals in advance.
+ */
+ @Override
+ public final boolean allowLiteralConversions() {
+ return true;
+ }
+
/**
* Tries to optimize this filter. Fist, this method applies the optimization documented
* in the {@linkplain Optimization.OnFilter#optimize default method impmementation}.
diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
index ad8f2c6fc1..8705943a6d 100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
@@ -32,10 +32,14 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.BiPredicate;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.apache.sis.feature.internal.shared.AttributeConvention;
import org.apache.sis.feature.Features;
+import org.apache.sis.feature.internal.shared.FeatureExpression;
+import org.apache.sis.feature.internal.shared.AttributeConvention;
import org.apache.sis.geometry.wrapper.Geometries;
import org.apache.sis.math.FunctionProperty;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.ObjectConverters;
+import org.apache.sis.util.UnconvertibleObjectException;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.collection.Containers;
import org.apache.sis.util.logging.Logging;
@@ -397,6 +401,54 @@ public class Optimization {
return false;
}
+ /**
+ * Returns whether all expressions are literal. In such case, the expression can be evaluated immediately.
+ *
+ * @param effective the expressions optimized by {@link OnFilter} or {@link OnExpression}.
+ * @return whether the given expressions contain only literals.
+ */
+ private static boolean isImmediate(final Expression<?,?>[] effective) {
+ for (int i = effective.length; --i >= 0;) {
+ if (!(effective[i] instanceof Literal<?,?>)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Converts all literal to the same class as result of non-literal parameters.
+ * This is useful for example in {@link ComparisonFilter} for avoiding to perform
+ * the same conversion of literal value each time that the filter is executed.
+ *
+ * @param effective the expressions optimized by {@link OnFilter} or {@link OnExpression}.
+ * @return whether at less one literal has been converted.
+ */
+ private boolean convertLiterals(final Expression<?,?>[] effective) {
+ Class<?> type = null;
+ for (final Expression<?,?> e : effective) {
+ if (e instanceof FeatureExpression<?,?> && !(e instanceof Literal<?,?>)) {
+ type = Classes.findCommonClass(type, ((FeatureExpression<?,?>) e).getResultClass());
+ }
+ }
+ boolean changed = false;
+ if (type != null && type != Object.class) {
+ for (int i = effective.length; --i >= 0;) {
+ Expression<?,?> e = effective[i];
+ if (e instanceof Literal<?,?>) try {
+ Object value = ((Literal<?,?>) e).getValue();
+ if (value != (value = ObjectConverters.convert(value, type))) {
+ effective[i] = literal(value);
+ changed = true;
+ }
+ } catch (UnconvertibleObjectException ex) {
+ warning(ex, false);
+ }
+ }
+ }
+ return changed;
+ }
+
/**
* Returns whether a call to {@code apply(…)} is the first of possibly recursive calls.
* This method shall be invoked in all {@code apply(…)} methods before to do the optimization.
@@ -506,24 +558,27 @@ public class Optimization {
* @return the simplified or optimized filter, or {@code this} if no optimization has been applied.
*/
default Filter<R> optimize(final Optimization optimization) {
- final List<Expression<R,?>> expressions = getExpressions();
@SuppressWarnings({"unchecked", "rawtypes"})
- final Expression<R,?>[] effective = new Expression[expressions.size()];
- boolean unchanged = true; // Will be `false` if at least one optimization has been applied.
- boolean immediate = true;
+ final Expression<R,?>[] effective = getExpressions().toArray(Expression[]::new);
+ Filter<R> optimized = this;
+ boolean changed = false;
for (int i=0; i<effective.length; i++) {
- Expression<R,?> e = expressions.get(i);
- unchanged &= (e == (e = optimization.apply(e)));
- immediate &= (e instanceof Literal<?,?>);
- effective[i] = e;
+ Expression<R,?> e = effective[i];
+ if (e != (e = optimization.apply(e))) {
+ effective[i] = e;
+ changed = true;
+ }
}
- if (immediate) {
- return test(null) ? Filter.include() : Filter.exclude();
- } else if (unchanged) {
- return this;
- } else {
- return recreate(effective);
+ if (this instanceof Node && ((Node) this).allowLiteralConversions()) {
+ changed |= optimization.convertLiterals(effective);
+ }
+ if (changed) {
+ optimized = recreate(effective);
}
+ if (isImmediate(effective)) {
+ return optimized.test(null) ? Filter.include() : Filter.exclude();
+ }
+ return optimized;
}
/**
@@ -681,24 +736,27 @@ public class Optimization {
* @return the simplified or optimized expression, or {@code this} if no optimization has been applied.
*/
default Expression<R, ? extends V> optimize(final Optimization optimization) {
- final List<Expression<R,?>> parameters = getParameters();
@SuppressWarnings({"unchecked", "rawtypes"})
- final Expression<R,?>[] effective = new Expression[parameters.size()];
- boolean unchanged = true; // Will be `false` if at least one optimization has been applied.
- boolean immediate = true;
+ final Expression<R,?>[] effective = getParameters().toArray(Expression[]::new);
+ Expression<R, ? extends V> optimized = this;
+ boolean changed = false;
for (int i=0; i<effective.length; i++) {
- Expression<R,?> e = parameters.get(i);
- unchanged &= (e == (e = optimization.apply(e)));
- immediate &= (e instanceof Literal<?,?>);
- effective[i] = e;
+ Expression<R,?> e = effective[i];
+ if (e != (e = optimization.apply(e))) {
+ effective[i] = e;
+ changed = true;
+ }
}
- if (immediate && !properties(this).contains(FunctionProperty.VOLATILE)) {
- return literal(apply(null));
- } else if (unchanged) {
- return this;
- } else {
- return recreate(effective);
+ if (this instanceof Node && ((Node) this).allowLiteralConversions()) {
+ changed |= optimization.convertLiterals(effective);
+ }
+ if (changed) {
+ optimized = recreate(effective);
+ }
+ if (isImmediate(effective) && !properties(this).contains(FunctionProperty.VOLATILE)) {
+ return literal(optimized.apply(null));
}
+ return optimized;
}
/**
diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/base/Node.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/base/Node.java
index 62e7c65fd8..77de191fd5 100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/base/Node.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/base/Node.java
@@ -315,6 +315,16 @@ public abstract class Node implements Serializable {
return isVolatile(operands) ? TRANSITIVE_PROPERTIES : Set.of();
}
+ /**
+ * Whether to convert literals to the same type as non-literal parameters during the optimization phase.
+ * This is invoked by {@link Optimization} for deciding whether to attempt such replacement.
+ *
+ * @return whether it is okay to convert literals in advance.
+ */
+ public boolean allowLiteralConversions() {
+ return false;
+ }
+
/**
* Returns the children of this node, or an empty collection if none. This is used
* for information purpose, for example in order to build a string representation.