use of org.opengis.referencing.cs.CoordinateSystem in project sis by apache.
the class Envelopes method transform.
/**
* Transforms an 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 envelope contains the North or South pole,
* or when it cross the ±180° longitude.</p>
*
* <div class="note"><b>Note:</b>
* If the envelope CRS is non-null, then the caller should ensure that the operation source CRS
* is the same than the envelope CRS. In case of mismatch, this method transforms the envelope
* to the operation source CRS before to apply the operation. This extra step may cause a lost
* of accuracy. In order to prevent this method from performing such pre-transformation (if not desired),
* callers can ensure that the envelope CRS is {@code null} before to call this method.</div>
*
* @param operation the operation to use.
* @param envelope envelope to transform, or {@code null}. This envelope will not be modified.
* @return the transformed envelope, or {@code null} if {@code envelope} was null.
* @throws TransformException if a transform failed.
*
* @see #transform(MathTransform, Envelope)
*
* @since 0.5
*/
@SuppressWarnings("null")
public static GeneralEnvelope transform(final CoordinateOperation operation, Envelope envelope) throws TransformException {
ensureNonNull("operation", operation);
if (envelope == null) {
return null;
}
boolean isOperationComplete = true;
final CoordinateReferenceSystem sourceCRS = operation.getSourceCRS();
if (sourceCRS != null) {
final CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
if (crs != null && !Utilities.equalsIgnoreMetadata(crs, sourceCRS)) {
/*
* Argument-check: the envelope CRS seems inconsistent with the given operation.
* However we need to push the check a little bit further, since 3D-GeographicCRS
* are considered not equal to CompoundCRS[2D-GeographicCRS + ellipsoidal height].
* Checking for identity MathTransform is a more powerfull (but more costly) check.
* Since we have the MathTransform, perform an opportunist envelope transform if it
* happen to be required.
*/
final MathTransform mt;
try {
mt = CoordinateOperations.factory().createOperation(crs, sourceCRS).getMathTransform();
} catch (FactoryException e) {
throw new TransformException(Errors.format(Errors.Keys.CanNotTransformEnvelope), e);
}
if (!mt.isIdentity()) {
isOperationComplete = false;
envelope = transform(mt, envelope);
}
}
}
MathTransform mt = operation.getMathTransform();
final double[] centerPt = new double[mt.getTargetDimensions()];
final GeneralEnvelope transformed = transform(mt, envelope, centerPt);
/*
* If the source envelope crosses the expected range of valid coordinates, also projects
* the range bounds as a safety. Example: if the source envelope goes from 150 to 200°E,
* some map projections will interpret 200° as if it was -160°, and consequently produce
* an envelope which do not include the 180°W extremum. We will add those extremum points
* explicitly as a safety. It may leads to bigger than necessary target envelope, but the
* contract is to include at least the source envelope, not to return the smallest one.
*/
if (sourceCRS != null) {
final CoordinateSystem cs = sourceCRS.getCoordinateSystem();
if (cs != null) {
// Should never be null, but check as a paranoiac safety.
DirectPosition sourcePt = null;
DirectPosition targetPt = null;
final int dimension = cs.getDimension();
for (int i = 0; i < dimension; i++) {
final CoordinateSystemAxis axis = cs.getAxis(i);
if (axis == null) {
// Should never be null, but check as a paranoiac safety.
continue;
}
final double min = envelope.getMinimum(i);
final double max = envelope.getMaximum(i);
final double v1 = axis.getMinimumValue();
final double v2 = axis.getMaximumValue();
final boolean b1 = (v1 > min && v1 < max);
final boolean b2 = (v2 > min && v2 < max);
if (!b1 && !b2) {
continue;
}
if (sourcePt == null) {
sourcePt = new GeneralDirectPosition(dimension);
for (int j = 0; j < dimension; j++) {
sourcePt.setOrdinate(j, envelope.getMedian(j));
}
}
if (b1) {
sourcePt.setOrdinate(i, v1);
transformed.add(targetPt = mt.transform(sourcePt, targetPt));
}
if (b2) {
sourcePt.setOrdinate(i, v2);
transformed.add(targetPt = mt.transform(sourcePt, targetPt));
}
sourcePt.setOrdinate(i, envelope.getMedian(i));
}
}
}
/*
* Now takes the target CRS in account...
*/
final CoordinateReferenceSystem targetCRS = operation.getTargetCRS();
if (targetCRS == null) {
return transformed;
}
transformed.setCoordinateReferenceSystem(targetCRS);
final CoordinateSystem targetCS = targetCRS.getCoordinateSystem();
if (targetCS == null) {
// It should be an error, but we keep this method tolerant.
return transformed;
}
/*
* Checks for singularity points. For example the south pole is a singularity point in
* geographic CRS because is is located at the maximal value allowed by one particular
* axis, namely latitude. This point is not a singularity in the stereographic projection,
* because axes extends toward infinity in all directions (mathematically) and because the
* South pole has nothing special apart being the origin (0,0).
*
* Algorithm:
*
* 1) Inspect the target axis, looking if there is any bounds. If bounds are found, get
* the coordinates of singularity points and project them from target to source CRS.
*
* Example: If the transformed envelope above is (80 … 85°S, 10 … 50°W), and if the
* latitude in the target CRS is bounded at 90°S, then project (90°S, 30°W)
* to the source CRS. Note that the longitude is set to the the center of
* the envelope longitude range (more on this below).
*
* 2) If the singularity point computed above is inside the source envelope, add that
* point to the target (transformed) envelope.
*
* 3) If step #2 added the point, iterate over all other axes. If an other bounded axis
* is found and that axis is of kind "WRAPAROUND", test for inclusion the same point
* than the point tested at step #1, except for the ordinate of the axis found in this
* step. That ordinate is set to the minimal and maximal values of that axis.
*
* Example: If the above steps found that the point (90°S, 30°W) need to be included,
* then this step #3 will also test the points (90°S, 180°W) and (90°S, 180°E).
*
* NOTE: we test (-180°, centerY), (180°, centerY), (centerX, -90°) and (centerX, 90°)
* at step #1 before to test (-180°, -90°), (180°, -90°), (-180°, 90°) and (180°, 90°)
* at step #3 because the later may not be supported by every projections. For example
* if the target envelope is located between 20°N and 40°N, then a Mercator projection
* may fail to transform the (-180°, 90°) coordinate while the (-180°, 30°) coordinate
* is a valid point.
*/
TransformException warning = null;
AbstractEnvelope generalEnvelope = null;
DirectPosition sourcePt = null;
DirectPosition targetPt = null;
// A bitmask for each dimension.
long includedMinValue = 0;
long includedMaxValue = 0;
long isWrapAroundAxis = 0;
long dimensionBitMask = 1;
final int dimension = targetCS.getDimension();
poles: for (int i = 0; i < dimension; i++, dimensionBitMask <<= 1) {
final CoordinateSystemAxis axis = targetCS.getAxis(i);
if (axis == null) {
// Should never be null, but check as a paranoiac safety.
continue;
}
// Tells if we are testing the minimal or maximal value.
boolean testMax = false;
do {
final double extremum = testMax ? axis.getMaximumValue() : axis.getMinimumValue();
if (Double.isInfinite(extremum) || Double.isNaN(extremum)) {
/*
* The axis is unbounded. It should always be the case when the target CRS is
* a map projection, in which case this loop will finish soon and this method
* will do nothing more (no object instantiated, no MathTransform inversed...)
*/
continue;
}
if (targetPt == null) {
try {
mt = mt.inverse();
} catch (NoninvertibleTransformException exception) {
/*
* If the transform is non invertible, this method can't do anything. This
* is not a fatal error because the envelope has already be transformed by
* the caller. We lost the check for singularity points performed by this
* method, but it make no difference in the common case where the source
* envelope didn't contains any of those points.
*
* Note that this exception is normal if target dimension is smaller than
* source dimension, since the math transform can not reconstituate the
* lost dimensions. So we don't log any warning in this case.
*/
if (dimension >= mt.getSourceDimensions()) {
warning = exception;
}
break poles;
}
targetPt = new GeneralDirectPosition(mt.getSourceDimensions());
for (int j = 0; j < dimension; j++) {
targetPt.setOrdinate(j, centerPt[j]);
}
// TODO: avoid the hack below if we provide a contains(DirectPosition)
// method in the GeoAPI org.opengis.geometry.Envelope interface.
generalEnvelope = AbstractEnvelope.castOrCopy(envelope);
}
targetPt.setOrdinate(i, extremum);
try {
sourcePt = mt.transform(targetPt, sourcePt);
} catch (TransformException exception) {
/*
* This exception may be normal. For example if may occur when projecting
* the latitude extremums with a cylindrical Mercator projection. Do not
* log any message (unless logging level is fine) and try the other points.
*/
if (warning == null) {
warning = exception;
} else {
warning.addSuppressed(exception);
}
continue;
}
if (generalEnvelope.contains(sourcePt)) {
transformed.add(targetPt);
if (testMax)
includedMaxValue |= dimensionBitMask;
else
includedMinValue |= dimensionBitMask;
}
} while ((testMax = !testMax) == true);
/*
* Keep trace of axes of kind WRAPAROUND, except if the two extremum values of that
* axis have been included in the envelope (in which case the next step after this
* loop doesn't need to be executed for that axis).
*/
if ((includedMinValue & includedMaxValue & dimensionBitMask) == 0 && CoordinateOperations.isWrapAround(axis)) {
isWrapAroundAxis |= dimensionBitMask;
}
// Restore 'targetPt' to its initial state, which is equals to 'centerPt'.
if (targetPt != null) {
targetPt.setOrdinate(i, centerPt[i]);
}
}
/*
* Step #3 described in the above "Algorithm" section: iterate over all dimensions
* of type "WRAPAROUND" for which minimal or maximal axis values have not yet been
* included in the envelope. The set of axes is specified by a bitmask computed in
* the above loop. We examine only the points that have not already been included
* in the envelope.
*/
final long includedBoundsValue = (includedMinValue | includedMaxValue);
if (includedBoundsValue != 0) {
while (isWrapAroundAxis != 0) {
final int wrapAroundDimension = Long.numberOfTrailingZeros(isWrapAroundAxis);
dimensionBitMask = 1 << wrapAroundDimension;
// Clear now the bit, for the next iteration.
isWrapAroundAxis &= ~dimensionBitMask;
final CoordinateSystemAxis wrapAroundAxis = targetCS.getAxis(wrapAroundDimension);
final double min = wrapAroundAxis.getMinimumValue();
final double max = wrapAroundAxis.getMaximumValue();
/*
* Iterate over all axes for which a singularity point has been previously found,
* excluding the "wrap around axis" currently under consideration.
*/
for (long am = (includedBoundsValue & ~dimensionBitMask), bm; am != 0; am &= ~bm) {
bm = Long.lowestOneBit(am);
final int axisIndex = Long.numberOfTrailingZeros(bm);
final CoordinateSystemAxis axis = targetCS.getAxis(axisIndex);
/*
* switch (c) {
* case 0: targetPt = (..., singularityMin, ..., wrapAroundMin, ...)
* case 1: targetPt = (..., singularityMin, ..., wrapAroundMax, ...)
* case 2: targetPt = (..., singularityMax, ..., wrapAroundMin, ...)
* case 3: targetPt = (..., singularityMax, ..., wrapAroundMax, ...)
* }
*/
for (int c = 0; c < 4; c++) {
/*
* Set the ordinate value along the axis having the singularity point
* (cases c=0 and c=2). If the envelope did not included that point,
* then skip completely this case and the next one, i.e. skip c={0,1}
* or skip c={2,3}.
*/
double value = max;
if ((c & 1) == 0) {
// 'true' if we are testing "wrapAroundMin".
if (((c == 0 ? includedMinValue : includedMaxValue) & bm) == 0) {
// Skip also the case for "wrapAroundMax".
c++;
continue;
}
targetPt.setOrdinate(axisIndex, (c == 0) ? axis.getMinimumValue() : axis.getMaximumValue());
value = min;
}
targetPt.setOrdinate(wrapAroundDimension, value);
try {
sourcePt = mt.transform(targetPt, sourcePt);
} catch (TransformException exception) {
if (warning == null) {
warning = exception;
} else {
warning.addSuppressed(exception);
}
continue;
}
if (generalEnvelope.contains(sourcePt)) {
transformed.add(targetPt);
}
}
targetPt.setOrdinate(axisIndex, centerPt[axisIndex]);
}
targetPt.setOrdinate(wrapAroundDimension, centerPt[wrapAroundDimension]);
}
}
/*
* 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. We do not wrap
* around if the source and target axes use the same range (e.g. the longitude stay [-180 … +180]°) in order
* to reduce the risk of discontinuities. If the user really wants unconditional wrap around, (s)he can call
* GeneralEnvelope.normalize().
*/
final Set<Integer> wrapAroundChanges;
if (isOperationComplete && operation instanceof AbstractCoordinateOperation) {
wrapAroundChanges = ((AbstractCoordinateOperation) operation).getWrapAroundChanges();
} else {
wrapAroundChanges = CoordinateOperations.wrapAroundChanges(sourceCRS, targetCS);
}
transformed.normalize(targetCS, 0, wrapAroundChanges.size(), wrapAroundChanges.iterator());
if (warning != null) {
recoverableException(Envelopes.class, warning);
}
return transformed;
}
use of org.opengis.referencing.cs.CoordinateSystem in project sis by apache.
the class LocationFormat method format.
/**
* Writes a textual representation of the given location in the given stream or buffer.
*
* <div class="warning"><b>Upcoming API change — generalization</b><br>
* in a future SIS version, the type of {@code location} parameter may be generalized
* to the {@code org.opengis.referencing.gazetteer.Location} interface.
* This change is pending GeoAPI revision.</div>
*
* @param location the location to format.
* @param toAppendTo where to format the location.
* @throws IOException if an error occurred while writing to the given appendable.
*/
@Override
@SuppressWarnings({ "fallthrough", "null" })
public void format(final AbstractLocation location, final Appendable toAppendTo) throws IOException {
ArgumentChecks.ensureNonNull("location", location);
final Locale locale = getLocale(Locale.Category.DISPLAY);
final Vocabulary vocabulary = Vocabulary.getResources(locale);
final TableAppender table = new TableAppender(toAppendTo, "│ ", columnSeparator, " │");
table.setMultiLinesCells(true);
/*
* Location type.
*/
table.appendHorizontalSeparator();
final AbstractLocationType type = location.type();
if (type != null) {
append(table, vocabulary, Vocabulary.Keys.LocationType, toString(type.getName(), locale));
}
/*
* Geographic identifier and alternative identifiers, if any.
*/
append(table, vocabulary, Vocabulary.Keys.GeographicIdentifier, toString(location.getGeographicIdentifier(), locale));
final Collection<? extends InternationalString> alt = location.getAlternativeGeographicIdentifiers();
if (alt != null && !alt.isEmpty()) {
boolean isFirst = true;
vocabulary.appendLabel(Vocabulary.Keys.AlternativeIdentifiers, table);
nextColumn(table);
for (final InternationalString id : alt) {
if (!isFirst) {
isFirst = false;
table.append(lineSeparator);
}
table.append(id);
}
table.nextLine();
}
/*
* Extents (temporal and geographic). If an envelope exists and the CRS is not geographic,
* then the envelope bounds will be appended on the same lines than the geographic bounds.
* But before writing the bounding box and/or the envelope, check if they are redundant.
* We may also need to change axis order (but not unit) of the envelope in order to match
* the axis order of the geographic bounding box.
*/
final Extent extent = new DefaultExtent(null, location.getGeographicExtent(), null, location.getTemporalExtent());
final Range<Date> time = Extents.getTimeRange(extent);
if (time != null) {
append(table, vocabulary, Vocabulary.Keys.StartDate, toString(time.getMinValue()));
append(table, vocabulary, Vocabulary.Keys.EndDate, toString(time.getMaxValue()));
}
GeographicBoundingBox bbox = Extents.getGeographicBoundingBox(extent);
Envelope envelope = location.getEnvelope();
DirectPosition position = position(location.getPosition());
// Position in geographic CRS.
DirectPosition geopos = null;
// Envelope Coordinate Reference System.
CoordinateReferenceSystem crs = null;
// CRS in conventional (x,y) axis order.
CoordinateReferenceSystem normCRS = null;
// If failed to transform envelope.
Exception warning = null;
try {
if (envelope != null) {
normCRS = normalize(crs = envelope.getCoordinateReferenceSystem());
if (normCRS != crs) {
// Should only change order and sign.
envelope = Envelopes.transform(envelope, normCRS);
}
}
if (position != null) {
/*
* If only one of the envelope or the position objects specify a CRS, assume that the other object
* use the same CRS. If both the envelope and the position objects specify a CRS, the envelope CRS
* will have precedence and the "representative position" will be projected to that CRS.
*/
final CoordinateReferenceSystem posCRS = position.getCoordinateReferenceSystem();
if (normCRS == null) {
normCRS = normalize(crs = posCRS);
if (normCRS != crs) {
// Should only change order and sign.
envelope = Envelopes.transform(envelope, normCRS);
}
}
if (bbox != null) {
// Compute geographic position only if there is a geographic bounding box.
GeographicCRS geogCRS = ReferencingUtilities.toNormalizedGeographicCRS(posCRS);
if (geogCRS != null) {
geopos = transform(position, posCRS, geogCRS);
}
}
position = transform(position, posCRS, normCRS);
}
} catch (FactoryException | TransformException e) {
envelope = null;
position = null;
warning = e;
}
/*
* At this point we got the final geographic bounding box and/or envelope to write.
* Since we will write the projected and geographic coordinates side-by-side in the same cells,
* we need to format them in advance so we can compute their width for internal right-alignment.
* We do the alignment ourselves instead than using TableAppender.setCellAlignment(ALIGN_RIGHT)
* because we do not want (projected geographic) tuple to appear far on the right side if other
* cells have long texts.
*/
if (bbox != null || envelope != null) {
final CoordinateSystem cs = (crs != null) ? crs.getCoordinateSystem() : null;
String[] geographic = null;
String[] projected = null;
String[] unitSymbol = null;
AngleFormat geogFormat = null;
NumberFormat projFormat = null;
UnitFormat unitFormat = null;
int maxGeogLength = 0;
int maxProjLength = 0;
int maxUnitLength = 0;
boolean showProj = false;
if (bbox != null || geopos != null) {
geogFormat = (AngleFormat) getFormat(Angle.class);
geographic = new String[BOUND_KEY.length];
Arrays.fill(geographic, "");
}
if (envelope != null || position != null) {
projFormat = (NumberFormat) getFormat(Number.class);
unitFormat = (UnitFormat) getFormat(Unit.class);
projected = new String[BOUND_KEY.length];
unitSymbol = new String[BOUND_KEY.length];
Arrays.fill(projected, "");
Arrays.fill(unitSymbol, "");
}
for (int i = 0; i < BOUND_KEY.length; i++) {
RoundingMode rounding = RoundingMode.FLOOR;
double g = Double.NaN;
double p = Double.NaN;
int dimension = 0;
switch(i) {
case 0:
if (bbox != null)
g = bbox.getWestBoundLongitude();
if (envelope != null)
p = envelope.getMinimum(0);
break;
case 2:
if (bbox != null)
g = bbox.getEastBoundLongitude();
if (envelope != null)
p = envelope.getMaximum(0);
rounding = RoundingMode.CEILING;
break;
case 3:
if (bbox != null)
g = bbox.getSouthBoundLatitude();
if (envelope != null)
p = envelope.getMinimum(1);
dimension = 1;
break;
case 5:
if (bbox != null)
g = bbox.getNorthBoundLatitude();
if (envelope != null)
p = envelope.getMaximum(1);
rounding = RoundingMode.CEILING;
dimension = 1;
break;
// Fall through
case 4:
dimension = 1;
case 1:
if (geopos != null)
g = geopos.getOrdinate(dimension);
if (position != null)
p = position.getOrdinate(dimension);
rounding = RoundingMode.HALF_EVEN;
break;
}
if (!Double.isNaN(p)) {
showProj |= (g != p);
if (cs != null) {
final Unit<?> unit = cs.getAxis(dimension).getUnit();
if (unit != null) {
final int length = (unitSymbol[i] = unitFormat.format(unit)).length();
if (length > maxUnitLength) {
maxUnitLength = length;
}
}
}
try {
projFormat.setRoundingMode(rounding);
} catch (UnsupportedOperationException e) {
// Ignore.
}
final int length = (projected[i] = projFormat.format(p)).length();
if (length > maxProjLength) {
maxProjLength = length;
}
}
if (!Double.isNaN(g)) {
geogFormat.setRoundingMode(rounding);
final Angle angle = (dimension == 0) ? new Longitude(g) : new Latitude(g);
final int length = (geographic[i] = geogFormat.format(angle)).length();
if (length > maxGeogLength) {
maxGeogLength = length;
}
}
}
if (!showProj) {
// All projected coordinates are identical to geographic ones.
projected = null;
unitSymbol = null;
maxProjLength = 0;
maxUnitLength = 0;
} else if (maxProjLength != 0) {
if (maxUnitLength != 0) {
maxUnitLength++;
}
// Arbitrary space between projected and geographic coordinates.
maxGeogLength += 4;
}
/*
* At this point all coordinates have been formatted in advance.
*/
final String separator = (projected != null && geographic != null) ? " —" : "";
for (int i = 0; i < BOUND_KEY.length; i++) {
final String p = (projected != null) ? projected[i] : "";
final String u = (unitSymbol != null) ? unitSymbol[i] : "";
final String g = (geographic != null) ? geographic[i] : "";
if (!p.isEmpty() || !g.isEmpty()) {
vocabulary.appendLabel(BOUND_KEY[i], table);
nextColumn(table);
table.append(CharSequences.spaces(maxProjLength - p.length())).append(p);
table.append(CharSequences.spaces(maxUnitLength - u.length())).append(u).append(separator);
table.append(CharSequences.spaces(maxGeogLength - g.length())).append(g);
table.nextLine();
}
}
}
if (crs != null) {
append(table, vocabulary, Vocabulary.Keys.CoordinateRefSys, IdentifiedObjects.getName(crs, null));
}
/*
* Organization responsible for defining the characteristics of the location instance.
*/
final AbstractParty administrator = location.getAdministrator();
if (administrator != null) {
append(table, vocabulary, Vocabulary.Keys.Administrator, toString(administrator.getName(), locale));
}
table.appendHorizontalSeparator();
table.flush();
if (warning != null) {
vocabulary.appendLabel(Vocabulary.Keys.Warnings, toAppendTo);
toAppendTo.append(warning.toString()).append(lineSeparator);
}
}
use of org.opengis.referencing.cs.CoordinateSystem in project sis by apache.
the class CRS method getVerticalComponent.
/**
* Returns the first vertical coordinate reference system found in the given CRS, or {@code null} if there is none.
* If the given CRS is already an instance of {@code VerticalCRS}, then this method returns it as-is.
* Otherwise if the given CRS is compound, then this method searches for the first vertical component
* in the order of the {@linkplain #getSingleComponents(CoordinateReferenceSystem) single components list}.
*
* <div class="section">Height in a three-dimensional geographic CRS</div>
* In ISO 19111 model, ellipsoidal heights are indissociable from geographic CRS because such heights
* without their (<var>latitude</var>, <var>longitude</var>) locations make little sense. Consequently
* a standard-conformant library should return {@code null} when asked for the {@code VerticalCRS}
* component of a geographic CRS. This is what {@code getVerticalComponent(…)} does when the
* {@code allowCreateEllipsoidal} argument is {@code false}.
*
* <p>However in some exceptional cases, handling ellipsoidal heights like any other kind of heights
* may simplify the task. For example when computing <em>difference</em> between heights above the
* same datum, the impact of ignoring locations may be smaller (but not necessarily canceled).
* Orphan {@code VerticalCRS} may also be useful for information purpose like labeling a plot axis.
* If the caller feels confident that ellipsoidal heights are safe for his task, he can set the
* {@code allowCreateEllipsoidal} argument to {@code true}. In such case, this {@code getVerticalComponent(…)}
* method will create a temporary {@code VerticalCRS} from the first three-dimensional {@code GeographicCRS}
* <em>in last resort</em>, only if it can not find an existing {@code VerticalCRS} instance.
* <strong>Note that this is not a valid CRS according ISO 19111</strong> — use with care.</p>
*
* @param crs the coordinate reference system, or {@code null}.
* @param allowCreateEllipsoidal {@code true} for allowing the creation of orphan CRS for ellipsoidal heights.
* The recommended value is {@code false}.
* @return the first vertical CRS, or {@code null} if none.
*
* @see #compound(CoordinateReferenceSystem...)
*
* @category information
*/
public static VerticalCRS getVerticalComponent(final CoordinateReferenceSystem crs, final boolean allowCreateEllipsoidal) {
if (crs instanceof VerticalCRS) {
return (VerticalCRS) crs;
}
if (crs instanceof CompoundCRS) {
final CompoundCRS cp = (CompoundCRS) crs;
boolean a = false;
do {
// Executed at most twice.
for (final CoordinateReferenceSystem c : cp.getComponents()) {
final VerticalCRS candidate = getVerticalComponent(c, a);
if (candidate != null) {
return candidate;
}
}
} while ((a = !a) == allowCreateEllipsoidal);
}
if (allowCreateEllipsoidal && horizontalCode(crs) == 3) {
final CoordinateSystem cs = crs.getCoordinateSystem();
final int i = AxisDirections.indexOfColinear(cs, AxisDirection.UP);
if (i >= 0) {
final CoordinateSystemAxis axis = cs.getAxis(i);
VerticalCRS c = CommonCRS.Vertical.ELLIPSOIDAL.crs();
if (!c.getCoordinateSystem().getAxis(0).equals(axis)) {
final Map<String, ?> properties = IdentifiedObjects.getProperties(c);
c = new DefaultVerticalCRS(properties, c.getDatum(), new DefaultVerticalCS(properties, axis));
}
return c;
}
}
return null;
}
use of org.opengis.referencing.cs.CoordinateSystem in project sis by apache.
the class CRS method horizontalCode.
/**
* If the given CRS would quality as horizontal except for its number of dimensions, returns that number.
* Otherwise returns 0. The number of dimensions can only be 2 or 3.
*/
private static int horizontalCode(final CoordinateReferenceSystem crs) {
/*
* In order to determine if the CRS is geographic, checking the CoordinateSystem type is more reliable
* then checking if the CRS implements the GeographicCRS interface. This is because the GeographicCRS
* type did not existed in ISO 19111:2007, so a CRS could be standard-compliant without implementing
* the GeographicCRS interface.
*/
boolean isEngineering = false;
final boolean isGeodetic = (crs instanceof GeodeticCRS);
if (isGeodetic || crs instanceof ProjectedCRS || (isEngineering = (crs instanceof EngineeringCRS))) {
final CoordinateSystem cs = crs.getCoordinateSystem();
final int dim = cs.getDimension();
if ((dim & ~1) == 2 && (!isGeodetic || (cs instanceof EllipsoidalCS))) {
if (isEngineering) {
int n = 0;
for (int i = 0; i < dim; i++) {
if (AxisDirections.isCompass(cs.getAxis(i).getDirection()))
n++;
}
// If we don't have exactly 2 east, north, etc. directions, consider as non-horizontal.
if (n != 2)
return 0;
}
return dim;
}
}
return 0;
}
use of org.opengis.referencing.cs.CoordinateSystem in project sis by apache.
the class AbstractCRS method formatTo.
/**
* Formats the inner part of the <cite>Well Known Text</cite> (WKT) representation of this CRS.
* The default implementation writes the following elements in WKT 2 format:
*
* <ul>
* <li>The object {@linkplain #getName() name}.</li>
* <li>The datum, if any.</li>
* <li>All {@linkplain #getCoordinateSystem() coordinate system}'s axis.</li>
* <li>The unit if all axes use the same unit, or nothing otherwise.</li>
* </ul>
*
* The WKT 1 format is similar to the WKT 2 one with two differences:
* <ul>
* <li>Units are formatted before the axes instead than after the axes.</li>
* <li>If no unit can be formatted because not all axes use the same unit, then the WKT is
* {@linkplain Formatter#setInvalidWKT(IdentifiedObject, Exception) flagged as invalid}.</li>
* </ul>
*
* @return {@inheritDoc}
*
* @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">WKT 2 specification</a>
* @see <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">Legacy WKT 1</a>
*/
@Override
protected String formatTo(final Formatter formatter) {
final String keyword = super.formatTo(formatter);
formatter.newLine();
formatter.append(toFormattable(getDatum()));
formatter.newLine();
final Convention convention = formatter.getConvention();
final boolean isWKT1 = convention.majorVersion() == 1;
if (isWKT1 || convention == Convention.INTERNAL || !isBaseCRS(formatter)) {
final CoordinateSystem cs = getCoordinateSystem();
formatCS(formatter, cs, ReferencingUtilities.getUnit(cs), isWKT1);
}
return keyword;
}
Aggregations