use of org.opengis.referencing.cs.RangeMeaning in project sis by apache.
the class GeodeticObjectVerifier method verifyRange.
/**
* Asserts that the axis range is either fully missing, or defined to exactly the given properties.
*/
private static void verifyRange(final CoordinateSystemAxis axis, final double min, final double max, final RangeMeaning expected, final boolean isMandatory) {
final double minimumValue = axis.getMinimumValue();
final double maximumValue = axis.getMaximumValue();
final RangeMeaning rangeMeaning = axis.getRangeMeaning();
if (isMandatory || rangeMeaning != null || minimumValue != Double.NEGATIVE_INFINITY || maximumValue != Double.POSITIVE_INFINITY) {
assertEquals("axis.minimumValue", min, minimumValue, STRICT);
assertEquals("axis.maximumValue", max, maximumValue, STRICT);
assertEquals("axis.rangeMeaning", expected, rangeMeaning);
}
}
use of org.opengis.referencing.cs.RangeMeaning in project sis by apache.
the class Normalizer method shiftAxisRange.
/**
* Returns a coordinate system with the same axes than the given CS, except that the wrapround axes
* are shifted to a range of positive values. This method can be used in order to shift between the
* [-180 … +180]° and [0 … 360]° ranges of longitude values.
*
* <p>This method shifts the axis {@linkplain CoordinateSystemAxis#getMinimumValue() minimum} and
* {@linkplain CoordinateSystemAxis#getMaximumValue() maximum} values by a multiple of half the range
* (typically 180°). This method does not change the meaning of ordinate values. For example a longitude
* of -60° still locate the same point in the old and the new coordinate system. But the preferred way
* to locate that point become the 300° value if the longitude range has been shifted to positive values.</p>
*
* @return a coordinate system using the given kind of longitude range, or {@code null} if no change is needed.
*/
private static AbstractCS shiftAxisRange(final CoordinateSystem cs) {
boolean changed = false;
final CoordinateSystemAxis[] axes = new CoordinateSystemAxis[cs.getDimension()];
for (int i = 0; i < axes.length; i++) {
CoordinateSystemAxis axis = cs.getAxis(i);
final RangeMeaning rangeMeaning = axis.getRangeMeaning();
if (RangeMeaning.WRAPAROUND.equals(rangeMeaning)) {
double min = axis.getMinimumValue();
if (min < 0) {
double max = axis.getMaximumValue();
double offset = (max - min) / 2;
offset *= Math.floor(min / offset + 1E-10);
min -= offset;
max -= offset;
if (min < max) {
// Paranoiac check, but also a way to filter NaN values when offset is infinite.
final Map<String, Object> properties = new HashMap<>();
properties.putAll(IdentifiedObjects.getProperties(axis, EXCLUDES));
properties.put(DefaultCoordinateSystemAxis.MINIMUM_VALUE_KEY, min);
properties.put(DefaultCoordinateSystemAxis.MAXIMUM_VALUE_KEY, max);
properties.put(DefaultCoordinateSystemAxis.RANGE_MEANING_KEY, rangeMeaning);
axis = new DefaultCoordinateSystemAxis(properties, axis.getAbbreviation(), axis.getDirection(), axis.getUnit());
changed = true;
}
}
}
axes[i] = axis;
}
if (!changed) {
return null;
}
return castOrCopy(cs).createForAxes(IdentifiedObjects.getProperties(cs, EXCLUDES), axes);
}
use of org.opengis.referencing.cs.RangeMeaning 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.RangeMeaning in project sis by apache.
the class AbstractDirectPosition method normalize.
/**
* Ensures that the position is contained in the coordinate system domain.
* For each dimension, this method compares the ordinate values against the
* limits of the coordinate system axis for that dimension.
* If some ordinates are out of range, then there is a choice depending on the
* {@linkplain CoordinateSystemAxis#getRangeMeaning() axis range meaning}:
*
* <ul>
* <li>If {@link RangeMeaning#EXACT} (typically <em>latitudes</em> ordinates), then values
* greater than the {@linkplain CoordinateSystemAxis#getMaximumValue() axis maximal value}
* are replaced by the axis maximum, and values smaller than the
* {@linkplain CoordinateSystemAxis#getMinimumValue() axis minimal value}
* are replaced by the axis minimum.</li>
*
* <li>If {@link RangeMeaning#WRAPAROUND} (typically <em>longitudes</em> ordinates), then
* a multiple of the axis range (e.g. 360° for longitudes) is added or subtracted.</li>
* </ul>
*
* @return {@code true} if this position has been modified as a result of this method call,
* or {@code false} if no change has been done.
*
* @see GeneralEnvelope#normalize()
*/
public boolean normalize() {
boolean changed = false;
final CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
if (crs != null) {
final int dimension = getDimension();
final CoordinateSystem cs = crs.getCoordinateSystem();
for (int i = 0; i < dimension; i++) {
double ordinate = getOrdinate(i);
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 (ordinate < minimum)
ordinate = minimum;
else if (ordinate > maximum)
ordinate = maximum;
else
continue;
} else if (RangeMeaning.WRAPAROUND.equals(rm)) {
final double csSpan = maximum - minimum;
final double shift = Math.floor((ordinate - minimum) / csSpan) * csSpan;
if (shift == 0) {
continue;
}
ordinate -= shift;
}
setOrdinate(i, ordinate);
changed = true;
}
}
return changed;
}
use of org.opengis.referencing.cs.RangeMeaning in project sis by apache.
the class StandardDefinitions method createAxis.
/**
* Creates an axis from hard-coded values for the given code.
*
* @param code the EPSG code.
* @return the coordinate system axis for the given code.
*/
static CoordinateSystemAxis createAxis(final short code) {
final String name, abrv;
Unit<?> unit = Units.METRE;
double min = Double.NEGATIVE_INFINITY;
double max = Double.POSITIVE_INFINITY;
RangeMeaning rm = null;
final AxisDirection dir;
switch(code) {
case 1:
name = "Easting";
abrv = "E";
dir = AxisDirection.EAST;
break;
case 2:
name = "Northing";
abrv = "N";
dir = AxisDirection.NORTH;
break;
case 60:
name = "Spherical latitude";
// See HardCodedAxes.SPHERICAL_LATITUDE in tests.
abrv = "φ′";
unit = Units.DEGREE;
dir = AxisDirection.NORTH;
min = Latitude.MIN_VALUE;
max = Latitude.MAX_VALUE;
rm = RangeMeaning.EXACT;
break;
case 61:
name = "Spherical longitude";
// See HardCodedAxes.SPHERICAL_LONGITUDE in tests.
abrv = "θ";
unit = Units.DEGREE;
dir = AxisDirection.EAST;
min = Longitude.MIN_VALUE;
max = Longitude.MAX_VALUE;
rm = RangeMeaning.WRAPAROUND;
break;
case 62:
name = "Geocentric radius";
// See HardCodedAxes.GEOCENTRIC_RADIUS in tests.
abrv = "R";
dir = AxisDirection.UP;
rm = RangeMeaning.EXACT;
min = 0;
break;
// Used in Ellipsoidal 3D.
case 108:
case 106:
name = AxisNames.GEODETIC_LATITUDE;
abrv = "φ";
unit = Units.DEGREE;
dir = AxisDirection.NORTH;
min = Latitude.MIN_VALUE;
max = Latitude.MAX_VALUE;
rm = RangeMeaning.EXACT;
break;
// Used in Ellipsoidal 3D.
case 109:
case 107:
name = AxisNames.GEODETIC_LONGITUDE;
abrv = "λ";
unit = Units.DEGREE;
dir = AxisDirection.EAST;
min = Longitude.MIN_VALUE;
max = Longitude.MAX_VALUE;
rm = RangeMeaning.WRAPAROUND;
break;
case 110:
name = AxisNames.ELLIPSOIDAL_HEIGHT;
abrv = "h";
dir = AxisDirection.UP;
break;
case 114:
name = AxisNames.GRAVITY_RELATED_HEIGHT;
abrv = "H";
dir = AxisDirection.UP;
break;
case 113:
name = AxisNames.DEPTH;
abrv = "D";
dir = AxisDirection.DOWN;
break;
case 115:
name = AxisNames.GEOCENTRIC_X;
abrv = "X";
dir = AxisDirection.GEOCENTRIC_X;
break;
case 116:
name = AxisNames.GEOCENTRIC_Y;
abrv = "Y";
dir = AxisDirection.GEOCENTRIC_Y;
break;
case 117:
name = AxisNames.GEOCENTRIC_Z;
abrv = "Z";
dir = AxisDirection.GEOCENTRIC_Z;
break;
// Actually no axis allocated by EPSG here, but createCoordinateSystem(1027) needs this number.
case 1057:
case 1056:
name = "Easting";
abrv = "E";
dir = CoordinateSystems.directionAlongMeridian(AxisDirection.NORTH, 90);
break;
case 1058:
name = "Northing";
abrv = "N";
dir = CoordinateSystems.directionAlongMeridian(AxisDirection.NORTH, 0);
break;
case 1065:
name = "Easting";
abrv = "E";
dir = CoordinateSystems.directionAlongMeridian(AxisDirection.SOUTH, 90);
break;
case 1066:
name = "Northing";
abrv = "N";
dir = CoordinateSystems.directionAlongMeridian(AxisDirection.SOUTH, 180);
break;
default:
throw new AssertionError(code);
}
final Map<String, Object> properties = properties(code, name, null, false);
properties.put(DefaultCoordinateSystemAxis.MINIMUM_VALUE_KEY, min);
properties.put(DefaultCoordinateSystemAxis.MAXIMUM_VALUE_KEY, max);
properties.put(DefaultCoordinateSystemAxis.RANGE_MEANING_KEY, rm);
return new DefaultCoordinateSystemAxis(properties, abrv, dir, unit);
}
Aggregations