Author: desruisseaux
Date: Sun Dec 16 09:55:00 2012
New Revision: 1422492
URL: http://svn.apache.org/viewvc?rev=1422492&view=rev
Log:
Ported the GeneralEnvelope class.
Added:
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
(with props)
Modified:
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
Modified:
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java?rev=1422492&r1=1422491&r2=1422492&view=diff
==============================================================================
---
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
(original)
+++
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
Sun Dec 16 09:55:00 2012
@@ -120,7 +120,7 @@ public abstract class AbstractEnvelope i
if (envelope == null || envelope instanceof AbstractEnvelope) {
return (AbstractEnvelope) envelope;
}
- return null; // TODO new GeneralEnvelope(envelope);
+ return new GeneralEnvelope(envelope);
}
/**
Modified:
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java?rev=1422492&r1=1422491&r2=1422492&view=diff
==============================================================================
---
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
(original)
+++
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
Sun Dec 16 09:55:00 2012
@@ -74,53 +74,15 @@ class ArrayEnvelope extends AbstractEnve
}
/**
- * Constructs one-dimensional envelope defined by a range of values.
+ * Constructs an empty envelope with the specified coordinate reference
system.
+ * All ordinate values are initialized to 0.
*
- * @param lower The lower value.
- * @param upper The upper value.
+ * @param crs The coordinate reference system.
*/
- public ArrayEnvelope(final double lower, final double upper) {
- ordinates = new double[] {lower, upper};
- }
-
- /**
- * Builds a two-dimensional envelope with the specified bounds.
- * The argument order follows the convention used by the <cite>Well Known
Text</cite>
- * format for the {@code BBOX} element: all ordinates for the lower corner
first,
- * followed by all ordinates for the upper corner.
- *
- * @param xmin The lower value for the first ordinate.
- * @param ymin The lower value for the second ordinate.
- * @param xmax The upper value for the first ordinate.
- * @param ymax The upper value for the second ordinate.
- */
- public ArrayEnvelope(final double xmin, final double ymin,
- final double xmax, final double ymax)
- {
- ordinates = new double[] {
- xmin, ymin, xmax, ymax
- };
- }
-
- /**
- * Builds a three-dimensional envelope with the specified bounds.
- * The argument order follows the convention used by the <cite>Well Known
Text</cite>
- * format for the {@code BBOX} element: all ordinates for the lower corner
first,
- * followed by all ordinates for the upper corner.
- *
- * @param xmin The lower value for the first ordinate.
- * @param ymin The lower value for the second ordinate.
- * @param zmin The lower value for the third ordinate.
- * @param xmax The upper value for the first ordinate.
- * @param ymax The upper value for the second ordinate.
- * @param zmax The upper value for the third ordinate.
- */
- public ArrayEnvelope(final double xmin, final double ymin, final double
zmin,
- final double xmax, final double ymax, final double
zmax)
- {
- ordinates = new double[] {
- xmin, ymin, zmin, xmax, ymax, zmax
- };
+ public ArrayEnvelope(final CoordinateReferenceSystem crs) {
+ ensureNonNull("crs", crs);
+ ordinates = new double[crs.getCoordinateSystem().getDimension() * 2];
+ this.crs = crs;
}
/**
@@ -130,7 +92,7 @@ class ArrayEnvelope extends AbstractEnve
* @param upperCorner Upper ordinate values.
* @throws MismatchedDimensionException if the two positions do not have
the same dimension.
*/
- public ArrayEnvelope(final double[] lowerCorner, final double[]
upperCorner) {
+ public ArrayEnvelope(final double[] lowerCorner, final double[]
upperCorner) throws MismatchedDimensionException {
ensureNonNull("lowerCorner", lowerCorner);
ensureNonNull("upperCorner", upperCorner);
ensureSameDimension(lowerCorner.length, upperCorner.length);
@@ -323,7 +285,7 @@ scanNumber: while ((i += Character.charC
* @return The dimensionality of this envelope.
*/
@Override
- public int getDimension() {
+ public final int getDimension() {
return ordinates.length >>> 1;
}
@@ -335,7 +297,7 @@ scanNumber: while ((i += Character.charC
* @return The envelope CRS, or {@code null} if unknown.
*/
@Override
- public CoordinateReferenceSystem getCoordinateReferenceSystem() {
+ public final CoordinateReferenceSystem getCoordinateReferenceSystem() {
assert crs == null || crs.getCoordinateSystem().getDimension() ==
getDimension();
return crs;
}
Added:
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java?rev=1422492&view=auto
==============================================================================
---
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
(added)
+++
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
Sun Dec 16 09:55:00 2012
@@ -0,0 +1,721 @@
+/*
+ * 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.geometry;
+
+import java.util.Arrays;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.geometry.Envelope;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.geometry.MismatchedDimensionException;
+import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.apache.sis.util.resources.Errors;
+
+import static org.apache.sis.util.ArgumentChecks.*;
+import static org.apache.sis.math.MathFunctions.isNegative;
+import static org.apache.sis.math.MathFunctions.isSameSign;
+
+
+/**
+ * A minimum bounding box or rectangle. Regardless of dimension, an {@code
Envelope} can
+ * be represented without ambiguity as two {@linkplain DirectPosition direct
positions}
+ * (coordinate points). To encode an {@code Envelope}, it is sufficient to
encode these
+ * two points.
+ *
+ * {@note <code>Envelope</code> uses an arbitrary <cite>Coordinate Reference
System</cite>, which
+ * doesn't need to be geographic. This is different than the
<code>GeographicBoundingBox</code>
+ * class provided in the metadata package, which can be used as a kind of
envelope restricted to
+ * a Geographic CRS having Greenwich prime meridian.}
+ *
+ * This particular implementation of {@code Envelope} is said "General"
because it uses
+ * coordinates of an arbitrary number of dimensions. This is in contrast with
+ * {@link Envelope2D}, which can use only two-dimensional coordinates.
+ *
+ * <p>A {@code GeneralEnvelope}Â can be created in various ways:</p>
+ * <ul>
+ * <li>{@linkplain #GeneralEnvelope(int) From a given number of dimension},
with all ordinates initialized to 0.</li>
+ * <li>{@linkplain #GeneralEnvelope(GeneralDirectPosition,
GeneralDirectPosition) From two coordinate points}.</li>
+ * <li>{@linkplain #GeneralEnvelope(Envelope) From a an other envelope}
(copy constructor).</li>
+ * <li>{@linkplain #GeneralEnvelope(GeographicBoundingBox) From a geographic
bounding box}.</li>
+ * <li>{@linkplain #GeneralEnvelope(String) From a string} representing a
{@code BBOX} in <cite>Well Known Text</cite> (WKT) format.</li>
+ * </ul>
+ *
+ * {@section Spanning the anti-meridian of a Geographic CRS}
+ * The <cite>Web Coverage Service</cite> (WCS) specification authorizes (with
special treatment)
+ * cases where <var>upper</var> < <var>lower</var> at least in the
longitude case. They are
+ * envelopes crossing the anti-meridian, like the red box below (the green box
is the usual case).
+ * The default implementation of methods listed in the right column can handle
such cases.
+ *
+ * <center><table><tr><td>
+ * <img src="doc-files/AntiMeridian.png">
+ * </td><td>
+ * Supported methods:
+ * <ul>
+ * <li>{@link #getMinimum(int)}</li>
+ * <li>{@link #getMaximum(int)}</li>
+ * <li>{@link #getMedian(int)}</li>
+ * <li>{@link #getSpan(int)}</li>
+ * <li>{@link #isEmpty()}</li>
+ * <li>{@link #contains(DirectPosition)}</li>
+ * <li>{@link #contains(Envelope, boolean)}</li>
+ * <li>{@link #intersects(Envelope, boolean)}</li>
+ * <li>{@link #intersect(Envelope)}</li>
+ * <li>{@link #add(Envelope)}</li>
+ * <li>{@link #add(DirectPosition)}</li>
+ * </ul>
+ * </td></tr></table></center>
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ * @author Johann Sorel (Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ *
+ * @see Envelope2D
+ * @see org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox
+ */
+public class GeneralEnvelope extends ArrayEnvelope implements Cloneable,
Serializable {
+ /**
+ * Serial number for inter-operability with different versions.
+ */
+ private static final long serialVersionUID = 1752330560227688940L;
+
+ /**
+ * Used for setting the {@link #ordinates} field during a {@link #clone()}
operation only.
+ * Will be fetch when first needed.
+ */
+ private static volatile Field ordinatesField;
+
+ /**
+ * Constructs an empty envelope of the specified dimension. All ordinates
+ * are initialized to 0 and the coordinate reference system is undefined.
+ *
+ * @param dimension The envelope dimension.
+ */
+ public GeneralEnvelope(final int dimension) {
+ super(dimension);
+ }
+
+ /**
+ * Constructs an empty envelope with the specified coordinate reference
system.
+ * All ordinate values are initialized to 0.
+ *
+ * @param crs The coordinate reference system.
+ */
+ public GeneralEnvelope(final CoordinateReferenceSystem crs) {
+ super(crs);
+ }
+
+ /**
+ * Constructs a envelope defined by two positions.
+ *
+ * @param lowerCorner Lower ordinate values.
+ * @param upperCorner Upper ordinate values.
+ * @throws MismatchedDimensionException if the two positions do not have
the same dimension.
+ */
+ public GeneralEnvelope(final double[] lowerCorner, final double[]
upperCorner) throws MismatchedDimensionException {
+ super(lowerCorner, upperCorner);
+ }
+
+ /**
+ * Constructs a new envelope with the same data than the specified
envelope.
+ *
+ * @param envelope The envelope to copy.
+ *
+ * @see Envelope2D#Envelope2D(Envelope)
+ */
+ public GeneralEnvelope(final Envelope envelope) {
+ super(envelope);
+ }
+
+ /**
+ * Constructs a new envelope with the same data than the specified
geographic bounding box.
+ * The coordinate reference system is set to {@code "CRS:84"}.
+ *
+ * @param box The bounding box to copy.
+ *
+ * @see Envelope2D#Envelope2D(GeographicBoundingBox)
+ */
+ public GeneralEnvelope(final GeographicBoundingBox box) {
+ super(box);
+ }
+
+ /**
+ * Constructs a new envelope initialized to the values parsed from the
given string in
+ * <cite>Well Known Text</cite> (WKT) format. The given string is
typically a {@code BOX}
+ * element like below:
+ *
+ * {@preformat wkt
+ * BOX(-180 -90, 180 90)
+ * }
+ *
+ * However this constructor is lenient to other geometry types like {@code
POLYGON}.
+ * Actually this constructor ignores the geometry type and just applies
the following
+ * simple rules:
+ *
+ * <ul>
+ * <li>Character sequences complying to the rules of Java identifiers
are skipped.</li>
+ * <li>Coordinates are separated by a coma ({@code ,}) character.</li>
+ * <li>The ordinates in a coordinate are separated by a space.</li>
+ * <li>Ordinate numbers are assumed formatted in US locale.</li>
+ * <li>The coordinate having the highest dimension determines the
dimension of this envelope.</li>
+ * </ul>
+ *
+ * This constructor does not check the consistency of the provided WKT.
For example it doesn't
+ * check that every points in a {@code LINESTRING} have the same
dimension. However this
+ * constructor ensures that the parenthesis are balanced, in order to
catch some malformed WKT.
+ *
+ * <p>The following examples can be parsed by this constructor in addition
of the standard
+ * {@code BOX} element. This constructor creates the bounding box of those
geometries:</p>
+ *
+ * <ul>
+ * <li>{@code POINT(6 10)}</li>
+ * <li>{@code MULTIPOLYGON(((1 1, 5 1, 1 5, 1 1),(2 2, 3 2, 3 3, 2
2)))}</li>
+ * <li>{@code GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(3 8,7 10))}</li>
+ * </ul>
+ *
+ * @param wkt The {@code BOX}, {@code POLYGON} or other kind of element
to parse.
+ * @throws IllegalArgumentException If the given string can not be parsed.
+ *
+ * @see Envelopes#parseWKT(String)
+ * @see Envelopes#toWKT(Envelope)
+ */
+ public GeneralEnvelope(final String wkt) throws IllegalArgumentException {
+ super(wkt);
+ }
+
+ /**
+ * Returns the given envelope as a {@code GeneralEnvelope} instance. If
the given envelope
+ * is already an instance of {@code GeneralEnvelope}, then it is returned
unchanged.
+ * Otherwise the coordinate values and the CRS of the given envelope are
+ * {@linkplain #GeneralEnvelope(Envelope) copied} in a new {@code
GeneralEnvelope}.
+ *
+ * @param envelope The envelope to cast, or {@code null}.
+ * @return The values of the given envelope as a {@code GeneralEnvelope}
instance.
+ *
+ * @see AbstractEnvelope#castOrCopy(Envelope)
+ */
+ public static GeneralEnvelope castOrCopy(final Envelope envelope) {
+ if (envelope == null || envelope instanceof GeneralEnvelope) {
+ return (GeneralEnvelope) envelope;
+ }
+ return new GeneralEnvelope(envelope);
+ }
+
+ /**
+ * Sets the coordinate reference system in which the coordinate are given.
+ * This method <strong>does not</strong> reproject the envelope, and do not
+ * check if the envelope is contained in the new domain of validity.
+ *
+ * <p>If the envelope coordinates need to be transformed to the new CRS,
consider
+ * using {@link Envelopes#transform(Envelope, CoordinateReferenceSystem)}
instead.</p>
+ *
+ * @param crs The new coordinate reference system, or {@code null}.
+ * @throws MismatchedDimensionException if the specified CRS doesn't have
the expected
+ * number of dimensions.
+ */
+ public void setCoordinateReferenceSystem(final CoordinateReferenceSystem
crs)
+ throws MismatchedDimensionException
+ {
+ AbstractDirectPosition.ensureDimensionMatch(crs, getDimension());
+ this.crs = crs;
+ }
+
+ /**
+ * Sets the envelope range along the specified dimension.
+ *
+ * @param dimension The dimension to set.
+ * @param minimum The minimum value along the specified dimension.
+ * @param maximum The maximum value along the specified dimension.
+ * @throws IndexOutOfBoundsException If the given index is out of bounds.
+ */
+ public void setRange(final int dimension, final double minimum, final
double maximum)
+ throws IndexOutOfBoundsException
+ {
+ final int d = ordinates.length >>> 1;
+ ensureValidIndex(d, dimension);
+ ordinates[dimension + d] = maximum;
+ ordinates[dimension] = minimum;
+ }
+
+ /**
+ * Sets the envelope to the specified values, which must be the lower
corner coordinates
+ * followed by upper corner coordinates. The number of arguments provided
shall be twice
+ * this {@linkplain #getDimension envelope dimension}, and minimum shall
not be greater
+ * than maximum.
+ *
+ * <p><b>Example:</b></p>
+ * (<var>x</var><sub>min</sub>, <var>y</var><sub>min</sub>,
<var>z</var><sub>min</sub>,
+ * <var>x</var><sub>max</sub>, <var>y</var><sub>max</sub>,
<var>z</var><sub>max</sub>)
+ *
+ * @param ordinates The new ordinate values.
+ */
+ public void setEnvelope(final double... ordinates) {
+ if ((ordinates.length & 1) != 0) {
+ throw new IllegalArgumentException(Errors.format(
+ Errors.Keys.OddArrayLength_1, ordinates.length));
+ }
+ final int dimension = ordinates.length >>> 1;
+ final int check = this.ordinates.length >>> 1;
+ if (dimension != check) {
+ throw new MismatchedDimensionException(Errors.format(
+ Errors.Keys.MismatchedDimension_3, "ordinates", dimension,
check));
+ }
+ System.arraycopy(ordinates, 0, this.ordinates, 0, ordinates.length);
+ }
+
+ /**
+ * Sets this envelope to the same coordinate values than the specified
envelope.
+ * If the given envelope has a non-null Coordinate Reference System (CRS),
then
+ * the CRS of this envelope will be set to the CRS of the given envelope.
+ *
+ * @param envelope The envelope to copy coordinates from.
+ * @throws MismatchedDimensionException if the specified envelope doesn't
have
+ * the expected number of dimensions.
+ */
+ public void setEnvelope(final Envelope envelope) throws
MismatchedDimensionException {
+ ensureNonNull("envelope", envelope);
+ final int dimension = ordinates.length >>> 1;
+ AbstractDirectPosition.ensureDimensionMatch("envelope",
envelope.getDimension(), dimension);
+ if (envelope instanceof ArrayEnvelope) {
+ System.arraycopy(((ArrayEnvelope) envelope).ordinates, 0,
ordinates, 0, ordinates.length);
+ } else {
+ final DirectPosition lower = envelope.getLowerCorner();
+ final DirectPosition upper = envelope.getUpperCorner();
+ for (int i=0; i<dimension; i++) {
+ ordinates[i] = lower.getOrdinate(i);
+ ordinates[i+dimension] = upper.getOrdinate(i);
+ }
+ }
+ final CoordinateReferenceSystem envelopeCRS =
envelope.getCoordinateReferenceSystem();
+ if (envelopeCRS != null) {
+ crs = envelopeCRS;
+ assert crs.getCoordinateSystem().getDimension() == getDimension()
: crs;
+ assert envelope.getClass() != getClass() || equals(envelope) :
envelope;
+ }
+ }
+
+ /**
+ * Sets the lower corner to {@linkplain Double#NEGATIVE_INFINITY negative
infinity}
+ * and the upper corner to {@linkplain Double#POSITIVE_INFINITY positive
infinity}.
+ * The {@linkplain #getCoordinateReferenceSystem() coordinate reference
system}
+ * (if any) stay unchanged.
+ */
+ public void setToInfinite() {
+ final int mid = ordinates.length >>> 1;
+ Arrays.fill(ordinates, 0, mid,
Double.NEGATIVE_INFINITY);
+ Arrays.fill(ordinates, mid, ordinates.length,
Double.POSITIVE_INFINITY);
+ assert isInfinite() : this;
+ }
+
+ /**
+ * Returns {@code true} if at least one ordinate has an
+ * {@linkplain Double#isInfinite infinite} value.
+ *
+ * @return {@code true} if this envelope has infinite value.
+ */
+ public boolean isInfinite() {
+ for (int i=0; i<ordinates.length; i++) {
+ if (Double.isInfinite(ordinates[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets all ordinate values to {@linkplain Double#NaN NaN}.
+ * The {@linkplain #getCoordinateReferenceSystem() coordinate reference
system}
+ * (if any) stay unchanged.
+ *
+ * @see #isNull()
+ */
+ public void setToNull() {
+ Arrays.fill(ordinates, Double.NaN);
+ assert isNull() : this;
+ }
+
+ /**
+ * Adds to this envelope a point of the given array.
+ * This method does not check for anti-meridian spanning. It is invoked
only
+ * by the {@link Envelopes} transform methods, which build "normal"
envelopes.
+ *
+ * @param array The array which contains the ordinate values.
+ * @param offset Index of the first valid ordinate value in the given
array.
+ */
+ final void add(final double[] array, final int offset) {
+ final int dim = ordinates.length >>> 1;
+ for (int i=0; i<dim; i++) {
+ final double value = array[offset + i];
+ if (value < ordinates[i ]) ordinates[i ] = value;
+ if (value > ordinates[i+dim]) ordinates[i+dim] = value;
+ }
+ }
+
+ /**
+ * Adds a point to this envelope. The resulting envelope is the smallest
envelope that
+ * contains both the original envelope and the specified point.
+ *
+ * <p>After adding a point, a call to {@link #contains(DirectPosition)
contains(DirectPosition)}
+ * with the added point as an argument will return {@code true}, except if
one of the point
+ * ordinates was {@link Double#NaN} in which case the corresponding
ordinate has been ignored.</p>
+ *
+ * {@note This method assumes that the specified point uses the same CRS
than this envelope.
+ * For performance raisons, it will no be verified unless Java
assertions are enabled.}
+ *
+ * {@section Spanning the anti-meridian of a Geographic CRS}
+ * This method supports envelopes spanning the anti-meridian. In such
cases it is possible to
+ * move both envelope borders in order to encompass the given point, as
illustrated below (the
+ * new point is represented by the {@code +} symbol):
+ *
+ * {@preformat text
+ * ââââââ + ââââââ
+ * ââââââ ââââââ
+ * }
+ *
+ * The default implementation moves only the border which is closest to
the given point.
+ *
+ * @param position The point to add.
+ * @throws MismatchedDimensionException if the specified point doesn't
have the expected dimension.
+ * @throws AssertionError If assertions are enabled and the envelopes have
mismatched CRS.
+ */
+ public void add(final DirectPosition position) throws
MismatchedDimensionException {
+ ensureNonNull("position", position);
+ final int dim = ordinates.length >>> 1;
+ AbstractDirectPosition.ensureDimensionMatch("position",
position.getDimension(), dim);
+ assert equalsIgnoreMetadata(crs,
position.getCoordinateReferenceSystem(), true) : position;
+ for (int i=0; i<dim; i++) {
+ final double value = position.getOrdinate(i);
+ final double min = ordinates[i];
+ final double max = ordinates[i+dim];
+ if (!isNegative(max - min)) { // Standard case, or NaN.
+ if (value < min) ordinates[i ] = value;
+ if (value > max) ordinates[i+dim] = value;
+ } else {
+ /*
+ * Spanning the anti-meridian. The [maxâ¦min] range (not that
min/max are
+ * interchanged) is actually an exclusion area. Changes only
the closest
+ * side.
+ */
+ addToClosest(i, value, max, min);
+ }
+ }
+ assert contains(position) || isEmpty() || hasNaN(position) : position;
+ }
+
+ /**
+ * Invoked when a point is added to a range spanning the anti-meridian.
+ * In the example below, the new point is represented by the {@code +}
+ * symbol. The point is added only on the closest side.
+ *
+ * {@preformat text
+ * ââââââ + ââââââ
+ * ââââââ ââââââ
+ * }
+ *
+ * @param i The dimension of the ordinate
+ * @param value The ordinate value to add to this envelope.
+ * @param left The border on the left side, which is the <em>max</em>
value (yes, this is confusing!)
+ * @param right The border on the right side, which is the <em>min</em>
value (yes, this is confusing!)
+ */
+ private void addToClosest(int i, final double value, double left, double
right) {
+ left = value - left;
+ if (left > 0) {
+ right -= value;
+ if (right > 0) {
+ if (right > left) {
+ i += (ordinates.length >>> 1);
+ }
+ ordinates[i] = value;
+ }
+ }
+ }
+
+ /**
+ * Adds an envelope object to this envelope. The resulting envelope is the
union of the
+ * two {@code Envelope} objects.
+ *
+ * {@note This method assumes that the specified envelope uses the same
CRS than this envelope.
+ * For performance raisons, it will no be verified unless Java
assertions are enabled.}
+ *
+ * {@section Spanning the anti-meridian of a Geographic CRS}
+ * This method supports envelopes spanning the anti-meridian. If one or
both envelopes span
+ * the anti-meridian, then the result of the {@code add} operation may be
an envelope expanding
+ * to infinities. In such case, the ordinate range will be either
[-ââ¦â] or [0â¦-0] depending on
+ * whatever the original range span the anti-meridian or not.
+ *
+ * @param envelope the {@code Envelope} to add to this envelope.
+ * @throws MismatchedDimensionException if the specified envelope doesn't
+ * have the expected dimension.
+ * @throws AssertionError If assertions are enabled and the envelopes have
mismatched CRS.
+ */
+ public void add(final Envelope envelope) throws
MismatchedDimensionException {
+ ensureNonNull("envelope", envelope);
+ final int dim = ordinates.length >>> 1;
+ AbstractDirectPosition.ensureDimensionMatch("envelope",
envelope.getDimension(), dim);
+ assert equalsIgnoreMetadata(crs,
envelope.getCoordinateReferenceSystem(), true) : envelope;
+ final DirectPosition lower = envelope.getLowerCorner();
+ final DirectPosition upper = envelope.getUpperCorner();
+ for (int i=0; i<dim; i++) {
+ final double min0 = ordinates[i];
+ final double max0 = ordinates[i+dim];
+ final double min1 = lower.getOrdinate(i);
+ final double max1 = upper.getOrdinate(i);
+ final boolean sp0 = isNegative(max0 - min0);
+ final boolean sp1 = isNegative(max1 - min1);
+ if (sp0 == sp1) {
+ /*
+ * Standard case (for rows in the above pictures), or case
where both envelopes
+ * span the anti-meridian (which is almost the same with an
additional post-add
+ * check).
+ * ââââââââââââ
ââââââââââââ
+ * â ââââââ â or â
âââââââââ¼âââ
+ * â ââââââ â â
âââââââââ¼âââ
+ * ââââââââââââ
ââââââââââââ
+ *
+ * âââââ âââââ
âââââ âââââ
+ * âââ â â âââ or
âââââ¼âââ¼ââââ
+ * âââ â â âââ
âââââ¼âââ¼ââââ
+ * âââââ âââââ
âââââ âââââ
+ */
+ if (min1 < min0) ordinates[i ] = min1;
+ if (max1 > max0) ordinates[i+dim] = max1;
+ if (!sp0 || isNegativeUnsafe(ordinates[i+dim] - ordinates[i]))
{
+ continue; // We are done, go to the next dimension.
+ }
+ // If we were spanning the anti-meridian before the union but
+ // are not anymore after the union, we actually merged to two
+ // sides, so the envelope is spanning to infinities. The code
+ // close to the end of this loop will set an infinite range.
+ } else if (sp0) {
+ /*
+ * Only this envelope spans the anti-meridian; the given
envelope is normal or
+ * has NaN values. First we need to exclude the cases were
the given envelope
+ * is fully included in this envelope:
+ * âââââââââââ ââââââ
+ * ââââââ â â
+ * ââââââ â â
+ * âââââââââââ ââââââ
+ */
+ if (max1 <= max0) continue; // This is the case of above
picture.
+ if (min1 >= min0) continue; // Like above picture, but on the
right side.
+ /*
+ * At this point, the given envelope partially overlaps the
"exclusion area"
+ * of this envelope or has NaN values. We will move at most
one edge of this
+ * envelope, in order to leave as much free space as possible.
+ * ââââââ ââââââ
+ * âââ¼âââââ â
+ * âââ¼âââââ â
+ * ââââââ ââââââ
+ */
+ final double left = min1 - max0;
+ final double right = min0 - max1;
+ if (left > 0 || right > 0) {
+ // The < and > checks below are not completly redundant.
+ // The difference is when a value is NaN.
+ if (left > right) ordinates[i ] = min1;
+ if (right > left) ordinates[i+dim] = max1; // This is the
case illustrated above.
+ continue; // We are done, go to the next dimension.
+ }
+ // If we reach this point, the given envelope fills completly
the "exclusion area"
+ // of this envelope. As a consequence this envelope is now
spanning to infinities.
+ // We will set that fact close to the end of this loop.
+ } else {
+ /*
+ * Opposite of above case: this envelope is "normal" or has
NaN values, and the
+ * given envelope spans to infinities.
+ */
+ if (max0 <= max1 || min0 >= min1) {
+ ordinates[i] = min1;
+ ordinates[i+dim] = max1;
+ continue;
+ }
+ final double left = min0 - max1;
+ final double right = min1 - max0;
+ if (left > 0 || right > 0) {
+ if (left > right) ordinates[i+dim] = max1;
+ if (right > left) ordinates[i ] = min1;
+ continue;
+ }
+ }
+ /*
+ * If we reach that point, we went in one of the many cases where
the envelope
+ * has been expanded to infinity. Declares an infinite range
while preserving
+ * the "normal" / "anti-meridian spanning" state.
+ */
+ if (sp0) {
+ ordinates[i ] = +0.0;
+ ordinates[i+dim] = -0.0;
+ } else {
+ ordinates[i ] = Double.NEGATIVE_INFINITY;
+ ordinates[i+dim] = Double.POSITIVE_INFINITY;
+ }
+ }
+ assert contains(envelope, true) || isEmpty() || hasNaN(envelope) :
this;
+ }
+
+ /**
+ * Sets this envelope to the intersection if this envelope with the
specified one.
+ *
+ * {@note This method assumes that the specified envelope uses the same
CRS than this envelope.
+ * For performance raisons, it will no be verified unless Java
assertions are enabled.}
+ *
+ * {@section Spanning the anti-meridian of a Geographic CRS}
+ * This method supports envelopes spanning the anti-meridian.
+ *
+ * @param envelope the {@code Envelope} to intersect to this envelope.
+ * @throws MismatchedDimensionException if the specified envelope doesn't
+ * have the expected dimension.
+ * @throws AssertionError If assertions are enabled and the envelopes have
mismatched CRS.
+ */
+ public void intersect(final Envelope envelope) throws
MismatchedDimensionException {
+ ensureNonNull("envelope", envelope);
+ final int dim = ordinates.length >>> 1;
+ AbstractDirectPosition.ensureDimensionMatch("envelope",
envelope.getDimension(), dim);
+ assert equalsIgnoreMetadata(crs,
envelope.getCoordinateReferenceSystem(), true) : envelope;
+ final DirectPosition lower = envelope.getLowerCorner();
+ final DirectPosition upper = envelope.getUpperCorner();
+ for (int i=0; i<dim; i++) {
+ final double min0 = ordinates[i];
+ final double max0 = ordinates[i+dim];
+ final double min1 = lower.getOrdinate(i);
+ final double max1 = upper.getOrdinate(i);
+ final double span0 = max0 - min0;
+ final double span1 = max1 - min1;
+ if (isSameSign(span0, span1)) { // Always 'false' if any value is
NaN.
+ /*
+ * First, verify that the two envelopes intersect.
+ * ââââââââââââ
âââââââââââââââ
+ * â âââââââââ¼âââ or â
âââââââââ â
+ * â âââââââââ¼âââ â
âââââââââ â
+ * ââââââââââââ
âââââââââââââââ
+ */
+ if ((min1 > max0 || max1 < min0) && !isNegativeUnsafe(span0)) {
+ /*
+ * The check for !isNegative(span0) is because if both
envelopes span the
+ * anti-merdian, then there is always an intersection on
both side no matter
+ * what envelope ordinates are because both envelopes
extend toward infinities:
+ * âââââ âââââ
âââââ âââââ
+ * âââ â â âââ or
âââââ¼âââ¼ââââ
+ * âââ â â âââ
âââââ¼âââ¼ââââ
+ * âââââ âââââ
âââââ âââââ
+ * Since we excluded the above case, entering in this
block means that the
+ * envelopes are "normal" and do not intersect, so we set
ordinates to NaN.
+ * ââââââ
+ * â â ââââââ
+ * â â ââââââ
+ * ââââââ
+ */
+ ordinates[i] = ordinates[i+dim] = Double.NaN;
+ continue;
+ }
+ } else {
+ int intersect = 0; // A bitmask of intersections (two bits).
+ if (!Double.isNaN(span0) && !Double.isNaN(span1)) {
+ if (isNegativeUnsafe(span0)) {
+ /*
+ * The first line below checks for the case
illustrated below. The second
+ * line does the same check, but with the small
rectangle on the right side.
+ * ââââââ ââââââ
âââââââââââ ââââââ
+ * âââ¼âââââ â or
ââââââ â â
+ * âââ¼âââââ â
ââââââ â â
+ * ââââââ ââââââ
âââââââââââ ââââââ
+ */
+ if (min1 <= max0) {intersect = 1; ordinates[i ] =
min1;}
+ if (max1 >= min0) {intersect |= 2; ordinates[i+dim] =
max1;}
+ } else {
+ // Same than above, but with indices 0 and 1
interchanged.
+ // No need to set ordinate values since they would be
the same.
+ if (min0 <= max1) {intersect = 1;}
+ if (max0 >= min1) {intersect |= 2;}
+ }
+ }
+ /*
+ * Cases 0 and 3 are illustrated below. In case 1 and 2, we
will set
+ * only the ordinate value which has not been set by the above
code.
+ *
+ * [intersect=0] [intersect=3]
+ * ââââââ ââââââ
ââââââ ââââââ
+ * negative: max0â âââ âmin0
âââ¼ââââââ¼ââ
+ * â âââ â
âââ¼ââââââ¼ââ
+ * ââââââ ââââââ
ââââââ ââââââ
+ *
+ * max1 âââ min1
âââââââââââ
+ * positive: ââââââ â â ââââââ
ââââ¼ââ âââ¼âââ
+ * ââââââ â â ââââââ
ââââ¼ââ âââ¼âââ
+ * âââ
âââââââââââ
+ */
+ switch (intersect) {
+ default: throw new AssertionError(intersect);
+ case 1: if (max1 < max0) ordinates[i+dim] = max1; break;
+ case 2: if (min1 > min0) ordinates[i ] = min1; break;
+ case 3: // Fall through
+ case 0: {
+ // Before to declare the intersection as invalid,
verify if the envelope
+ // actually span the whole Earth. In such case, the
intersection is a no-
+ // operation (or a copy operation).
+ final double min, max;
+ final double csSpan = getSpan(getAxis(crs, i));
+ if (span1 >= csSpan) {
+ min = min0;
+ max = max0;
+ } else if (span0 >= csSpan) {
+ min = min1;
+ max = max1;
+ } else {
+ min = Double.NaN;
+ max = Double.NaN;
+ }
+ ordinates[i] = min;
+ ordinates[i+dim] = max;
+ break;
+ }
+ }
+ continue;
+ }
+ if (min1 > min0) ordinates[i ] = min1;
+ if (max1 < max0) ordinates[i+dim] = max1;
+ }
+ // Tests only if the interection result is non-empty.
+ assert isEmpty() ||
AbstractEnvelope.castOrCopy(envelope).contains(this, true) : this;
+ }
+
+ /**
+ * Returns a deep copy of this envelope.
+ *
+ * @return A clone of this envelope.
+ */
+ @Override
+ public GeneralEnvelope clone() {
+ try {
+ Field field = ordinatesField;
+ if (field == null) {
+ field = ArrayEnvelope.class.getDeclaredField("ordinates");
+ field.setAccessible(true);
+ ordinatesField = field;
+ }
+ GeneralEnvelope e = (GeneralEnvelope) super.clone();
+ field.set(e, ordinates.clone());
+ return e;
+ } catch (CloneNotSupportedException | ReflectiveOperationException
exception) {
+ // Should not happen, since we are cloneable, the
+ // field is known to exist and we made it accessible.
+ throw new AssertionError(exception);
+ }
+ }
+}
Propchange:
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sis/branches/JDK7/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1422492&r1=1422491&r2=1422492&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
(original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
Sun Dec 16 09:55:00 2012
@@ -276,6 +276,11 @@ public final class Errors extends Indexe
public static final int NullArgument_1 = 0;
/**
+ * Array length is {0}, while we expected an even length.
+ */
+ public static final int OddArrayLength_1 = 61;
+
+ /**
* Recursive call while creating an object for the â{0}â key.
*/
public static final int RecursiveCreateCallForKey_1 = 18;
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1422492&r1=1422491&r2=1422492&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
(original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
Sun Dec 16 09:55:00 2012
@@ -58,6 +58,7 @@ NonTemporalUnit_1 = \u201c
NotANumber_1 = Argument \u2018{0}\u2019 shall not be NaN
(Not-a-Number).
NotAPrimitiveWrapper_1 = Class \u2018{0}\u2019 is not a primitive
type wrapper.
NullArgument_1 = Argument \u2018{0}\u2019 shall not be null.
+OddArrayLength_1 = Array length is {0}, while we expected an
even length.
RecursiveCreateCallForKey_1 = Recursive call while creating an object for
the \u201c{0}\u201d key.
RequireDecimalSeparator = A decimal separator is required.
UnexpectedArgumentDimension_3 = Argument \u2018{0}\u2019 has {1} dimensions,
while {2} was expected.
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1422492&r1=1422491&r2=1422492&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
(original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
Sun Dec 16 09:55:00 2012
@@ -58,6 +58,7 @@ NonTemporalUnit_1 = \u201c
NotANumber_1 = L\u2019argument \u2018{0}\u2019 ne doit pas
\u00eatre NaN (Not-a-Number).
NotAPrimitiveWrapper_1 = La classe \u2018{0}\u2019 n\u2019est pas un
adaptateur d\u2019un type primitif.
NullArgument_1 = L\u2019argument \u2018{0}\u2019 ne doit pas
\u00eatre nul.
+OddArrayLength_1 = La longueur du tableau est {0}, alors
qu\u2019on attendait une longueur paire.
RecursiveCreateCallForKey_1 = Appel r\u00e9cursif lors de la cr\u00e9ation
d\u2019un objet pour la cl\u00e9 \u201c{0}\u201d.
RequireDecimalSeparator = Un s\u00e9parateur d\u00e9cimal est requis.
UnexpectedArgumentDimension_3 = L\u2019argument \u2018{0}\u2019 a {1}
dimensions, alors qu\u2019on en attendait {2}.