This is an automated email from the ASF dual-hosted git repository. jsorel pushed a commit to branch feat/geometry in repository https://gitbox.apache.org/repos/asf/sis.git
commit f48d2d32b178be6f2ec71040c7331e97f101a7fe Author: jsorel <johann.so...@geomatys.com> AuthorDate: Mon Feb 13 17:00:27 2023 +0100 feat(Geometry): add parent interface for upcoming geometry API --- .../sis/internal/geometry/AttributeType.java | 92 +++++ .../sis/internal/geometry/AttributesType.java | 58 +++ .../org/apache/sis/internal/geometry/Geometry.java | 158 ++++++++ .../org/apache/sis/internal/math/DataType.java | 430 +++++++++++++++++++++ .../org/apache/sis/internal/math/SampleSystem.java | 152 ++++++++ 5 files changed, 890 insertions(+) diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/geometry/AttributeType.java b/core/sis-feature/src/main/java/org/apache/sis/internal/geometry/AttributeType.java new file mode 100644 index 0000000000..4a5c880af1 --- /dev/null +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/geometry/AttributeType.java @@ -0,0 +1,92 @@ +/* + * 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.geometry; + +import org.apache.sis.internal.math.DataType; +import org.apache.sis.internal.math.SampleSystem; + +/** + * + * @author Johann Sorel (Geomatys) + */ +public interface AttributeType { + + /** + * This attribute correspond to SFA coordinates. + * + * GLTF position attribute. + * POSITION,VEC3 + * Unitless XYZ vertex positions + */ + String ATT_POSITION = "POSITION"; + /** + * GLTF normal attribute. + * NORMAL,VEC3 + * Normalized XYZ vertex normals + */ + String ATT_NORMAL = "NORMAL"; + /** + * GLTF tangent attribute. + * TANGENT,VEC4 + * XYZW vertex tangents where the XYZ portion is normalized, + * and the W component is a sign value (-1 or +1) indicating handedness of the tangent basis + */ + String ATT_TANGENT = "TANGENT"; + /** + * GLTF indexed texture coordinate attribute. + * TEXCOORD_n,VEC2 + * ST texture coordinates + */ + String ATT_TEXCOORD = "TEXCOORD"; + /** + * GLTF indexed color attribute. + * COLOR_n,VEC3/VEC4 + * RGB or RGBA vertex color linear multiplier + */ + String ATT_COLOR = "COLOR"; + /** + * GLTF indexed joints attribute. + * JOINTS_n,VEC4 + * Skinned Mesh Attribute + */ + String ATT_JOINTS = "JOINTS"; + /** + * GLTF indexed weights attribute. + * JOINTS_n,VEC4 + * Skinned Mesh Attribute + */ + String ATT_WEIGHTS = "WEIGHTS"; + + /** + * @return attribute name, not null. + */ + String getName(); + + /** + * Returns attribute system for given name. + * + * @return system or null. + */ + SampleSystem getSampleSystem(); + + /** + * Returns attribute type for given name. + * + * @return type or null. + */ + DataType getDataType(); +} diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/geometry/AttributesType.java b/core/sis-feature/src/main/java/org/apache/sis/internal/geometry/AttributesType.java new file mode 100644 index 0000000000..6968e30d56 --- /dev/null +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/geometry/AttributesType.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.geometry; + +import java.util.Collection; +import org.opengis.feature.PropertyNotFoundException; + +/** + * An attributesType is a description of geometry attributes. + * <p> + * Based on specification : + * <ul> + * <li>OGC Simple Feature Access - https://www.ogc.org/standards/sfa</li> + * <li>Khronos GLTF-2 - https://github.com/KhronosGroup/glTF/tree/main/specification/2.0</li> + * </ul> + * + * <p> + * Differences from OGC Simple Feature Access :<br> + * In SFA a single attribute is possible, and exist if method isMeasured returns true.<br> + * Transposed to the GPU model we obtain two possible attributes : POSITION(2D or 3D) and MEASURE(1D). + * + * <p> + * The GPU model as defined by GLTF is more rich and allows any number of attributes.<br> + * Each attribute may itself be composed of 1 to 16 values. + * + * @author Johann Sorel (Geomatys) + */ +public interface AttributesType { + + /** + * @param name searched attribute name + * @return requested attribute type, never null + * @throws PropertyNotFoundException if not found + */ + AttributeType getAttribute(String name) throws PropertyNotFoundException; + + /** + * Returns collection of all attributes. + * + * @return never null, can be empty + */ + Collection<AttributeType> getAttributes(); + +} diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/geometry/Geometry.java b/core/sis-feature/src/main/java/org/apache/sis/internal/geometry/Geometry.java new file mode 100644 index 0000000000..4b699caaff --- /dev/null +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/geometry/Geometry.java @@ -0,0 +1,158 @@ +/* + * 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.geometry; + +import java.util.Map; +import org.opengis.geometry.Envelope; +import org.opengis.referencing.crs.CoordinateReferenceSystem; + +/** + * Parent interface of any geometry. + * <p> + * Based on specification : + * <ul> + * <li>OGC Simple Feature Access - https://www.ogc.org/standards/sfa</li> + * <li>Khronos GLTF-2 - https://github.com/KhronosGroup/glTF/tree/main/specification/2.0</li> + * </ul> + * + * <p> + * Differences from OGC Simple Feature Access : + * <ul> + * <li>SRID : replaced by getCoordinateReferenceSystem</li> + * <li>is3D : look at geometry CoordinateReferenceSystem instead</li> + * <li>isMeasured() : + * SFA defines only a single measure attribute attached to the geometry. + * Khronos/GPU geometries may defined multiple and complex attributes. + * Therefor a dedicated interface AttributesType is defined and accessed with {@linkplain #getAttributesType() }.</li> + * <li>spatial and relation methods : found on GeometryOperations.</li> + * </ul> + * + * <p> + * To be reviewed with upcoming OGC Geometry / ISO-19107. + * + * @author Johann Sorel (Geomatys) + */ +public interface Geometry { + + /** + * Get geometry coordinate system. + * + * @return never null + */ + CoordinateReferenceSystem getCoordinateReferenceSystem(); + + /** + * Set coordinate system in which the coordinates are declared. + * This method does not transform the coordinates. + * + * @param crs , not null + * @Throws IllegalArgumentException if coordinate system is not compatible with geometrie. + */ + void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) throws IllegalArgumentException; + + /** + * Get geometry attributes type. + * + * @return attributes type, never null + */ + AttributesType getAttributesType(); + + /** + * Get the geometry number of dimensions.<br> + * This is the same as coordinate system dimension. + * + * @return number of dimension + */ + default int getDimension() { + return getCoordinateReferenceSystem().getCoordinateSystem().getDimension(); + } + + /** + * Returns the name of the instantiable subtype of Geometry of which this geometric object is an instantiable member.<br> + * The name of the subtype of Geometry is returned as a string. + * + * @see OGC Simple Feature Access 1.2.1 - 6.1.2.2 + * @return geometry subtype name. + */ + String getGeometryType(); + + /** + * The minimum bounding box for this Geometry, returned as a Geometry.<br> + * The polygon is defined by the corner points of the bounding box [(MINX, MINY), (MAXX, MINY), (MAXX, MAXY), (MINX, MAXY), (MINX, MINY)].<br> + * Minimums for Z and M may be added.<br> + * The simplest representation of an Envelope is as two direct positions, one containing all the minimums, and another all the maximums.<br> + * In some cases, this coordinate will be outside the range of validity for the Spatial Reference System. + * + * @see OGC Simple Feature Access 1.2.1 - 6.1.2.2 + * @return Envelope in geometry coordinate reference system. + */ + Envelope getEnvelope(); + + /** + * Exports this geometric object to a specific Well-known Text Representation of Geometry. + * + * @see OGC Simple Feature Access 1.2.1 - 6.1.2.2 + * @return this geometry in Well-known Text + */ + String asText(); + + /** + * Exports this geometric object to a specific Well-known Binary Representation of Geometry. + * + * @see OGC Simple Feature Access 1.2.1 - 6.1.2.2 + * @return this geometry in Well-known Binary + */ + byte[] asBinary(); + + /** + * Returns TRUE if this geometric object is the empty Geometry. + * If true, then this geometric object represents the empty point set ∅ for the coordinate space. + * + * @see OGC Simple Feature Access 1.2.1 - 6.1.2.2 + * @return true if empty. + */ + boolean isEmpty(); + + /** + * Returns TRUE if this geometric object has no anomalous geometric points, such as self intersection or self tangency. + * The description of each instantiable geometric class will include the specific conditions that cause an instance + * of that class to be classified as not simple. + * + * @see OGC Simple Feature Access 1.2.1 - 6.1.2.2 + * @return true if geometry is simple + */ + boolean isSimple(); + + /** + * Returns the closure of the combinatorial boundary of this geometric object (Reference [1], section 3.12.2). + * Because the result of this function is a closure, and hence topologically closed, the resulting boundary can be + * represented using representational Geometry primitives (Reference [1], section 3.12.2). + * + * @see OGC Simple Feature Access 1.2.1 - 6.1.2.2 + * @return boundary of the geometry + */ + Geometry boundary(); + + /** + * Map of properties for user needs. + * Those informations may be lost in geometry processes. + * + * @return Map, can be null if the geometry can not store additional informations. + */ + Map<String,Object> userProperties(); + +} diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/math/DataType.java b/core/sis-feature/src/main/java/org/apache/sis/internal/math/DataType.java new file mode 100644 index 0000000000..ad1b13f3fb --- /dev/null +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/math/DataType.java @@ -0,0 +1,430 @@ +/* + * 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.math; + +import java.awt.image.RasterFormatException; +import org.apache.sis.internal.feature.Resources; +import static org.apache.sis.internal.util.Numerics.MAX_INTEGER_CONVERTIBLE_TO_FLOAT; +import org.apache.sis.measure.NumberRange; +import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.Numbers; + + +/** + * This class is a clone of Apache SIS org.apache.sis.image.DataType. + * But without image type restrictions. + * + * Normalized values definitions can be found at : + * https://www.khronos.org/opengl/wiki/Normalized_Integer + * https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_mesh_quantization/README.md + * + * @author Martin Desruisseaux (Geomatys) + * @author Johann Sorel (Geomatys) + */ +public enum DataType { + + /** + * Signed 8-bits data. + */ + BYTE(0), + + /** + * Unsigned 8-bits data. + */ + UBYTE(1), + + /** + * Signed 16-bits data. + */ + SHORT(2), + + /** + * Unsigned 16-bits data. + */ + USHORT(3), + + /** + * Signed 32-bits data. + */ + INT(4), + + /** + * Unsigned 32-bits data. + */ + UINT(5), + + /** + * Signed 64-bits data. + */ + LONG(6), + + /** + * Single precision (32-bits) floating point data. + */ + FLOAT(7), + + /** + * Double precision (64-bits) floating point data. + */ + DOUBLE(8), + + /** + * Signed 8-bits data interpreted as a decimal in range [-1..1] + */ + NORMALIZED_BYTE(9), + + /** + * Unsigned 8-bits data interpreted as a decimal in range [0..1] + */ + NORMALIZED_UBYTE(10), + + /** + * Signed 16-bits data interpreted as a decimal in range [-1..1] + */ + NORMALIZED_SHORT(11), + + /** + * Unsigned 16-bits data interpreted as a decimal in range [0..1] + */ + NORMALIZED_USHORT(12); + + private final int order; + + /** + * Creates a new enumeration. + */ + private DataType(int order) { + this.order = order; + } + + /** + * Returns the smallest data type capable to store the given range of values. + * If the given range uses a floating point type, there there is a choice: + * + * <ul> + * <li>If {@code asInteger} is {@code false}, then this method returns + * {@link #FLOAT} or {@link #DOUBLE} depending on the range type.</li> + * <li>Otherwise this method treats the floating point values as if they + * were integers, with minimum value rounded toward negative infinity + * and maximum value rounded toward positive infinity.</li> + * </ul> + * + * @param range the range of values. + * @param asInteger whether to handle floating point values as integers. + * @return smallest data type for the given range of values. + */ + public static DataType forRange(final NumberRange<?> range, final boolean asInteger) { + ArgumentChecks.ensureNonNull("range", range); + final byte nt = Numbers.getEnumConstant(range.getElementType()); + if (!asInteger) { + if (nt >= Numbers.DOUBLE) return DOUBLE; + if (nt >= Numbers.FRACTION) return FLOAT; + } + final double min = range.getMinDouble(); + final double max = range.getMaxDouble(); + if (nt < Numbers.BYTE || nt > Numbers.FLOAT || nt == Numbers.LONG) { + /* + * Value type is long, double, BigInteger, BigDecimal or unknown type. + * If conversions to 32 bits integers would lost integer digits, or if + * a bound is NaN, stick to the most conservative data buffer type. + */ + if (!(min >= -MAX_INTEGER_CONVERTIBLE_TO_FLOAT - 0.5 && + max < MAX_INTEGER_CONVERTIBLE_TO_FLOAT + 0.5)) + { + return DOUBLE; + } + } + /* + * Check most common types first. If the range could be both signed and unsigned short, + * give precedence to unsigned values. + * If a bounds is NaN, fallback on TYPE_FLOAT. + */ + final DataType type; + if (min >= -0.5 && max < 0xFF + 0.5) { + type = UBYTE; + } else if (min >= Byte.MIN_VALUE - 0.5 && max < 0xFF + 0.5) { + type = BYTE; + } else if (min >= -0.5 && max < 0xFFFF + 0.5) { + type = USHORT; + } else if (min >= Short.MIN_VALUE - 0.5 && max < Short.MAX_VALUE + 0.5) { + type = SHORT; + } else if (min >= - 0.5 && max < 4294967295L + 0.5) { + type = UINT; + } else if (min >= Integer.MIN_VALUE - 0.5 && max < Integer.MAX_VALUE + 0.5) { + type = INT; + } else if (min >= Long.MIN_VALUE - 0.5 && max < Long.MAX_VALUE + 0.5) { + type = LONG; + } else { + type = FLOAT; + } + return type; + } + + /** + * Returns the data type for the given primitive type. The given {@code type} should be a primitive + * type such as {@link Short#TYPE}, but wrappers class such as {@code Short.class} are also accepted. + * + * @param type the primitive type or its wrapper class. + * @param unsigned whether the type should be considered unsigned. + * @return the data type (never {@code null}) for the given primitive type. + * @throws RasterFormatException if the given type is not a recognized. + */ + public static DataType forPrimitiveType(final Class<?> type, final boolean unsigned) { + switch (Numbers.getEnumConstant(type)) { + case Numbers.BYTE: return unsigned ? UBYTE : BYTE; + case Numbers.SHORT: return unsigned ? USHORT : SHORT; + case Numbers.INTEGER: return unsigned ? UINT : INT; + case Numbers.LONG: return LONG; + case Numbers.FLOAT: return FLOAT; + case Numbers.DOUBLE: return DOUBLE; + } + throw new RasterFormatException(Resources.format(Resources.Keys.UnknownDataType_1, type)); + } + + /** + * Returns the size in bits of this data type. + * + * @return size in bits of this data type. + */ + public int size() { + switch (this) { + case BYTE : + case UBYTE : + return 8; + case SHORT : + case USHORT : + return 16; + case INT : + case UINT : + case FLOAT : + return 32; + case LONG : + case DOUBLE : + return 64; + default : + throw new IllegalStateException("Unexpected type " + this.name()); + } + } + + /** + * Returns whether this type is an unsigned integer type. + * Unsigned types are {@link #UBYTE}, {@link #USHORT} and {@link #UINT}. + * + * @return {@code true} if this type is an unsigned integer type. + */ + public boolean isUnsigned() { + switch (this) { + case UBYTE : + case USHORT : + case UINT : + return true; + default : + return false; + } + } + + /** + * Returns whether this type is an integer type, signed or not. + * + * @return {@code true} if this type is an integer type. + */ + public boolean isInteger() { + switch (this) { + case BYTE : + case UBYTE : + case SHORT : + case USHORT : + case INT : + case UINT : + case LONG : + return true; + default : + return false; + } + } + + /** + * Returns the smallest floating point type capable to store all values of this type + * without precision lost. This method returns: + * + * <ul> + * <li>{@link #DOUBLE} if this data type is {@link #DOUBLE}, {@link #INT}, {@link #UINT} or {@link #LONG}.</li> + * <li>{@link #FLOAT} for all other types.</li> + * </ul> + * + * The promotion of integer values to floating point values is sometime necessary + * when the image may contain {@link Float#NaN} values. + * + * @return the smallest of {@link #FLOAT} or {@link #DOUBLE} types + * which can store all values of this type without any lost. + */ + public DataType toFloat() { + switch (this) { + case INT : + case UINT : + case LONG : + case DOUBLE : + return DOUBLE; + default : + return FLOAT; + } + } + + /** + * Get the widest datatype which may contain both types. + */ + public static DataType largest(DataType type1, DataType type2) { + if (type1.equals(type2)) { + return type1; + } + if (type1.order > type2.order) { + DataType t = type1; + type1 = type2; + type2 = t; + } + + switch (type1) { + case BYTE : + switch (type2) { + case UBYTE : return SHORT; + case SHORT : return SHORT; + case USHORT : return INT; + case INT : return INT; + case UINT : return LONG; + case LONG : return LONG; + case FLOAT : return FLOAT; + case DOUBLE : return DOUBLE; + case NORMALIZED_BYTE : return FLOAT; + case NORMALIZED_UBYTE : return FLOAT; + case NORMALIZED_SHORT : return FLOAT; + case NORMALIZED_USHORT : return FLOAT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case UBYTE : + switch (type2) { + case SHORT : return SHORT; + case USHORT : return USHORT; + case INT : return INT; + case UINT : return UINT; + case LONG : return LONG; + case FLOAT : return FLOAT; + case DOUBLE : return DOUBLE; + case NORMALIZED_BYTE : return FLOAT; + case NORMALIZED_UBYTE : return FLOAT; + case NORMALIZED_SHORT : return FLOAT; + case NORMALIZED_USHORT : return FLOAT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case SHORT : + switch (type2) { + case USHORT : return INT; + case INT : return INT; + case UINT : return LONG; + case LONG : return LONG; + case FLOAT : return FLOAT; + case DOUBLE : return DOUBLE; + case NORMALIZED_BYTE : return FLOAT; + case NORMALIZED_UBYTE : return FLOAT; + case NORMALIZED_SHORT : return FLOAT; + case NORMALIZED_USHORT : return FLOAT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case USHORT : + switch (type2) { + case INT : return INT; + case UINT : return UINT; + case LONG : return LONG; + case FLOAT : return FLOAT; + case DOUBLE : return DOUBLE; + case NORMALIZED_BYTE : return FLOAT; + case NORMALIZED_UBYTE : return FLOAT; + case NORMALIZED_SHORT : return FLOAT; + case NORMALIZED_USHORT : return FLOAT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case INT : + switch (type2) { + case UINT : return LONG; + case LONG : return LONG; + case FLOAT : return FLOAT; + case DOUBLE : return DOUBLE; + case NORMALIZED_BYTE : return FLOAT; + case NORMALIZED_UBYTE : return FLOAT; + case NORMALIZED_SHORT : return FLOAT; + case NORMALIZED_USHORT : return FLOAT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case UINT : + switch (type2) { + case LONG : return LONG; + case FLOAT : return FLOAT; + case DOUBLE : return DOUBLE; + case NORMALIZED_BYTE : return FLOAT; + case NORMALIZED_UBYTE : return FLOAT; + case NORMALIZED_SHORT : return FLOAT; + case NORMALIZED_USHORT : return FLOAT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case LONG : + switch (type2) { + case FLOAT : return DOUBLE; + case DOUBLE : return DOUBLE; + case NORMALIZED_BYTE : return DOUBLE; + case NORMALIZED_UBYTE : return DOUBLE; + case NORMALIZED_SHORT : return DOUBLE; + case NORMALIZED_USHORT : return DOUBLE; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case FLOAT : + switch (type2) { + case DOUBLE : return DOUBLE; + case NORMALIZED_BYTE : return FLOAT; + case NORMALIZED_UBYTE : return FLOAT; + case NORMALIZED_SHORT : return FLOAT; + case NORMALIZED_USHORT : return FLOAT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case DOUBLE : + switch (type2) { + case NORMALIZED_BYTE : return DOUBLE; + case NORMALIZED_UBYTE : return DOUBLE; + case NORMALIZED_SHORT : return DOUBLE; + case NORMALIZED_USHORT : return DOUBLE; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case NORMALIZED_BYTE : + switch (type2) { + case NORMALIZED_UBYTE : return NORMALIZED_SHORT; + case NORMALIZED_SHORT : return NORMALIZED_SHORT; + case NORMALIZED_USHORT : return FLOAT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case NORMALIZED_UBYTE : + switch (type2) { + case NORMALIZED_SHORT : return NORMALIZED_SHORT; + case NORMALIZED_USHORT : return NORMALIZED_USHORT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + case NORMALIZED_SHORT : + switch (type2) { + case NORMALIZED_USHORT : return FLOAT; + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + default : throw new IllegalArgumentException("Unexpected types " + type1 + " " + type2); + } + } +} diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/math/SampleSystem.java b/core/sis-feature/src/main/java/org/apache/sis/internal/math/SampleSystem.java new file mode 100644 index 0000000000..13e45ae582 --- /dev/null +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/math/SampleSystem.java @@ -0,0 +1,152 @@ +/* + * 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.math; + +import java.util.Arrays; +import java.util.Objects; +import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.collection.BackingStoreException; +import org.apache.sis.util.collection.Cache; +import org.apache.sis.util.iso.Names; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.util.GenericName; + +/** + * Experimental class to store multisamples dimensions. + * + * This serves an identical purpose as SampleDimension but usable with geometry attributes. + * + * Waiting for a proper implementation in SIS when reviewing ISO 19123 / 2153. + * + * @author Johann Sorel (Geomatys) + */ +public final class SampleSystem { + + private static final GenericName UNNAMED = Names.createLocalName(null, null, "unnamed"); + private static final SampleSystem UNDEFINED_1S = new SampleSystem(1); + private static final SampleSystem UNDEFINED_2S = new SampleSystem(2); + private static final SampleSystem UNDEFINED_3S = new SampleSystem(3); + private static final SampleSystem UNDEFINED_4S = new SampleSystem(4); + private static SampleSystem[] UNDEFINED = new SampleSystem[0]; + private static Cache<CoordinateReferenceSystem, SampleSystem> CACHE = new Cache<>(); + + private final GenericName name; + private final CoordinateReferenceSystem crs; + private final int size; + + private SampleSystem(int size) { + ArgumentChecks.ensureStrictlyPositive("size", size); + this.name = UNNAMED; + this.crs = null; + this.size = size; + } + + private SampleSystem(CoordinateReferenceSystem crs) { + this(UNNAMED, crs); + } + + private SampleSystem(GenericName name, CoordinateReferenceSystem crs) { + ArgumentChecks.ensureNonNull("name", name); + ArgumentChecks.ensureNonNull("crs", crs); + this.name = name; + this.crs = crs; + this.size = crs.getCoordinateSystem().getDimension(); + } + + public static SampleSystem ofSize(int nbDim) { + ArgumentChecks.ensureStrictlyPositive("nbDim", nbDim); + switch (nbDim) { + case 1 : return UNDEFINED_1S; + case 2 : return UNDEFINED_2S; + case 3 : return UNDEFINED_3S; + case 4 : return UNDEFINED_4S; + default: { + final int idx = nbDim - 4; + synchronized (UNNAMED) { + if (idx >= UNDEFINED.length) { + UNDEFINED = Arrays.copyOf(UNDEFINED, idx+1); + } + if (UNDEFINED[idx] == null) { + UNDEFINED[idx] = new SampleSystem(nbDim); + } + return UNDEFINED[idx]; + } + } + } + } + + public static SampleSystem of(CoordinateReferenceSystem crs) { + ArgumentChecks.ensureNonNull("crs", crs); + try { + return CACHE.getOrCreate(crs, () -> new SampleSystem(crs)); + } catch (Exception ex) { + throw new BackingStoreException(ex.getMessage(), ex); + } + } + + /** + * Returns an identification for this dimension. This is typically used as a way to perform a band select + * by using human comprehensible descriptions instead of just numbers. + * + * @return an identification of this system, nerver null. + */ + public GenericName getName() { + return name; + } + + /** + * Returns the coordinate reference system for this record dimension if it is a coordinate system. + * + * @return can be null. + */ + public CoordinateReferenceSystem getCoordinateReferenceSystem() { + return crs; + } + + /** + * Returns the size in number of samples in this dimension. + * + * @return dimension size + */ + public int getSize() { + return size; + } + + @Override + public int hashCode() { + return Objects.hash(name, crs, size); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final SampleSystem other = (SampleSystem) obj; + if (!Objects.equals(this.name, other.name)) { + return false; + } + return Objects.equals(this.crs, other.crs); + } + +}