use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class GeneralEnvelope method simplify.
/**
* Ensures that <var>lower</var> <= <var>upper</var> for every dimensions.
* If a {@linkplain #getUpper(int) upper ordinate value} is less than a
* {@linkplain #getLower(int) lower ordinate value}, then there is a choice:
*
* <ul>
* <li>If the axis has {@link RangeMeaning#WRAPAROUND}, then:<ul>
* <li>the lower ordinate value is set to the {@linkplain CoordinateSystemAxis#getMinimumValue() axis minimum value}, and</li>
* <li>the upper ordinate value is set to the {@linkplain CoordinateSystemAxis#getMaximumValue() axis maximum value}.</li>
* </ul></li>
* <li>Otherwise an {@link IllegalStateException} is thrown.</li>
* </ul>
*
* This method is useful when the envelope needs to be used with libraries that do not support
* envelopes spanning the anti-meridian.
*
* @return {@code true} if this envelope has been modified as a result of this method call,
* or {@code false} if no change has been done.
* @throws IllegalStateException if a upper ordinate value is less than a lower ordinate
* value on an axis which does not have the {@code WRAPAROUND} range meaning.
*
* @see #toSimpleEnvelopes()
*/
public boolean simplify() throws IllegalStateException {
boolean changed = false;
final int d = ordinates.length >>> 1;
final int beginIndex = beginIndex();
final int dimension = endIndex() - beginIndex;
for (int i = 0; i < dimension; i++) {
final int iLower = beginIndex + i;
final int iUpper = iLower + d;
final double lower = ordinates[iLower];
final double upper = ordinates[iUpper];
if (isNegative(upper - lower)) {
final CoordinateSystemAxis axis = getAxis(crs, i);
if (isWrapAround(axis)) {
ordinates[iLower] = axis.getMinimumValue();
ordinates[iUpper] = axis.getMaximumValue();
changed = true;
} else {
throw new IllegalStateException(Errors.format(Errors.Keys.IllegalOrdinateRange_3, lower, upper, (axis != null) ? axis.getName() : i));
}
}
}
return changed;
}
use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class GeneralEnvelope method normalize.
/**
* Normalizes only the dimensions returned by the given iterator, or all dimensions if the iterator is null.
* This is used for normalizing the result of a coordinate operation where a wrap around axis does not
* necessarily means that the ordinates need to be normalized along that axis.
*
* @param cs the coordinate system of this envelope CRS (as an argument because sometime already known).
* @param beginIndex index of the first ordinate value in {@link #ordinates} array. Non-zero for sub-envelopes.
* @param count number of coordinates, i.e. this envelope dimensions.
* @param dimensions the dimensions to check for normalization, or {@code null} for all dimensions.
* @return {@code true} if this envelope has been modified as a result of this method call.
*/
final boolean normalize(final CoordinateSystem cs, final int beginIndex, final int count, final Iterator<Integer> dimensions) {
boolean changed = false;
final int d = ordinates.length >>> 1;
for (int j = 0; j < count; j++) {
final int i = (dimensions != null) ? dimensions.next() : j;
final int iLower = beginIndex + i;
final int iUpper = iLower + d;
final CoordinateSystemAxis axis = cs.getAxis(i);
final double minimum = axis.getMinimumValue();
final double maximum = axis.getMaximumValue();
final RangeMeaning rm = axis.getRangeMeaning();
if (RangeMeaning.EXACT.equals(rm)) {
if (ordinates[iLower] < minimum) {
ordinates[iLower] = minimum;
changed = true;
}
if (ordinates[iUpper] > maximum) {
ordinates[iUpper] = maximum;
changed = true;
}
} else if (RangeMeaning.WRAPAROUND.equals(rm)) {
final double csSpan = maximum - minimum;
if (csSpan > 0 && csSpan < Double.POSITIVE_INFINITY) {
double o1 = ordinates[iLower];
double o2 = ordinates[iUpper];
if (Math.abs(o2 - o1) >= csSpan) {
/*
* If the range exceed the CS span, then we have to replace it by the
* full span, otherwise the range computed by the "else" block is too
* small. The full range will typically be [-180 … 180]°. However we
* make a special case if the two bounds are multiple of the CS span,
* typically [0 … 360]°. In this case the [0 … -0]° range matches the
* original values and is understood by GeneralEnvelope as a range
* spanning all the world.
*/
if (o1 != minimum || o2 != maximum) {
if ((o1 % csSpan) == 0 && (o2 % csSpan) == 0) {
ordinates[iLower] = +0.0;
ordinates[iUpper] = -0.0;
} else {
ordinates[iLower] = minimum;
ordinates[iUpper] = maximum;
}
changed = true;
}
} else {
o1 = Math.floor((o1 - minimum) / csSpan) * csSpan;
o2 = Math.floor((o2 - minimum) / csSpan) * csSpan;
if (o1 != 0) {
ordinates[iLower] -= o1;
changed = true;
}
if (o2 != 0) {
ordinates[iUpper] -= o2;
changed = true;
}
}
}
}
}
return changed;
}
use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class Shapes2D method transform.
/**
* Transforms a rectangular envelope using the given coordinate operation.
* The transformation is only approximative: the returned envelope may be bigger
* than the smallest possible bounding box, but should not be smaller in most cases.
*
* <p>This method can handle the case where the rectangle contains the North or South pole,
* or when it cross the ±180° longitude.</p>
*
* @param operation the operation to use. Source and target dimension must be 2.
* @param envelope the rectangle to transform (may be {@code null}).
* @param destination the destination rectangle (may be {@code envelope}).
* If {@code null}, a new rectangle will be created and returned.
* @return {@code destination}, or a new rectangle if {@code destination} was non-null and {@code envelope} was null.
* @throws TransformException if a transform failed.
*
* @see #transform(MathTransform2D, Rectangle2D, Rectangle2D)
* @see Envelopes#transform(CoordinateOperation, Envelope)
*/
@SuppressWarnings("null")
public static Rectangle2D transform(final CoordinateOperation operation, final Rectangle2D envelope, Rectangle2D destination) throws TransformException {
ArgumentChecks.ensureNonNull("operation", operation);
if (envelope == null) {
return null;
}
final MathTransform transform = operation.getMathTransform();
if (!(transform instanceof MathTransform2D)) {
throw new MismatchedDimensionException(Errors.format(Errors.Keys.IllegalPropertyValueClass_3, "transform", MathTransform2D.class, MathTransform.class));
}
MathTransform2D mt = (MathTransform2D) transform;
final double[] center = new double[2];
destination = transform(mt, envelope, destination, center);
/*
* If the source envelope crosses the expected range of valid coordinates, also projects
* the range bounds as a safety. See the comments in transform(Envelope, ...).
*/
final CoordinateReferenceSystem sourceCRS = operation.getSourceCRS();
if (sourceCRS != null) {
final CoordinateSystem cs = sourceCRS.getCoordinateSystem();
if (cs != null && cs.getDimension() == 2) {
// Paranoiac check.
CoordinateSystemAxis axis = cs.getAxis(0);
double min = envelope.getMinX();
double max = envelope.getMaxX();
Point2D.Double pt = null;
for (int i = 0; i < 4; i++) {
if (i == 2) {
axis = cs.getAxis(1);
min = envelope.getMinY();
max = envelope.getMaxY();
}
final double v = (i & 1) == 0 ? axis.getMinimumValue() : axis.getMaximumValue();
if (!(v > min && v < max)) {
continue;
}
if (pt == null) {
pt = new Point2D.Double();
}
if ((i & 2) == 0) {
pt.x = v;
pt.y = envelope.getCenterY();
} else {
pt.x = envelope.getCenterX();
pt.y = v;
}
destination.add(mt.transform(pt, pt));
}
}
}
/*
* Now take the target CRS in account.
*/
final CoordinateReferenceSystem targetCRS = operation.getTargetCRS();
if (targetCRS == null) {
return destination;
}
final CoordinateSystem targetCS = targetCRS.getCoordinateSystem();
if (targetCS == null || targetCS.getDimension() != 2) {
// It should be an error, but we keep this method tolerant.
return destination;
}
/*
* Checks for singularity points. See the Envelopes.transform(CoordinateOperation, Envelope)
* method for comments about the algorithm. The code below is the same algorithm adapted for
* the 2D case and the related objects (Point2D, Rectangle2D, etc.).
*
* The 'border' variable in the loop below is used in order to compress 2 dimensions
* and 2 extremums in a single loop, in this order: (xmin, xmax, ymin, ymax).
*/
TransformException warning = null;
Point2D sourcePt = null;
Point2D targetPt = null;
// A bitmask for each (dimension, extremum) pairs.
int includedBoundsValue = 0;
for (int border = 0; border < 4; border++) {
// 2 dimensions and 2 extremums compacted in a flag.
// The dimension index being examined.
final int dimension = border >>> 1;
final CoordinateSystemAxis axis = targetCS.getAxis(dimension);
if (axis == null) {
// Should never be null, but check as a paranoiac safety.
continue;
}
final double extremum = (border & 1) == 0 ? axis.getMinimumValue() : axis.getMaximumValue();
if (Double.isInfinite(extremum) || Double.isNaN(extremum)) {
continue;
}
if (targetPt == null) {
try {
mt = mt.inverse();
} catch (NoninvertibleTransformException exception) {
Envelopes.recoverableException(Shapes2D.class, exception);
return destination;
}
targetPt = new Point2D.Double();
}
switch(dimension) {
case 0:
targetPt.setLocation(extremum, center[1]);
break;
case 1:
targetPt.setLocation(center[0], extremum);
break;
default:
throw new AssertionError(border);
}
try {
sourcePt = mt.transform(targetPt, sourcePt);
} catch (TransformException exception) {
if (warning == null) {
warning = exception;
} else {
warning.addSuppressed(exception);
}
continue;
}
if (envelope.contains(sourcePt)) {
destination.add(targetPt);
includedBoundsValue |= (1 << border);
}
}
/*
* Iterate over all dimensions of type "WRAPAROUND" for which minimal or maximal axis
* values have not yet been included in the envelope. We could inline this check inside
* the above loop, but we don't in order to have a chance to exclude the dimensions for
* which the point have already been added.
*
* See transform(CoordinateOperation, Envelope) for more comments about the algorithm.
*/
if (includedBoundsValue != 0) {
/*
* Bits mask transformation:
* 1) Swaps the two dimensions (YyXx → XxYy)
* 2) Insert a space between each bits (XxYy → X.x.Y.y.)
* 3) Fill the space with duplicated values (X.x.Y.y. → XXxxYYyy)
*
* In terms of bit positions 1,2,4,8 (not bit values), we have:
*
* 8421 → 22881144
* i.e. (ymax, ymin, xmax, xmin) → (xmax², ymax², xmin², ymin²)
*
* Now look at the last part: (xmin², ymin²). The next step is to perform a bitwise
* AND operation in order to have only both of the following conditions:
*
* Borders not yet added to the envelope: ~(ymax, ymin, xmax, xmin)
* Borders in which a singularity exists: (xmin, xmin, ymin, ymin)
*
* The same operation is repeated on the next 4 bits for (xmax, xmax, ymax, ymax).
*/
int toTest = ((includedBoundsValue & 1) << 3) | ((includedBoundsValue & 4) >>> 1) | ((includedBoundsValue & 2) << 6) | ((includedBoundsValue & 8) << 2);
// Duplicate the bit values.
toTest |= (toTest >>> 1);
toTest &= ~(includedBoundsValue | (includedBoundsValue << 4));
/*
* Forget any axes that are not of kind "WRAPAROUND". Then get the final
* bit pattern indicating which points to test. Iterate over that bits.
*/
if ((toTest & 0x33333333) != 0 && !CoordinateOperations.isWrapAround(targetCS.getAxis(0)))
toTest &= 0xCCCCCCCC;
if ((toTest & 0xCCCCCCCC) != 0 && !CoordinateOperations.isWrapAround(targetCS.getAxis(1)))
toTest &= 0x33333333;
while (toTest != 0) {
final int border = Integer.numberOfTrailingZeros(toTest);
final int bitMask = 1 << border;
// Clear now the bit, for the next iteration.
toTest &= ~bitMask;
final int dimensionToAdd = (border >>> 1) & 1;
final CoordinateSystemAxis toAdd = targetCS.getAxis(dimensionToAdd);
final CoordinateSystemAxis added = targetCS.getAxis(dimensionToAdd ^ 1);
double x = (border & 1) == 0 ? toAdd.getMinimumValue() : toAdd.getMaximumValue();
double y = (border & 4) == 0 ? added.getMinimumValue() : added.getMaximumValue();
if (dimensionToAdd != 0) {
final double t = x;
x = y;
y = t;
}
targetPt.setLocation(x, y);
try {
sourcePt = mt.transform(targetPt, sourcePt);
} catch (TransformException exception) {
if (warning == null) {
warning = exception;
} else {
warning.addSuppressed(exception);
}
continue;
}
if (envelope.contains(sourcePt)) {
destination.add(targetPt);
}
}
}
/*
* At this point we finished envelope transformation. Verify if some ordinates need to be "wrapped around"
* as a result of the coordinate operation. This is usually the longitude axis where the source CRS uses
* the [-180 … +180]° range and the target CRS uses the [0 … 360]° range, or the converse. In such case we
* set the rectangle to the full range (we do not use the mechanism documented in Envelope2D) because most
* Rectangle2D implementations do not support spanning the anti-meridian. This results in larger rectangle
* than what would be possible with GeneralEnvelope or Envelope2D, but we try to limit the situation where
* this expansion is applied.
*/
final Set<Integer> wrapAroundChanges;
if (operation instanceof AbstractCoordinateOperation) {
wrapAroundChanges = ((AbstractCoordinateOperation) operation).getWrapAroundChanges();
} else {
wrapAroundChanges = CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS);
}
for (int dim : wrapAroundChanges) {
// Empty in the vast majority of cases.
final CoordinateSystemAxis axis = targetCS.getAxis(dim);
final double minimum = axis.getMinimumValue();
final double maximum = axis.getMaximumValue();
final double o1, o2;
if (dim == 0) {
o1 = destination.getMinX();
o2 = destination.getMaxX();
} else {
o1 = destination.getMinY();
o2 = destination.getMaxY();
}
if (o1 < minimum || o2 > maximum) {
final double span = maximum - minimum;
if (dim == 0) {
destination.setRect(minimum, destination.getY(), span, destination.getHeight());
} else {
destination.setRect(destination.getX(), minimum, destination.getWidth(), span);
}
}
}
if (warning != null) {
Envelopes.recoverableException(Shapes2D.class, warning);
}
return destination;
}
use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class MetadataTest method testMetadataWithVerticalCRS.
/**
* Tests the (un)marshalling of a metadata with a vertical CRS.
*
* @throws JAXBException if the (un)marshalling process fails.
*/
@Test
public void testMetadataWithVerticalCRS() throws JAXBException {
final Metadata metadata = unmarshalFile(Metadata.class, VERTICAL_CRS_XML);
if (REGRESSION) {
((DefaultMetadata) metadata).setCharacterSet(CharacterSet.UTF_8);
}
assertEquals("fileIdentifier", "20090901", metadata.getFileIdentifier());
assertEquals("language", Locale.ENGLISH, metadata.getLanguage());
assertEquals("characterSet", CharacterSet.UTF_8, metadata.getCharacterSet());
assertEquals("dateStamp", xmlDate("2014-01-04 00:00:00"), metadata.getDateStamp());
/*
* <gmd:contact>
* <gmd:CI_ResponsibleParty>
* …
* </gmd:CI_ResponsibleParty>
* </gmd:contact>
*/
final ResponsibleParty contact = getSingleton(metadata.getContacts());
final OnlineResource onlineResource = contact.getContactInfo().getOnlineResource();
assertNotNull("onlineResource", onlineResource);
assertEquals("organisationName", "Apache SIS", contact.getOrganisationName().toString());
assertEquals("linkage", URI.create("http://sis.apache.org"), onlineResource.getLinkage());
assertEquals("function", OnLineFunction.INFORMATION, onlineResource.getFunction());
assertEquals("role", Role.PRINCIPAL_INVESTIGATOR, contact.getRole());
/*
* <gmd:spatialRepresentationInfo>
* <gmd:MD_VectorSpatialRepresentation>
* …
* </gmd:MD_VectorSpatialRepresentation>
* </gmd:spatialRepresentationInfo>
*/
final SpatialRepresentation spatial = getSingleton(metadata.getSpatialRepresentationInfo());
assertInstanceOf("spatialRepresentationInfo", VectorSpatialRepresentation.class, spatial);
assertEquals("geometricObjectType", GeometricObjectType.POINT, getSingleton(((VectorSpatialRepresentation) spatial).getGeometricObjects()).getGeometricObjectType());
/*
* <gmd:referenceSystemInfo>
* <gmd:MD_ReferenceSystem>
* …
* </gmd:MD_ReferenceSystem>
* </gmd:referenceSystemInfo>
*/
assertIdentifierEquals("referenceSystemInfo", null, "EPSG", null, "World Geodetic System 84", getSingleton(metadata.getReferenceSystemInfo()).getName());
/*
* <gmd:identificationInfo>
* <gmd:MD_DataIdentification>
* …
*/
final DataIdentification identification = (DataIdentification) getSingleton(metadata.getIdentificationInfo());
final Citation citation = identification.getCitation();
assertInstanceOf("citation", NilObject.class, citation);
assertEquals("nilReason", NilReason.MISSING, ((NilObject) citation).getNilReason());
assertEquals("abstract", "SIS test", identification.getAbstract().toString());
assertEquals("language", Locale.ENGLISH, getSingleton(identification.getLanguages()));
/*
* <gmd:geographicElement>
* <gmd:EX_GeographicBoundingBox>
* …
* </gmd:EX_GeographicBoundingBox>
* </gmd:geographicElement>
*/
final Extent extent = getSingleton(identification.getExtents());
final GeographicBoundingBox bbox = (GeographicBoundingBox) getSingleton(extent.getGeographicElements());
assertEquals("extentTypeCode", Boolean.TRUE, bbox.getInclusion());
assertEquals("westBoundLongitude", 4.55, bbox.getWestBoundLongitude(), STRICT);
assertEquals("eastBoundLongitude", 4.55, bbox.getEastBoundLongitude(), STRICT);
assertEquals("southBoundLatitude", 44.22, bbox.getSouthBoundLatitude(), STRICT);
assertEquals("northBoundLatitude", 44.22, bbox.getNorthBoundLatitude(), STRICT);
/*
* <gmd:verticalElement>
* <gmd:EX_VerticalExtent>
* …
* </gmd:EX_VerticalExtent>
* </gmd:verticalElement>
*/
final VerticalExtent ve = getSingleton(extent.getVerticalElements());
assertEquals("minimumValue", 0.1, ve.getMinimumValue(), STRICT);
assertEquals("maximumValue", 10000, ve.getMaximumValue(), STRICT);
final VerticalCRS crs = ve.getVerticalCRS();
verifyIdentifiers("test1", crs);
assertEquals("scope", "World", crs.getScope().toString());
final VerticalDatum datum = crs.getDatum();
verifyIdentifiers("test2", datum);
assertEquals("scope", "World", datum.getScope().toString());
// Inferred from the name.
assertEquals("vertDatumType", VerticalDatumType.DEPTH, datum.getVerticalDatumType());
final VerticalCS cs = crs.getCoordinateSystem();
verifyIdentifiers("test3", cs);
final CoordinateSystemAxis axis = cs.getAxis(0);
verifyIdentifiers("test4", axis);
assertEquals("axisAbbrev", "d", axis.getAbbreviation());
assertEquals("axisDirection", AxisDirection.DOWN, axis.getDirection());
/*
* …
* </gmd:MD_DataIdentification>
* </gmd:identificationInfo>
*
* Now marshal the object and compare with the original file.
*/
assertMarshalEqualsFile(VERTICAL_CRS_XML, metadata, VERSION_2007, "xmlns:*", "xsi:schemaLocation");
}
use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class Angle method valueOf.
/**
* Returns the angular value of the axis having the given direction.
* This helper method is used for subclass constructors expecting a {@link DirectPosition} argument.
*
* @param position the position from which to get an angular value.
* @param positive axis direction of positive values.
* @param negative axis direction of negative values.
* @return angular value in degrees.
* @throws IllegalArgumentException if the given coordinate it not associated to a CRS,
* or if no axis oriented toward the given directions is found, or if that axis
* does not use {@linkplain Units#isAngular angular units}.
*/
static double valueOf(final DirectPosition position, final AxisDirection positive, final AxisDirection negative) {
final CoordinateReferenceSystem crs = position.getCoordinateReferenceSystem();
if (crs == null) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.UnspecifiedCRS));
}
final CoordinateSystem cs = crs.getCoordinateSystem();
final int dimension = cs.getDimension();
IncommensurableException cause = null;
for (int i = 0; i < dimension; i++) {
final CoordinateSystemAxis axis = cs.getAxis(i);
final AxisDirection dir = axis.getDirection();
final boolean isPositive = dir.equals(positive);
if (isPositive || dir.equals(negative)) {
double value = position.getOrdinate(i);
if (!isPositive)
value = -value;
final Unit<?> unit = axis.getUnit();
if (unit != Units.DEGREE)
try {
value = unit.getConverterToAny(Units.DEGREE).convert(value);
} catch (IncommensurableException e) {
cause = e;
break;
}
return value;
}
}
throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalCRSType_1, Classes.getLeafInterfaces(crs.getClass(), CoordinateReferenceSystem.class)[0]), cause);
}
Aggregations